diff --git a/.gitignore b/.gitignore index 5dd6dcdc..f4e642d0 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ test/cmake-build-cache lib/ .sublime-project.sublime-workspace *.sublime-workspace - ekg-ui-library.sublime-workspace ekg-sandbox ekg-docs diff --git a/CMakeLists.txt b/CMakeLists.txt index e070e722..e2a37277 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ find_package(Freetype REQUIRED) file(GLOB_RECURSE HEADER_FILES "include/*.hpp") file(GLOB_RECURSE SOURCE_FILES "src/*.cpp") -exclude_files_by_regex(SOURCE_FILES "/ui_|/util/") +# exclude_files_by_regex(SOURCE_FILES /* regex */) add_library( ekg STATIC diff --git a/LICENSE.md b/LICENSE.md index c7a5568f..c7202e72 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com +Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2c63030c..6657a791 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,14 @@ # 🐄 EKG 🐈 -EKG is a descriptor-based low-latency UI-toolkit for desktop-apps, mobile-apps, and high-performance apps. Offers multi-support for platforms and graphics API(s). +EKG is a descriptor-based low-latency modular UI-toolkit for desktop-apps, mobile-apps, and high-performance apps. -**Note: EKG is not stable yet.** - -Contributing for EKG is nicely welcome, just take a look on [Vokegpu standard](https://github.com/vokegpu/standard), thanks. - -### GUIs with EKG - -Creating a simple frame: - -```C++ -ekg::make( - ekg::frame_t { - .tag = "first frame", - .rect = {20.0f, 20.0f, 200.0f, 200.0f}, - .drag_dock = ekg::dock::top, - .resize_dock = ekg::dock::left | ekg::dock::bottom | ekg::dock::right - } -); -``` - -![image](https://github.com/user-attachments/assets/1629c5ae-fa50-47f4-8900-65d11479df64) - -Adding buttons: - -```C++ -ekg::button_t button {}; - -button.tag = "bt-1"; -button.text = "moo moo"; -button.text_dock = ekg::dock::left; -button.dock = ekg::dock::left; -ekg::make(button); - -button.tag = "bt-2"; -button.text = "owlf olwf"; -button.text_dock = ekg::dock::center; -button.dock = ekg::dock::next | ekg::dock::fill; -ekg::make(button); - -button.tag = "bt-3"; -button.text = "oi amo gatinhos"; -ekg::make(button); -``` +Platforms: SDL2, GLFW. +GPU APIs: OpenGL3+, OpenGLES3. -![image](https://github.com/user-attachments/assets/5f510ef6-e51b-4ca0-a5fc-1d36582bb055) - -You can dynamic change the value, text or any display output via safety-reference: - -```C++ -button.tag = "bt-3"; -button.text = "oi amo gatinhos"; -ekg::button_t &bla = ekg::make(button); -``` - -And then dynamic trigger/set value from fields. - -```C++ -bla.value = true; // set button trigger value forced -bla.text = "clicked here times: " + std::to_string(button_click_times); // set the text dynamically -``` - -Others widgets are being re-implemented in this new memory-model, also new future widgets. - -May you want to know about this new memory-model or about EKG more, go here. -[EKG Docs](https://github.com/vokegpu/ekg-docs) - -### Initializing EKG - -EKG is a modular library, which does not necessary have one unique base, for example the platform used, supporting SDL2, GLFW and soon others. - -```cpp -ekg::runtime_property_t ekg_runtime_property { - .font_path = "default.ttf", - .font_path_emoji = "default-emoji.ttf", - .p_gpu_api = new ekg::opengl(), - .p_os_platform = new ekg::sdl(p_win_sdl) -}; +**Note: EKG is not stable yet.** -ekg::runtime ekg_runtime {}; -ekg::init(&ekg_runtime, &ekg_runtime_property); -``` +## GUIs with EKG -Supported GPU API(s): OpenGL3+, OpenGLES3. +## Contributing -Supported platform(s): SDL2, GLFW. +Contributing for EKG is nicely welcome, please confer [EKG docs and read the model architecture](https://github.com/vokegpu/ekg-docs?tab=readme-ov-file#ekg-technical-details) for prevent ~useless~ pull requests. diff --git a/ekg-ui-library.sublime-project b/ekg-ui-library.sublime-project index b4cb355c..54d67b93 100644 --- a/ekg-ui-library.sublime-project +++ b/ekg-ui-library.sublime-project @@ -1,62 +1,62 @@ { - "folders": - [ - { - "path": ".", - } - ], - "settings": - { - "tab_size": 2, - "translate_tabs_to_spaces": true, - "LSP": - { - "binary": "clang++", - "clangd": - { - "initializationOptions": - { - "clangd.compile-commands-dir": "$folder/cmake-build", - }, - }, - }, - }, - "build_systems": - [ - { - "name": "Build Linux GNU", - "shell_cmd": - "cd $folder && EKG_BUILD_MODE=build-only EKG_BUILD_COMPILER=/usr/bin/g++ sh ./linux.sh" - }, - { - "name": "Build-&-Test Linux GNU", - "shell_cmd": - "cd $folder && EKG_BUILD_MODE=build-and-test EKG_BUILD_COMPILER=/usr/bin/g++ sh ./linux.sh" - }, - { - "name": "Build Linux Clang", - "shell_cmd": - "cd $folder && EKG_BUILD_MODE=build-only EKG_BUILD_COMPILER=/usr/bin/g++ sh ./linux.sh" - }, - { - "name": "Build-&-Test Linux Clang", - "shell_cmd": - "cd $folder && EKG_BUILD_MODE=build-and-test EKG_BUILD_COMPILER=/usr/bin/clang++ sh ./linux.sh" - }, - { - "name": "Build Linux WASM/emscripten", - "shell_cmd": - "cd $folder && EKG_BUILD_MODE=build-only EKG_BUILD_COMPILER=emscripten sh ./linux.sh" - }, - { - "name": "Build Windows GNU", - "shell_cmd": - "cd $folder && cmake -S . -B ./cmake-build/ -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 && cmake --build ./cmake-build/" - }, - { - "name": "Build-&-Test Windows GNU", - "shell_cmd": - "cd $folder && cmake -S . -B ./cmake-build/ -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 && cmake --build ./cmake-build/ && cd ./ekg-sandbox && cmake -S . -B ./cmake-build -G Ninja -DEKG_DEVELOPER_MODE=1 && cmake --build ./cmake-build && cd ./bin && ./ekg-showcase", - }, - ], + "folders": + [ + { + "path": ".", + } + ], + "settings": + { + "tab_size": 2, + "translate_tabs_to_spaces": true, + "LSP": + { + "clangd": + { + "system_binary": "clangd", + "initializationOptions": + { + "clangd.compile-commands-dir": "$folder/cmake-build" + } + }, + }, + }, + "build_systems": + [ + { + "name": "Build Linux GNU", + "shell_cmd": + "cd $folder && EKG_BUILD_MODE=build-only EKG_BUILD_COMPILER=/usr/bin/g++ sh ./linux.sh" + }, + { + "name": "Build-&-Test Linux GNU", + "shell_cmd": + "cd $folder && EKG_BUILD_MODE=build-and-test EKG_BUILD_COMPILER=/usr/bin/g++ sh ./linux.sh" + }, + { + "name": "Build Linux Clang", + "shell_cmd": + "cd $folder && EKG_BUILD_MODE=build-only EKG_BUILD_COMPILER=/usr/bin/g++ sh ./linux.sh" + }, + { + "name": "Build-&-Test Linux Clang", + "shell_cmd": + "cd $folder && EKG_BUILD_MODE=build-and-test EKG_BUILD_COMPILER=/usr/bin/clang++ sh ./linux.sh" + }, + { + "name": "Build Linux WASM/emscripten", + "shell_cmd": + "cd $folder && EKG_BUILD_MODE=build-only EKG_BUILD_COMPILER=emscripten sh ./linux.sh" + }, + { + "name": "Build Windows GNU", + "shell_cmd": + "cd $folder && cmake -S . -B ./cmake-build/ -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 && cmake --build ./cmake-build/" + }, + { + "name": "Build-&-Test Windows GNU", + "shell_cmd": + "cd $folder && cmake -S . -B ./cmake-build/ -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=1 && cmake --build ./cmake-build/ && cd ./ekg-sandbox && cmake -S . -B ./cmake-build -G Ninja -DEKG_DEVELOPER_MODE=1 && cmake --build ./cmake-build && cd ./bin && ./ekg-showcase", + }, + ], } diff --git a/include/ekg/core/context.hpp b/include/ekg/core/context.hpp index 8486691e..76eb00cf 100644 --- a/include/ekg/core/context.hpp +++ b/include/ekg/core/context.hpp @@ -1,48 +1,78 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef EKG_CORE_CONTEXT_HPP #define EKG_CORE_CONTEXT_HPP -#include "ekg/io/typography.hpp" #include "ekg/math/geometry.hpp" -#include "ekg/ui/types.hpp" +#include "ekg/io/descriptor.hpp" namespace ekg { - extern FT_Library freetype_library; - - extern struct viewport_t { - float x {}; - float y {}; - float w {}; - float h {}; - float dt {}; - float minimum_possible_size {10.0f}; - bool redraw {}; - } viewport; + extern struct metrics_t { + public: + size_t gpu_data_count {}; + } metrics; extern struct dpi_t { - ekg::rect_t scale {0.0f, 0.0f, 1920.0f, 1080.0f}; + public: + ekg::rect_t viewport {}; + ekg::rect_t scale {0.0f, 0.0f, 1080.0f, 1920.0f}; + bool auto_scale {}; float scale_interval {25.0f}; - - float font_scale {18.0f}; float factor_scale {}; - + float font_scale {}; + float min_sizes {10.0f}; ekg::vec2_t font_offset {4, 6}; - bool auto_scale {true}; } dpi; - extern struct current_t { - ekg::id_t pressed {}; - ekg::type pressed_type {}; - ekg::id_t released {}; - ekg::type released_type {}; - ekg::id_t last {}; - ekg::id_t unique_id {}; - ekg::type type {}; - } current; - - extern struct tweaks_t { - int64_t task_latency {500}; - int64_t debug {}; - } tweaks; + extern struct gui_t { + public: + struct bind_t { + public: + ekg::at_t stack_at {ekg::at_t::not_found}; + ekg::at_t swap_at {ekg::at_t::not_found}; + ekg::at_t parent_at {ekg::at_t::not_found}; + }; + + // @TODO: add last for press, release and hover + + struct ui_t { + public: + ekg::at_t abs_widget_at {ekg::at_t::not_found}; + ekg::type hovered_type {}; + ekg::at_t hovered_at {ekg::at_t::not_found}; + ekg::at_t last_hovered_at {ekg::at_t::not_found}; + ekg::type pressed_type {}; + ekg::at_t pressed_at {ekg::at_t::not_found}; + ekg::type released_type {}; + ekg::at_t released_at {ekg::at_t::not_found}; + float dt {}; + bool redraw {}; + }; + public: + ekg::gui_t::bind_t bind {}; + ekg::gui_t::ui_t ui {}; + } gui; constexpr uint32_t minimum_small_font_height {4}; constexpr uint32_t minimum_font_height {8}; diff --git a/include/ekg/core/pools.hpp b/include/ekg/core/pools.hpp new file mode 100644 index 00000000..e47c24e1 --- /dev/null +++ b/include/ekg/core/pools.hpp @@ -0,0 +1,224 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_CORE_POOLS_HPP +#define EKG_CORE_POOLS_HPP + +#include "ekg/io/memory.hpp" +#include "ekg/handler/callback.hpp" +#include "ekg/gpu/sampler.hpp" +#include "ekg/ui/property.hpp" +#include "ekg/ui/stack.hpp" +#include "context.hpp" +#include "ekg/handler/theme.hpp" + +#include "ekg/ui/button/button.hpp" +#include "ekg/ui/button/widget.hpp" + +#include "ekg/ui/frame/frame.hpp" +#include "ekg/ui/frame/widget.hpp" + +namespace ekg::core { + void registry(ekg::property_t &property); +} + +#define ekg_abstract_todo(ekg_abstract_todo_at_type, ekg_abstract_todo_at, ekg_abstract_todo_todo) \ + switch (ekg_abstract_todo_at_type) { \ + case ekg::type::button: { \ + ekg::button_t &descriptor { \ + ekg::query(ekg_abstract_todo_at) \ + }; \ + if (descriptor == ekg::button_t::not_found) { \ + break; \ + } \ + ekg_abstract_todo_todo \ + break; \ + } \ + case ekg::type::frame: { \ + ekg::frame_t &descriptor { \ + ekg::query(ekg_abstract_todo_at) \ + }; \ + if (descriptor == ekg::frame_t::not_found) { \ + break; \ + } \ + ekg_abstract_todo_todo \ + break; \ + } \ + } + +#define ekg_registry_widget(widget_descriptor_t, register_widget_pool, register_property_pool, is_container, register_settings) \ + widget_descriptor_t &widget { \ + register_widget_pool.push_back( \ + ekg::io::any_static_cast(&descriptor) \ + ) \ + }; \ + ekg::property_t &property { \ + register_property_pool.push_back({}) \ + }; \ +\ + widget.at.flags = widget_descriptor_t::type; \ + property.descriptor_at = widget.at; \ +\ + property.at.flags = widget_descriptor_t::type; \ + widget.property_at = property.at; \ +\ + ekg::theme_t &global_theme {ekg::theme()};\ +\ + register_settings; \ +\ + ekg::stack_t &stack {ekg::query(ekg::gui.bind.stack_at)}; \ + if (stack != ekg::stack_t::not_found) { \ + stack.widgets.push_back(widget.at); \ + } \ +\ + ekg::property_t &parent {ekg::query(ekg::gui.bind.parent_at)}; \ + if (parent != ekg::property_t::not_found && is_container) { \ + if (widget.dock != ekg::dock::none) { \ + parent.children.push_back(widget.at); \ + } else { \ + ekg::gui.bind.parent_at = widget.at; \ + } \ + } else if (parent != ekg::property_t::not_found) { \ + parent.children.push_back(widget.at); \ + } \ +\ + ekg::core::registry(property); \ + return ekg::io::any_static_cast(&widget); + +namespace ekg { + extern struct pools_t { + public: + ekg::pool stack {}; + ekg::pool callback {}; + ekg::pool sampler {}; + + ekg::pool button_property {}; + ekg::pool button {}; + + ekg::pool frame_property {}; + ekg::pool frame {}; + } pools; + + template + t &query( + ekg::at_t &at + ) { + switch (t::type) { + case ekg::type::stack: + return ekg::io::any_static_cast( + &ekg::pools.stack.query(at) + ); + case ekg::type::callback: + return ekg::io::any_static_cast( + &ekg::pools.callback.query(at) + ); + case ekg::type::sampler: + return ekg::io::any_static_cast( + &ekg::pools.sampler.query(at) + ); + case ekg::type::property: + switch (at.flags) { + case ekg::type::button: + return ekg::io::any_static_cast( + &ekg::pools.button_property.query(at) + ); + case ekg::type::frame: + return ekg::io::any_static_cast( + &ekg::pools.frame_property.query(at) + ); + } + case ekg::type::button: + return ekg::io::any_static_cast( + &ekg::pools.button.query(at) + ); + case ekg::type::frame: + return ekg::io::any_static_cast( + &ekg::pools.frame.query(at) + ); + } + + return t::not_found; + } + + template + t &make( + t descriptor + ) { + switch (t::type) { + case ekg::type::stack: { + ekg::stack_t &stack { + ekg::pools.stack.push_back( + ekg::io::any_static_cast(&descriptor) + ) + }; + + ekg::gui.bind.stack_at = stack.at; + + return ekg::io::any_static_cast( + &stack + ); + } + case ekg::type::callback: + return ekg::io::any_static_cast( + &ekg::pools.callback.push_back( + ekg::io::any_static_cast(&descriptor) + ) + ); + case ekg::type::sampler: + return ekg::io::any_static_cast( + &ekg::pools.sampler.push_back( + ekg::io::any_static_cast(&descriptor) + ) + ); + case ekg::type::button: { + ekg_registry_widget( + ekg::button_t, + ekg::pools.button, + ekg::pools.button_property, + false, + { + property.widget.is_childnizate = false; + property.widget.is_children_docknizable = false; + widget.color_scheme = global_theme.button_color_scheme; + } + ); + } + case ekg::type::frame: { + ekg_registry_widget( + ekg::frame_t, + ekg::pools.frame, + ekg::pools.frame_property, + true, + { + property.widget.is_childnizate = true; + property.widget.is_children_docknizable = true; + widget.color_scheme = global_theme.frame_color_scheme; + } + ); + }} + + return t::not_found; + } +} + +#endif diff --git a/include/ekg/core/runtime.hpp b/include/ekg/core/runtime.hpp index 7b0bd979..ec52bcfd 100644 --- a/include/ekg/core/runtime.hpp +++ b/include/ekg/core/runtime.hpp @@ -21,86 +21,61 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #ifndef EKG_CORE_RUNTIME_HPP #define EKG_CORE_RUNTIME_HPP -#include "ekg/ui/abstract.hpp" -#include "ekg/service/handler.hpp" -#include "ekg/service/theme.hpp" -#include "ekg/service/input.hpp" -#include "ekg/gpu/allocator.hpp" +#include "ekg/platform/base.hpp" +#include "ekg/gpu/api.hpp" +#include "ekg/draw/typography/font.hpp" +#include "ekg/draw/allocator.hpp" +#include "ekg/handler/theme/handler.hpp" +#include "ekg/handler/callback/handler.hpp" +#include "ekg/handler/callback.hpp" +#include "ekg/handler/input/handler.hpp" #include "ekg/layout/docknize.hpp" -#include "ekg/draw/font_renderer.hpp" -#include "ekg/io/algorithm.hpp" - -#include namespace ekg { - struct runtime_property_t { + struct runtime_properties_info_t { public: - std::string font_path {}; - std::string font_path_emoji {}; + std::string_view default_font_path_text {}; + std::string_view default_font_path_emoji {}; + ekg::platform::base *p_platform_base {}; ekg::gpu::api *p_gpu_api {}; - ekg::os::platform *p_os_platform {}; + ekg::ft_library ft_library {}; }; - /** - * The main core of EKG. - **/ - extern class runtime { - private: - /** - * TODO: answer questions about this part specifically until second `:` statment. - **/ - std::vector> loaded_widget_list {}; - ekg::id_t global_id {}; - - std::vector context_widget_list {}; - std::vector high_frequency_widget_list {}; - std::vector reload_widget_list {}; - std::vector layout_docknize_list {}; - - ekg::ui::abstract *p_abs_activity_widget {}; - ekg::io::target_collector_t swap_target_collector {}; - - ekg::properties_t *p_current_parent_properties {}; + extern struct runtime_t { public: - ekg::service::handler service_handler {}; - ekg::service::theme service_theme {}; - ekg::service::input service_input {}; - - ekg::os::platform *p_os_platform {}; - - ekg::gpu::allocator gpu_allocator {}; + ekg::platform::base *p_platform_base {}; ekg::gpu::api *p_gpu_api {}; - - ekg::draw::font_renderer draw_fr_small {}; - ekg::draw::font_renderer draw_fr_normal {}; - ekg::draw::font_renderer draw_fr_big {}; - - ekg::layout::mask layout_mask {}; + ekg::ft_library ft_library {}; public: - ekg::ui::abstract *emplace_back_new_widget_safety( - ekg::ui::abstract *p_widget - ); - - ekg::id_t generate_unique_id(); - - void dispatch_widget_op( - ekg::ui::abstract *p_widget, - ekg::io::operation op - ); - - void set_current_parent_properties(ekg::properties_t *p_properties); - ekg::properties_t *get_current_parent_properties(); + ekg::handler::input handler_input {}; + ekg::handler::callback handler_callback {}; + ekg::handler::theme handler_theme {}; public: - void init(); - void quit(); - void update(); - void render(); - void poll_events(); + ekg::draw::allocator draw_allocator {}; + ekg::draw::font draw_font_small {}; + ekg::draw::font draw_font_medium {}; + ekg::draw::font draw_font_big {}; + public: + std::vector collector {}; + std::vector registry {}; + std::vector stack {}; + std::vector top_level_stack {}; + std::vector reload {}; + std::vector docknize {}; + std::vector high_frequency {}; } *p_core; } +namespace ekg::core { + void swap_collector(bool &was_found, ekg::at_t &property_at); + void swap(ekg::info_t &info); + void reload(ekg::info_t &info); + void docknize(ekg::info_t &info); + void scalenize(ekg::info_t &info); + void poll_event(); +} + #endif diff --git a/include/ekg/draw/allocator.hpp b/include/ekg/draw/allocator.hpp new file mode 100644 index 00000000..40643ad1 --- /dev/null +++ b/include/ekg/draw/allocator.hpp @@ -0,0 +1,93 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_DRAW_ALLOCATOR_HPP +#define EKG_DRAW_ALLOCATOR_HPP + +#include + +#include "ekg/math/geometry.hpp" +#include "ekg/gpu/data.hpp" +#include "ekg/gpu/sampler.hpp" + +/** + * This macro prevent from dispatching any GPU-data under a render section + * if the content is not visible to the parent rect_scissor. + **/ +#define ekg_assert_scissor(rect_scissor, rect_child, rect_parent, is_parented) \ + if (!ekg::p_core->draw_allocator.sync_scissor(rect_scissor, rect_child, rect_parent, is_parented)) return; + +namespace ekg::draw { + class allocator { + public: + static bool enable_high_priority; + static bool is_simple_shape; + protected: + size_t data_instance {}; + ekg::vec2_t stride_instance {}; + size_t simple_shape_instance {}; + size_t geometry_instance {}; + size_t high_priority_data_instance {}; + ekg::rect_t scissor_instance {}; + + std::vector gpu_data_buffer {}; + std::vector high_priority_gpu_data_buffer {}; + std::vector geometry_buffer {}; + size_t last_geometry_buffer_size {}; + + bool was_hash_changed {}; + int32_t previous_hash {}; + public: + void init(); + void quit(); + + void invoke(); + void revoke(); + void to_gpu(); + + void bind_texture(ekg::sampler_t &sampler); + ekg::gpu::data_t &bind_current_data(); + size_t get_current_data_id(); + ekg::gpu::data_t &get_data_by_index(size_t index); + void clear_current_data(); + void dispatch(); + + bool sync_scissor( + ekg::rect_t &rect_scissor, + ekg::rect_t &rect_child, + ekg::rect_t &rect_parent_scissor, + bool is_parented + ); + + void unsafe_set_scissor_rect( + float x, float y, float w, float h + ); + + void push_back_geometry( + float x, float y, + float u, float v + ); + }; +} + +#endif diff --git a/include/ekg/draw/font_renderer.hpp b/include/ekg/draw/font_renderer.hpp deleted file mode 100644 index f7087df6..00000000 --- a/include/ekg/draw/font_renderer.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_DRAW_FONT_RENDERER_HPP -#define EKG_DRAW_FONT_RENDERER_HPP - -#include - -#include "ekg/gpu/allocator.hpp" -#include "ekg/math/geometry.hpp" -#include "ekg/gpu/api.hpp" - -#define FT_CONFIG_OPTION_USE_PNG - -namespace ekg::draw { - /** - * Normally an UV is normalized-clamped, so multiplying by 100 - * do the number be able to sum the factor and show some - * effect on screen. - * - * For example, a same number but UV was updated, no factor is changed - * then the way to fix it is make UV noticable. - **/ - constexpr int32_t generate_factor_hash( - float axis, - char32_t char32, - float x - ) { - return static_cast( - axis + char32 + x * 100 - ); - } - - class font_renderer { - public: - std::vector loaded_sampler_generate_list {}; - uint64_t last_sampler_generate_list_size {}; - - std::unordered_map mapped_glyph_char_data {}; - std::array faces {}; - - ekg::sampler_t atlas_texture_sampler {}; - ekg::rect_t atlas_rect {}; - float offset_text_height {}; - - uint32_t font_size {}; - float text_height {}; - float non_swizzlable_range {}; - FT_Bool ft_bool_kerning {}; - - bool font_size_changed {}; - bool was_initialized {}; - bool is_any_functional_font_face_loaded {}; - - ekg::gpu::allocator *p_allocator {}; - public: - /** - * Return the sampler atlas with all font(s) combined. - */ - ekg::sampler_t *get_atlas_texture_sampler(); - - /** - * Return the text width. - * Note: font-rendering is UTF-8-based. - */ - float get_text_width(std::string_view text); - - /** - * Return the text width and the lines `\n`. - * Note: font-rendering is UTF-8-based. - */ - float get_text_width(std::string_view text, int32_t &lines); - - /** - * Return the font face height. - */ - float get_text_height(); - - /** - * Set a new font face for emoji, check FreeType docs. - */ - void set_font_emoji(std::string_view font_face_emoji_path); - - /** - * Set a new font face, check FreeType docs. - */ - void set_font(std::string_view font_face_path); - - /** - * Set the font face height. - */ - void set_size(uint32_t font_face_size); - - /** - * Reload the font face with the new metrics and file path. - */ - void reload(); - - /** - * Bind an external GPU allocator, but is not recommend pass a nullptr value. - */ - void bind_allocator(ekg::gpu::allocator *p_allocator_bind); - - /** - * Generate GPU data to draw text on screen. - */ - void blit(std::string_view text, float x, float y, const ekg::vec4_t &color); - - /** - * Init the internal-system of font-rendering. - */ - void init(); - - /** - * Quit the internal-system and free FreeType features from memory. - */ - void quit(); - - /** - * flush new glyph(s) to re-generate texture atlas and map for the GPU-side. - **/ - void flush(); - }; - - /** - * Temp solution for fixed-font-renderer - **/ - ekg::draw::font_renderer &get_font_renderer(ekg::font font); -} - -#endif diff --git a/include/ekg/draw/shape.hpp b/include/ekg/draw/shape/shape.hpp similarity index 63% rename from include/ekg/draw/shape.hpp rename to include/ekg/draw/shape/shape.hpp index 34c1f159..2e5d53a0 100644 --- a/include/ekg/draw/shape.hpp +++ b/include/ekg/draw/shape/shape.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,58 +21,39 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #ifndef EKG_DRAW_SHAPE_HPP #define EKG_DRAW_SHAPE_HPP -#include -#include - #include "ekg/math/geometry.hpp" -#include "ekg/gpu/api.hpp" +#include "ekg/gpu/sampler.hpp" -namespace ekg { - enum draw_mode { - filled = 0, - circle = -1, +namespace ekg::draw { + enum mode : int8_t { + fill = 0, outline = 1 }; -} -namespace ekg::draw { void rect( const ekg::rect_t &rect, - const ekg::vec4_t &color, - int32_t draw_mode, - ekg::sampler_t *p_sampler = nullptr + const ekg::rgba_t &color, + ekg::pixel_thickness_t line_thicnkess, + ekg::sampler_t &sampler ); void rect( float x, float y, float w, float h, - const ekg::vec4_t &color, - int32_t draw_mode, - ekg::sampler_t *p_sampler = nullptr + const ekg::rgba_t &color, + ekg::pixel_thickness_t line_thicnkess, + ekg::sampler_t &sampler ); void scissor( - float x, - float y, - float w, - float h + const ekg::rect_t &rect ); - /** - * Enable high-priority: - * Instead of sending the currrent GPU data to the current index, - * the data is reserved to the end of rendering plus high priority. - **/ - void enable_high_priority(); - - /** - * Disable high-priority: - * Send the current GPU data to the current index. - **/ - void disable_high_priority(); + void scissor( + float x, float y, float w, float h + ); } #endif diff --git a/include/ekg/draw/typography/font.hpp b/include/ekg/draw/typography/font.hpp new file mode 100644 index 00000000..4d0a6cfb --- /dev/null +++ b/include/ekg/draw/typography/font.hpp @@ -0,0 +1,83 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_DRAW_TYPOGRAPHY_FONT_HPP +#define EKG_DRAW_TYPOGRAPHY_FONT_HPP + +#include +#include +#include +#include +#include + +#include "ekg/math/geometry.hpp" +#include "ekg/io/font.hpp" +#include "ekg/gpu/sampler.hpp" + +namespace ekg::draw { + class font { + public: + std::vector new_glyphs_to_atlas {}; + size_t last_sampler_generate_list_size {}; + + std::unordered_map mapped_glyph {}; + std::array faces {}; + + ekg::at_t atlas_texture_sampler_at {ekg::at_t::not_found}; + ekg::rect_t atlas_rect {}; + float offset_text_height {}; + + uint32_t font_size {}; + float text_height {}; + float non_swizzlable_range {}; + FT_Bool ft_bool_kerning {}; + + bool font_size_changed {}; + bool was_initialized {}; + bool is_any_functional_font_face_loaded {}; + public: + void init(); + void quit(); + void flush(); + + ekg::sampler_t &get_atlas_texture_sampler(); + float get_text_width(const std::string_view &text); + float get_text_width(const std::string_view &text, int32_t &lines); + float get_text_height(); + + void set_font_emoji(const std::string_view &font_face_emoji_path); + void set_font(const std::string_view &font_face_path); + void set_size(uint32_t font_face_size); + void reload(); + + void blit( + const std::string_view &text, + float x, float y, + const ekg::rgba_t &color + ); + }; + + ekg::draw::font &get_font_renderer(ekg::font font); +} + +#endif diff --git a/include/ekg/ekg.hpp b/include/ekg/ekg.hpp index a4eda4a1..7e9e18dd 100644 --- a/include/ekg/ekg.hpp +++ b/include/ekg/ekg.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,56 +21,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #ifndef EKG_HPP #define EKG_HPP -#pragma STDC FENV_ACCESS ON - +#include "ekg/io/memory.hpp" +#include "ekg/core/runtime.hpp" #include "ekg/core/context.hpp" -#include "ekg/io/safety.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/math/floating_point.hpp" +#include "ekg/math/geometry.hpp" +#include "ekg/io/log.hpp" namespace ekg { - constexpr std::string_view version_id {"2.0.0"}; - constexpr std::string_view version_state {"ALPHA"}; - - /** The setup and handling functions of ekg **/ - - /** - * The initialization of EKG, - * initialize `ekg::runtime_property_t` correct before calling this. - * - * EKG does not has a font-service to handle system fonts, so it is required - * to load a local (ttf, otf) font file. - * - * GPU API tells which GPU API the application is built in; For OpenGL API - * do not worry about the initialization setup process; Vulkan API requires - * more of work to setup, EKG only initialize the necessary descriptor sets, - * and PSO - you pre-initialize everything -. - * Check: - * - * The OS platform, under the system libraries (X11, Wayland, Win32) or - * window library (SDL, GLFW, etc). - * Check: - */ - void init( - ekg::runtime *p_ekg_runtime, - ekg::runtime_property_t *p_ekg_runtime_property + ekg::flags_t init( + ekg::runtime_properties_info_t &runtime_properties_info, + ekg::runtime_t *p_runtime ); - /** - * Quit from all services and main runtime core. - */ void quit(); - - /** - * Process events, tasks, services & widgets. - */ void update(); - - /** - * Draw all data from gpu allocator. - */ void render(); } diff --git a/include/ekg/gpu/allocator.hpp b/include/ekg/gpu/allocator.hpp deleted file mode 100644 index 410d27ce..00000000 --- a/include/ekg/gpu/allocator.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_GPU_ALLOCATOR_HPP -#define EKG_GPU_ALLOCATOR_HPP - -#include -#include - -#include "ekg/gpu/api.hpp" - -#define EKG_ASSERT_SCISSOR(scissor, rect_child, p_parent_scissor) if (!ekg::p_core->gpu_allocator.sync_scissor(scissor, rect_child, p_parent_scissor)) return - -namespace ekg::gpu { - class allocator { - public: - static float concave; - static bool high_priority; - static uint64_t current_rendering_data_count; - protected: - std::vector data_list {}; - std::vector geometry_resource_list {}; - - std::vector high_priority_data_list {}; - std::vector high_priority_geometry_resource_list {}; - - uint64_t high_priority_data_instance_index {}; - uint64_t data_instance_index {}; - uint64_t geometry_resource_index {}; - uint64_t previous_geometry_resource_list_size {}; - - int32_t simple_shape_index {-1}; - int32_t previous_factor {}; - - int32_t begin_stride_count {}; - int32_t end_stride_count {}; - - bool factor_changed {}; - bool simple_shape {}; - bool out_of_scissor_rect {}; - ekg::rect_t scissor_instance {}; - public: - /* - * Init gpu allocator. - */ - void init(); - - /* - * Delete all GPU buffers & GL stuf. - */ - void quit(); - - /* - * Bind a new gpu data. - */ - ekg::io::gpu_data_t &bind_current_data(); - - /* - * Clear current gpu data active. - */ - void clear_current_data(); - - /* - * Find registered gpu data in allocator's batch. - */ - ekg::io::gpu_data_t *get_data_by_id(int32_t id); - - /* - * Get current gpu data. - */ - uint32_t get_current_data_id(); - - /* - * Bind texture for send in data. - */ - void bind_texture(ekg::sampler_t *p_sampler); - - /* - * Insert geometry positions here: - * vertex x, y and texture coords u, and v. - */ - void push_back_geometry(float, float, float, float); - - /* - * Update animations. - */ - void on_update(); - - /* - * Invoke allocator data reader. - */ - void invoke(); - - /* - * Dispatch current data. - */ - void dispatch(); - - /* - * Revoke data to GPU. - */ - void revoke(); - - /* - * Draw all data from the gpu allocator's batch. - */ - void draw(); - - /* - * Sync active scissor position. - */ - bool sync_scissor( - ekg::rect_t &scissor, - ekg::rect_t &rect_child, - ekg::rect_t *p_parent_scissor - ); - - /** - * Set scissor position and size, unsafe due the stack GPU-data order in allocator, - * which affect the rendering. - * - * When a GPU-data is created it receives the current scissor placement (position and size), - * and the next GPU-data stacked may not receive the same placements. - **/ - void unsafe_set_scissor_placement( - float x, - float y, - float w, - float h - ); - }; -} - -#endif diff --git a/include/ekg/gpu/api.hpp b/include/ekg/gpu/api.hpp index 60f80a7b..35b46f23 100644 --- a/include/ekg/gpu/api.hpp +++ b/include/ekg/gpu/api.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,9 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -#ifndef EKG_GPU_BASE_IMPL_HPP -#define EKG_GPU_BASE_IMPL_HPP +#ifndef EKG_GPU_API_HPP +#define EKG_GPU_API_HPP #include #include @@ -31,20 +30,25 @@ #include #include +#include "sampler.hpp" +#include "data.hpp" #include "ekg/math/geometry.hpp" -#include "ekg/io/typography.hpp" -#include "ekg/io/gpu.hpp" +#include "ekg/io/font.hpp" + +namespace ekg { + enum class which_gpu_api { + opengl, + opengles, + vulkan + }; +} namespace ekg::gpu { class api { protected: - std::string_view rendering_shader_fragment_source {}; - float projection_matrix[16] {}; - public: - ekg::gpu_api gpu_api {}; - ekg::rect_t viewport {}; + float mat4x4_proj_matrix[16] {}; public: - void set_rendering_shader_fragment_source(std::string_view source); + ekg::which_gpu_api which_gpu_api {}; public: virtual void log_vendor_details() {}; virtual void init() {}; @@ -58,39 +62,39 @@ namespace ekg::gpu { int32_t h ) {}; - virtual void re_alloc_geometry_resources( + virtual void pass_geometry_buffer_to_gpu( const float *p_data, uint64_t size ) {}; - virtual void draw( - std::vector &loaded_gpu_data_list + virtual void pass_gpu_data_buffer_to_gpu( + std::vector &gpu_data_buffer ) {}; virtual ekg::flags_t gen_font_atlas_and_map_glyph( - ekg::sampler_t *p_sampler, + ekg::sampler_t &sampler, ekg::io::font_face_t *p_font_face_text, ekg::io::font_face_t *p_font_face_emoji, ekg::io::font_face_t *p_font_face_kanjis, ekg::rect_t &atlas_rect, std::vector &char_to_gen_sampler_list, - std::unordered_map &mapped_gpu_data_char_glyph, + std::unordered_map &mapped_gpu_data_char_glyph, float &non_swizzlable_range - ) { return ekg::result::not_implemented; }; + ) { return ekg::result::failed_not_implemented; }; virtual ekg::flags_t allocate_sampler( - ekg::sampler_allocate_info_t *p_sampler_allocate_info, - ekg::sampler_t *p_sampler - ) { return ekg::result::not_implemented; } + ekg::sampler_allocate_info_t &sampler_allocate_info, + ekg::sampler_t &sampler + ) { return ekg::result::failed_not_implemented; } virtual ekg::flags_t fill_sampler( - ekg::sampler_fill_info_t *p_sampler_fill_info, - ekg::sampler_t *p_sampler - ) { return ekg::result::not_implemented; }; + ekg::sampler_fill_info_t &sampler_fill_info, + ekg::sampler_t &sampler + ) { return ekg::result::failed_not_implemented; }; - virtual ekg::flags_t bind_sampler( - ekg::sampler_t *p_sampler - ) { return ekg::result::not_implemented; }; + virtual ekg::at_t &bind_sampler( + ekg::sampler_t &sampler + ) { return ekg::at_t::not_found; }; }; } diff --git a/include/ekg/ui/button/button_widget.hpp b/include/ekg/gpu/data.hpp similarity index 69% rename from include/ekg/ui/button/button_widget.hpp rename to include/ekg/gpu/data.hpp index b972135a..65ee15e9 100644 --- a/include/ekg/ui/button/button_widget.hpp +++ b/include/ekg/gpu/data.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,23 +21,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_GPU_DATA_HPP +#define EKG_GPU_DATA_HPP -#ifndef EKG_UI_BUTTON_WIDGET_HPP -#define EKG_UI_BUTTON_WIDGET_HPP +#include "ekg/io/memory.hpp" +#include "ekg/io/descriptor.hpp" -#include "ekg/ui/button/button.hpp" -#include "ekg/ui/abstract.hpp" - -namespace ekg::ui { - class button : public ekg::ui::abstract { +namespace ekg::gpu { + struct data_t { public: - ekg::button_t descriptor {}; + static ekg::gpu::data_t not_found; public: - ekg::rect_t text_rect {}; + float buffer[12] {}; + ekg::at_t sampler_at {ekg::at_t::not_found}; + int8_t line_thickness {}; + int32_t begin_stride {}; + int32_t end_stride {}; + ekg::hash_t hash {}; + int32_t scissor_id {-1}; public: - void on_reload() override; - void on_event(ekg::io::stage stage) override; - void on_draw() override; + ekg_descriptor(ekg::gpu::data_t); }; } diff --git a/include/ekg/os/ekg_opengl.hpp b/include/ekg/gpu/opengl/gl.hpp similarity index 78% rename from include/ekg/os/ekg_opengl.hpp rename to include/ekg/gpu/opengl/gl.hpp index 3015a370..400ba5ce 100644 --- a/include/ekg/os/ekg_opengl.hpp +++ b/include/ekg/gpu/opengl/gl.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,18 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -// TODO: fix pattern to new-project rules -// TODO: remove useless macros -// TODO: add some comments if necessary - -#ifndef EKG_OS_OPENGL_HPP -#define EKG_OS_OPENGL_HPP +#ifndef EKG_GPU_OPENGL_GL_HPP +#define EKG_GPU_OPENGL_GL_HPP #if defined(__ANDROID__) -#include + #include #else -#include + #include #endif #include @@ -47,9 +42,9 @@ namespace ekg { class opengl : public ekg::gpu::api { protected: - std::vector bound_sampler_list {}; + std::vector bound_sampler_list {}; std::string_view glsl_version {}; - + int32_t uniform_active_texture {}; int32_t uniform_active_tex_slot {}; int32_t uniform_content {}; @@ -83,35 +78,35 @@ namespace ekg { void quit() override; void pre_re_alloc() override; void update_viewport(int32_t w, int32_t h) override; - void re_alloc_geometry_resources(const float *p_data, uint64_t size) override; + void pass_geometry_buffer_to_gpu(const float *p_data, uint64_t size) override; - void draw( - std::vector &loaded_gpu_data_list + void pass_gpu_data_buffer_to_gpu( + std::vector &gpu_data_buffer ) override; ekg::flags_t allocate_sampler( - ekg::sampler_allocate_info_t *p_sampler_allocate_info, - ekg::sampler_t *p_sampler + ekg::sampler_allocate_info_t &sampler_allocate_info, + ekg::sampler_t &sampler ) override; ekg::flags_t fill_sampler( - ekg::sampler_fill_info_t *p_sampler_fill_info, - ekg::sampler_t *p_sampler + ekg::sampler_fill_info_t &sampler_fill_info, + ekg::sampler_t &sampler ) override; ekg::flags_t gen_font_atlas_and_map_glyph( - ekg::sampler_t *p_sampler, + ekg::sampler_t &sampler, ekg::io::font_face_t *p_font_face_text, ekg::io::font_face_t *p_font_face_emoji, ekg::io::font_face_t *p_font_face_kanjis, ekg::rect_t &atlas_rect, std::vector &char_to_gen_sampler_list, - std::unordered_map &mapped_gpu_data_char_glyph, + std::unordered_map &mapped_gpu_data_char_glyph, float &non_swizzlable_range ) override; - ekg::flags_t bind_sampler( - ekg::sampler_t *p_sampler + ekg::at_t &bind_sampler( + ekg::sampler_t &sampler ) override; }; } diff --git a/src/gpu/api.cpp b/include/ekg/gpu/opengl/shaders.hpp similarity index 72% rename from src/gpu/api.cpp rename to include/ekg/gpu/opengl/shaders.hpp index 8cf14f96..caee9b7a 100644 --- a/src/gpu/api.cpp +++ b/include/ekg/gpu/opengl/shaders.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,9 +21,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_GPU_OPENGL_SHADERS_HPP +#define EKG_GPU_OPENGL_SHADERS_HPP #include "ekg/gpu/api.hpp" -void ekg::gpu::api::set_rendering_shader_fragment_source(std::string_view source) { - this->rendering_shader_fragment_source = source; -} \ No newline at end of file +namespace ekg::gpu { + void glsl_opengl_pipeline_vsh( + const std::string &glsl_version, + ekg::which_gpu_api which_gpu_api, + std::string &out_shader + ); + + void glsl_opengl_pipeline_fsh( + const std::string &glsl_version, + ekg::which_gpu_api which_gpu_api, + std::string &out_shader + ); +} + +#endif diff --git a/include/ekg/gpu/opengl_pipeline_template.hpp b/include/ekg/gpu/opengl_pipeline_template.hpp deleted file mode 100644 index 251357e5..00000000 --- a/include/ekg/gpu/opengl_pipeline_template.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef EKG_GPU_OPENGL_PIPELINE_TEMPLATE_HPP -#define EKG_GPU_OPENGL_PIPELINE_TEMPLATE_HPP - -#include "ekg/os/ekg_opengl.hpp" - -namespace ekg::gpu { - void get_standard_vertex_shader( - std::string glsl_version, - ekg::gpu_api gpu_api, - std::string &output_kernel_source - ); - - void get_standard_fragment_shader( - std::string glsl_version, - ekg::gpu_api gpu_api, - std::string &output_kernel_source - ); -} - -#endif diff --git a/include/ekg/gpu/sampler.hpp b/include/ekg/gpu/sampler.hpp new file mode 100644 index 00000000..1aef96f8 --- /dev/null +++ b/include/ekg/gpu/sampler.hpp @@ -0,0 +1,74 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_GPU_SAMPLER_HPP +#define EKG_GPU_SAMPLER_HPP + +#include "ekg/io/memory.hpp" +#include "ekg/io/descriptor.hpp" +#include "ekg/math/geometry.hpp" + +namespace ekg { + struct sampler_t { + public: + static constexpr ekg::type type {ekg::type::sampler}; + static ekg::sampler_t not_found; + public: + const char *p_tag {}; + uint32_t w {}; + uint32_t h {}; + uint32_t channel {}; + uint32_t gl_id {}; + int8_t gl_protected_active_index {-1}; + bool is_protected {}; + public: + ekg_descriptor(ekg::sampler_t); + }; + + struct sampler_info_t { + public: + const char *p_tag {}; + int32_t offset[2] {}; + int32_t w {}; + int32_t h {}; + int32_t gl_parameter_filter[2] {}; + int32_t gl_wrap_modes[2] {}; + int32_t gl_internal_format {}; + uint32_t gl_format {}; + uint32_t gl_type {}; + bool gl_unpack_alignment {}; + bool gl_generate_mipmap {}; + void *pv_data {}; + }; + + typedef sampler_info_t sampler_allocate_info_t; + typedef sampler_info_t sampler_fill_info_t; + + ekg::flags_t sampler_src_r8_to_r8g8b8a8( + const ekg::vec2_t &size, + const unsigned char *p_src, + std::vector &dst + ); +} + +#endif diff --git a/src/io/task.cpp b/include/ekg/handler/callback.hpp similarity index 63% rename from src/io/task.cpp rename to include/ekg/handler/callback.hpp index ff5804ce..c036d565 100644 --- a/src/io/task.cpp +++ b/include/ekg/handler/callback.hpp @@ -21,44 +21,34 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_HANDLER_CALLBACK_HPP +#define EKG_HANDLER_CALLBACK_HPP -#include "ekg/io/task.hpp" -#include "ekg/core/runtime.hpp" +#include "ekg/io/memory.hpp" +#include "ekg/io/descriptor.hpp" +#include +#include -void ekg::io::dispatch( - ekg::io::operation op -) { - switch (op) { - case ekg::io::operation::high_frequency: - break; - default: - ekg::p_core->service_handler.dispatch_pre_allocated_task( - static_cast(op) - ); - break; - } -} - -void ekg::io::trigger( - bool must_trigger, - ekg::action action, - ekg::actions actions, - ekg::properties_t *p_properties -) { - ekg::task_t *&p_task { - actions[action] +namespace ekg { + struct info_t { + public: + std::string tag {}; + void *pv_data {nullptr}; }; - if ( - p_task == nullptr - || - p_properties == nullptr - || - !must_trigger - ) { - return; - } + using callback_function_t = void(*)(ekg::info_t&); - p_task->info.p_properties = p_properties; - p_task->function(p_task->info); + struct callback_t { + public: + static constexpr ekg::type type {ekg::type::callback}; + static ekg::callback_t not_found; + public: + ekg::info_t info {}; + std::function lambda {}; + ekg::callback_function_t function {nullptr}; + public: + ekg_descriptor(ekg::callback_t); + }; } + +#endif diff --git a/include/ekg/service/handler.hpp b/include/ekg/handler/callback/handler.hpp similarity index 69% rename from include/ekg/service/handler.hpp rename to include/ekg/handler/callback/handler.hpp index 007f7543..307406ae 100644 --- a/include/ekg/service/handler.hpp +++ b/include/ekg/handler/callback/handler.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,33 +21,31 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_HANDLER_CALLBACK_HANDLER_HPP +#define EKG_HANDLER_CALLBACK_HANDLER_HPP -#ifndef EKG_SERVICE_HANDLER_HPP -#define EKG_SERVICE_HANDLER_HPP - -#include -#include #include -#include -#include -#include +#include -#include "ekg/io/task.hpp" +#include "ekg/io/memory.hpp" +#include "ekg/handler/callback.hpp" -namespace ekg::service { - class handler { +namespace ekg::handler { + enum status { + not_dispateched = 2 << 1, + dispatched = 2 << 2 + }; + + class callback { protected: - std::queue task_queue {}; - std::vector pre_allocated_task_list {}; + std::vector loaded {}; + std::queue queue {}; public: - ekg::task_t *&allocate(); - - void dispatch_pre_allocated_task(uint64_t index); - void dispatch(ekg::task_t *p_task); - void init(); - void quit(); - void on_update(); + ekg::callback_t &load(); + void dispatch(size_t index); + void dispatch(ekg::at_t &at); + void update(); }; } diff --git a/include/ekg/handler/input.hpp b/include/ekg/handler/input.hpp new file mode 100644 index 00000000..c52ae1f1 --- /dev/null +++ b/include/ekg/handler/input.hpp @@ -0,0 +1,98 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_HANDLER_INPUT_HPP +#define EKG_HANDLER_INPUT_HPP + +#include "ekg/io/timing.hpp" +#include "ekg/math/geometry.hpp" + +#include +#include +#include + +namespace ekg { + enum class system_cursor { + arrow, + ibeam, + wait, + crosshair, + wait_arrow, + size_nwse, + size_nesw, + size_we, + size_ns, + size_all, + no, + hand + }; + + enum class special_key { + left_shift, + right_shift, + left_ctrl, + right_ctrl, + left_alt, + right_alt, + tab, + unknown + }; + + struct input_info_t { + public: + float scroll_speed {0.4f}; + ekg::timing_t ui_timeout_timing {}; + ekg::timing_t ui_scrolling_timing {}; + ekg::timing_t timing_last_interact {}; + ekg::timing_t ui_timing {}; + ekg::vec4_t interact {}; + bool was_pressed {}; + bool was_released {}; + bool has_motion {}; + bool was_wheel {}; + bool was_typed {}; + }; + + struct input_key_t { + public: + int32_t key {}; + int32_t scancode {}; + }; + + struct input_bind_t { + public: + std::vector registry {}; + bool state {}; + bool *p_address {}; + }; +} + +namespace ekg { + ekg::input_info_t &input(); + bool fire(std::string_view tag); + bool input(std::string_view input); + void bind(std::string_view tag, std::string_view input); + void bind(std::string_view tag, std::vector inputs); +} + +#endif diff --git a/include/ekg/service/input.hpp b/include/ekg/handler/input/handler.hpp similarity index 50% rename from include/ekg/service/input.hpp rename to include/ekg/handler/input/handler.hpp index 8d32a31c..d7e6adef 100644 --- a/include/ekg/service/input.hpp +++ b/include/ekg/handler/input/handler.hpp @@ -1,43 +1,20 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_SERVICE_INPUT_HPP -#define EKG_SERVICE_INPUT_HPP +#ifndef EKG_HANDLER_INPUT_HANDLER_HPP +#define EKG_HANDLER_INPUT_HANDLER_HPP #include #include #include -#include "ekg/io/log.hpp" -#include "ekg/math/geometry.hpp" -#include "ekg/os/platform.hpp" +#include "ekg/handler/input.hpp" +#include "ekg/io/timing.hpp" -namespace ekg::service { +namespace ekg::handler { class input { protected: + // @TODO: change from unordored map to linear query + std::unordered_map> input_bindings_map {}; - std::unordered_map input_bind_map {}; + std::unordered_map input_bind_map {}; std::unordered_map input_map {}; std::array special_keys {}; @@ -53,7 +30,7 @@ namespace ekg::service { ekg::timing_t double_interact {}; ekg::timing_t last_time_wheel_was_fired {}; public: - ekg::input_t input {}; + ekg::input_info_t input {}; protected: void complete_with_units( std::string &string_builder, @@ -69,12 +46,9 @@ namespace ekg::service { ); public: void init(); - void quit(); - - void on_event(); - - void on_update(); + void poll_event(); + void update(); void insert_input_bind( std::string_view tag, diff --git a/include/ekg/os/ekg_wayland_server.hpp b/include/ekg/handler/theme.hpp similarity index 65% rename from include/ekg/os/ekg_wayland_server.hpp rename to include/ekg/handler/theme.hpp index 01a26418..af8e7ce9 100644 --- a/include/ekg/os/ekg_wayland_server.hpp +++ b/include/ekg/handler/theme.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,14 +21,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_HANDLER_THEME_HPP +#define EKG_HANDLER_THEME_HPP -#ifndef EKG_OS_WAYLAND_SERVER_HPP -#define EKG_OS_WAYLAND_SERVER_HPP - -#include "platform.hpp" +#include "ekg/ui/button/button.hpp" +#include "ekg/ui/frame/frame.hpp" namespace ekg { - class wayland_server : public ekg::os::platform {}; + struct theme_t { + public: + std::string tag {}; + std::string author {}; + std::string description {}; + public: + float layout_offset {}; + ekg::pixel_t layout_margin_thickness {}; + ekg::button_color_scheme_t button_color_scheme {}; + ekg::frame_color_scheme_t frame_color_scheme {}; + }; + + ekg::theme_t &theme(std::string_view tag = ""); + ekg::theme_t &set_current_theme(std::string_view tag); } #endif diff --git a/include/ekg/ui/combobox/ui_combobox.hpp b/include/ekg/handler/theme/handler.hpp similarity index 67% rename from include/ekg/ui/combobox/ui_combobox.hpp rename to include/ekg/handler/theme/handler.hpp index b1a5686d..bab6c401 100644 --- a/include/ekg/ui/combobox/ui_combobox.hpp +++ b/include/ekg/handler/theme/handler.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,20 +21,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_HANDLER_THEME_HANDLER_HPP +#define EKG_HANDLER_THEME_HANDLER_HPP -#ifndef EKG_UI_COMBOBOX_H -#define EKG_UI_COMBOBOX_H +#include "ekg/handler/theme.hpp" -#include "ekg/ui/abstract/ui_abstract.hpp" -#include "ekg/display/display.hpp" +#include +#include -namespace ekg::ui { - class combobox : public ekg::ui::abstract { +namespace ekg::handler { + class theme { protected: - std::vector value_list {}; - std::string value {}; + std::string_view current_theme_tag {"dark-theme"}; + std::map themes {}; public: + void init(); + void quit(); + + ekg::theme_t ®istry(const std::string_view &tag); + ekg::theme_t &get_current_theme(); + ekg::theme_t &set_current_theme(const std::string_view &tag); }; } -#endif \ No newline at end of file +#endif diff --git a/include/ekg/io/algorithm.hpp b/include/ekg/io/algorithm.hpp deleted file mode 100644 index e9ae3e11..00000000 --- a/include/ekg/io/algorithm.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef EKG_IO_ALGORITHM_HPP -#define EKG_IO_ALGORITHM_HPP - -#include "ekg/io/memory.hpp" -#include "ekg/ui/stack.hpp" - -#include -#include - -namespace ekg { - ekg::flags_t add_child_to_parent( - ekg::properties_t *p_parent, - ekg::properties_t *p_child - ); - - ekg::properties_t *find( - ekg::stack_t *p_stack, - std::string_view widget_tag - ); - - ekg::flags_t destroy( - ekg::stack_t *p_stack, - ekg::properties_t *p_parent - ); - - ekg::flags_t find_and_destroy( - ekg::stack_t *p_stack, - std::string_view widget_tag - ); -} - -namespace ekg::io { - struct target_collector_t { - public: - ekg::id_t unique_id {}; - std::vector storage {}; - bool was_target_found {}; - size_t count {}; - }; - - ekg::flags_t push_back_widget_tree_recursively( - ekg::io::target_collector_t *p_target_collector, - ekg::ui::abstract *p_widget - ); -} - -#endif diff --git a/include/ekg/ui/properties.hpp b/include/ekg/io/descriptor.hpp similarity index 55% rename from include/ekg/ui/properties.hpp rename to include/ekg/io/descriptor.hpp index 1772291c..1a25841e 100644 --- a/include/ekg/ui/properties.hpp +++ b/include/ekg/io/descriptor.hpp @@ -21,56 +21,47 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -#ifndef EKG_UI_PROPERTIES_HPP -#define EKG_UI_PROPERTIES_HPP +#ifndef EKG_IO_DESCRIPTOR_HPP +#define EKG_IO_DESCRIPTOR_HPP #include "ekg/io/memory.hpp" -#include "ekg/math/geometry.hpp" -#include "ekg/io/gpu.hpp" -#include "ekg/ui/types.hpp" - -#include -#include -#include namespace ekg { - struct properties_t { - public: - std::string tag {}; - ekg::flags_t dock {}; - ekg::type type {}; - ekg::id_t unique_id {}; - ekg::level level {}; - ekg::rect_t rect {}; - - void *p_descriptor {}; - void *p_widget {}; - void *p_stack {}; - - ekg::properties_t *p_abs_parent {}; - ekg::properties_t *p_parent {}; - std::vector children {}; - - bool is_enabled {}; - bool is_alive {}; - bool is_visible {}; - bool is_parentable {}; - bool is_docknizable {}; - bool must_refresh_size {true}; - public: - template - operator t&() { - return *static_cast(this->p_descriptor); - } - - template - operator t*() { - return static_cast(this->p_widget); - } + enum type : ekg::flags_t { + unknown = 0, + callback = 1, + property = 2, + sampler = 3, + stack = 4, + button = 5, + scrollbar = 6, + frame = 7 }; - - typedef ekg::properties_t* top_level_t; } +#define ekg_descriptor(descriptor_t) \ + public: \ + ekg::at_t at { \ + .unique_id = ekg::not_found, \ + .index = ekg::not_found, \ + .flags = ekg::not_found \ + }; \ + bool is_dead {}; \ + public: \ + bool operator == (descriptor_t &descriptor) { \ + return ( \ + (this->is_dead && descriptor.at == descriptor_t::not_found.at) \ + || \ + (!this->is_dead && this->at == descriptor.at) \ + ); \ + } \ +\ + bool operator != (descriptor_t &descriptor) { \ + return !(*this == descriptor); \ + } \ +\ + operator ekg::at_t() { \ + return this->at; \ + } + #endif diff --git a/include/ekg/io/design.hpp b/include/ekg/io/design.hpp deleted file mode 100644 index 10cdc2a8..00000000 --- a/include/ekg/io/design.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef EKG_IO_DESIGN_HPP -#define EKG_IO_DESIGN_HPP - -#include - -#include "ekg/ui/button/button.hpp" -#include "ekg/ui/checkbox/checkbox.hpp" -#include "ekg/ui/combobox/combobox.hpp" -#include "ekg/ui/frame/frame.hpp" -#include "ekg/ui/label/label.hpp" -#include "ekg/ui/listbox/listbox.hpp" -#include "ekg/ui/menu/menu.hpp" -#include "ekg/ui/popup/popup.hpp" -#include "ekg/ui/scrollbar/scrollbar.hpp" -#include "ekg/ui/slider/slider.hpp" -#include "ekg/ui/textbox/textbox.hpp" - -namespace ekg { - struct theme_t { - public: - std::string_view name {}; - std::string_view author {}; - std::string_view description {}; - std::string_view path {}; - public: - ekg::pixel_t layout_offset {2.0f}; - ekg::pixel_thickness_t layout_margin_thickness {2}; - - ekg::button_theme_t button {}; - ekg::checkbox_theme_t checkbox {}; - ekg::combobox_theme_t combobox {}; - ekg::frame_theme_t frame {}; - ekg::label_theme_t label {}; - ekg::listbox_theme_t listbox {}; - ekg::menu_theme_t menu {}; - ekg::popup_theme_t popup {}; - ekg::scrollbar_theme_t scrollbar {}; - ekg::slider_theme_t slider {}; - ekg::textbox_theme_t textbox {}; - }; - - std::map &themes(); - ekg::theme_t &theme(std::string_view name = ""); - void theme(ekg::theme_t theme); - ekg::flags_t set_current_theme(std::string_view name); -} - -#endif diff --git a/include/ekg/io/event.hpp b/include/ekg/io/event.hpp new file mode 100644 index 00000000..701bd3f4 --- /dev/null +++ b/include/ekg/io/event.hpp @@ -0,0 +1,98 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_IO_EVENT_HPP +#define EKG_IO_EVENT_HPP + +#include "ekg/handler/input.hpp" + +namespace ekg { + enum behavior : ekg::flags_t { + no_auto_set_viewport_when_resize = 2 << 1 + }; +} + +namespace ekg::io { + enum class operation { + swap, + reload, + docknize, + scalenize, + high_frequency + }; + + void dispatch( + ekg::io::operation op, + ekg::at_t &property_at = ekg::at_t::not_found + ); + + enum class stage { + pre, + process, + post + }; + + enum class event_type { + none, + text_input, + mouse_button_up, + mouse_button_down, + mouse_motion, + mouse_wheel, + finger_up, + finger_down, + finger_motion, + key_down, + key_up + }; + + struct event_t { + public: + ekg::io::event_type type {}; + std::string_view text_input {}; + uint8_t mouse_button {}; + + int32_t mouse_motion_x {}; + int32_t mouse_motion_y {}; + + int32_t mouse_wheel_x {}; + int32_t mouse_wheel_y {}; + + float mouse_wheel_precise_x {}; + float mouse_wheel_precise_y {}; + + ekg::input_key_t key {}; + + float finger_x {}; + float finger_y {}; + + float finger_dx {}; + float finger_dy {}; + }; +} + +namespace ekg::io { + +} + +#endif diff --git a/include/ekg/io/typography.hpp b/include/ekg/io/font.hpp similarity index 81% rename from include/ekg/io/typography.hpp rename to include/ekg/io/font.hpp index 9ae93d10..c1859ed2 100644 --- a/include/ekg/io/typography.hpp +++ b/include/ekg/io/font.hpp @@ -1,18 +1,18 @@ /** * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,9 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -#ifndef EKG_IO_TYPOGRAPHY_HPP -#define EKG_IO_TYPOGRAPHY_HPP +#ifndef EKG_IO_FONT_HPP +#define EKG_IO_FONT_HPP #include #include FT_FREETYPE_H @@ -33,50 +32,54 @@ #include "memory.hpp" namespace ekg { - // TODO: add non fixed font-renderer - enum font { + /** + * @TODO: Add non-fixed size font-rendering + **/ + enum class font { small, - normal, + medium, big }; + + typedef FT_Library ft_library; } namespace ekg::io { - constexpr size_t supported_faces_size {3}; - enum font_face_type { text, emojis, kanjis }; - struct glyph_char_t { - public: - float x {}; - float wsize {}; - float w {}; - float h {}; - float top {}; - float left {}; - float kerning {}; - bool was_sampled {}; - }; + constexpr size_t enum_font_face_type_size {3}; struct font_face_t { public: FT_Face ft_face {}; FT_GlyphSlot ft_glyph_slot {}; + FT_Vector highest_glyph_size {}; std::string path {}; - int32_t size {}; + uint32_t size {}; bool was_face_changed {}; bool was_size_changed {}; bool was_loaded {}; - FT_Vector highest_glyph_size {}; }; - ekg::flags_t refresh_font_face( - ekg::io::font_face_t *p_font_face + ekg::flags_t font( + ekg::io::font_face_t &font_face ); + + struct glyph_t { + public: + float x {}; + float wsize {}; + float w {}; + float h {}; + float top {}; + float left {}; + float kerning {}; + bool was_sampled {}; + }; } #endif diff --git a/include/ekg/io/gpu.hpp b/include/ekg/io/gpu.hpp deleted file mode 100644 index 60623a82..00000000 --- a/include/ekg/io/gpu.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef EKG_IO_GPU_HPP -#define EKG_IO_GPU_HPP - -#include -#include - -#include "ekg/io/memory.hpp" -#include "ekg/math/geometry.hpp" - -namespace ekg { - enum class gpu_api { - vulkan, - opengl, - opengles, - webgpu - }; - - struct sampler_info_t { - public: - const char *p_tag {}; - int32_t offset[2] {}; - int32_t w {}; - int32_t h {}; - int32_t gl_parameter_filter[2] {}; - int32_t gl_wrap_modes[2] {}; - int32_t gl_internal_format {}; - uint32_t gl_format {}; - uint32_t gl_type {}; - bool gl_unpack_alignment {}; - bool gl_generate_mipmap {}; - void *p_data {}; - }; - - typedef sampler_info_t sampler_allocate_info_t; - typedef sampler_info_t sampler_fill_info_t; - - struct sampler_t { - public: - const char *p_tag {}; - uint32_t w {}; - uint32_t h {}; - uint32_t channel {}; - uint32_t gl_id {}; - int8_t gl_protected_active_index {-1}; - bool is_protected {}; - }; - - enum class layer { - background, - highlight, - active - }; - - struct layer_group_t { - protected: - std::array samplers {}; - public: - ekg::sampler_t *&operator[](ekg::layer layer) { - return this->samplers[static_cast(layer)]; - } - }; - - template - struct layer_t { - protected: - std::array layer_groups {}; - public: - ekg::sampler_t *&operator[](ekg::layer layer) { - return this->layer_groups[0][layer]; - } - - ekg::layer_group_t &operator[](size_t group) { - return this->layer_groups[group]; - } - }; - - ekg::flags_t gpu_allocate_sampler( - ekg::sampler_allocate_info_t *p_sampler_allocate_info, - ekg::sampler_t *p_sampler - ); - - ekg::flags_t gpu_fill_sampler( - ekg::sampler_fill_info_t *p_sampler_fill_info, - ekg::sampler_t *p_sampler - ); - - ekg::flags_t image_src_r8_convert_to_r8g8b8a8( - ekg::vec2_t size, - const unsigned char *p_src, - std::vector &dst - ); -} - -namespace ekg::io { - struct gpu_data_t { - public: - float buffer_content[12] {}; - int32_t sampler_index {-1}; - int8_t line_thickness {}; - int32_t begin_stride {}; - int32_t end_stride {}; - int32_t factor {}; - int32_t scissor_id {-1}; - }; -} - -#endif diff --git a/include/ekg/io/input.hpp b/include/ekg/io/input.hpp deleted file mode 100644 index 44ca6bee..00000000 --- a/include/ekg/io/input.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef EKG_IO_INPUT_HPP -#define EKG_IO_INPUT_HPP - -#include "ekg/io/log.hpp" -#include "ekg/math/geometry.hpp" - -#include -#include -#include - -namespace ekg { - enum class system_cursor_type { - arrow, - ibeam, - wait, - crosshair, - wait_arrow, - size_nwse, - size_nesw, - size_we, - size_ns, - size_all, - no, - hand - }; - - enum class special_key_type { - left_shift, - right_shift, - left_ctrl, - right_ctrl, - left_alt, - right_alt, - tab, - unknown - }; - - enum internal_behavior { - no_auto_set_viewport_when_resize = 2 << 1 - }; - - struct input_t { - public: - float scroll_speed {0.4f}; - ekg::timing_t ui_timeout_timing {}; - ekg::timing_t ui_scrolling_timing {}; - ekg::timing_t timing_last_interact {}; - ekg::timing_t ui_timing {}; - ekg::vec4_t interact {}; - bool was_pressed {}; - bool was_released {}; - bool has_motion {}; - bool was_wheel {}; - bool was_typed {}; - }; - - bool fire(std::string_view tag); - bool input(std::string_view input); - void bind(std::string_view tag, std::string_view input); - void bind(std::string_view tag, std::vector inputs); - ekg::input_t &input(); -} - -namespace ekg::io { - enum class stage { - pre, - process, - post - }; - - enum class input_event_type { - none, - text_input, - mouse_button_up, - mouse_button_down, - mouse_motion, - mouse_wheel, - finger_up, - finger_down, - finger_motion, - key_down, - key_up - }; - - struct input_key_t { - public: - int32_t key {}; - int32_t scancode {}; - }; - - struct serialized_input_event_t { - public: - ekg::io::input_event_type type {}; - std::string_view text_input {}; - uint8_t mouse_button {}; - - int32_t mouse_motion_x {}; - int32_t mouse_motion_y {}; - - int32_t mouse_wheel_x {}; - int32_t mouse_wheel_y {}; - - float mouse_wheel_precise_x {}; - float mouse_wheel_precise_y {}; - - ekg::io::input_key_t key {}; - - float finger_x {}; - float finger_y {}; - - float finger_dx {}; - float finger_dy {}; - }; - - struct input_bind_t { - public: - std::vector registry {}; - bool state {}; - bool *p_address {}; - }; -} - -#endif diff --git a/include/ekg/io/log.hpp b/include/ekg/io/log.hpp index d5b61a44..504f1492 100644 --- a/include/ekg/io/log.hpp +++ b/include/ekg/io/log.hpp @@ -1,3 +1,26 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef EKG_IO_LOG_HPP #define EKG_IO_LOG_HPP @@ -27,22 +50,6 @@ namespace ekg { ekg::log::buffered = false; } } - - static void print(std::string_view msg) { - std::cout << "[EKG-PRINT] " << msg << std::endl; - } - - template - static void trace(bool should, t trace, bool interrupt_runtime = false) { - if (!should) { - return; - } - - std::cout << "[EKG-TRACE] " << trace << std::endl; - if (interrupt_runtime) { - std::terminate(); - } - } public: template explicit log(t content) { @@ -51,7 +58,7 @@ namespace ekg { explicit log() { ekg::log::buffered = true; - ekg::log::buffer << "[EKG-INFO] "; + ekg::log::buffer << "[EKG] "; } ~log() { @@ -69,46 +76,6 @@ namespace ekg { return *this; } }; - - struct timing_t { - public: - /** - * The 1 second counter in ms. - */ - static int64_t second; - - /** - * The total running ticks since the application was started. - */ - static int64_t ticks; - public: - int64_t elapsed_ticks {}; - int64_t current_ticks {}; - int64_t ticks_going_on {}; - }; - - bool reach( - ekg::timing_t *p_timing, - int64_t ms - ); - - bool reset_if_reach( - ekg::timing_t *p_timing, - int64_t ms - ); - - bool reset( - ekg::timing_t *p_timing - ); - - bool extend( - ekg::timing_t *p_timing, - int64_t ms - ); - - int64_t interval( - ekg::timing_t *p_timing - ); } #endif diff --git a/include/ekg/io/memory.hpp b/include/ekg/io/memory.hpp index 446c1fc9..21802749 100644 --- a/include/ekg/io/memory.hpp +++ b/include/ekg/io/memory.hpp @@ -21,164 +21,227 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #ifndef EKG_IO_MEMORY_HPP #define EKG_IO_MEMORY_HPP -#define EKG_MEMORY_MUST_FREE_TASKS_AUTOMATICALLY true -#define EKG_MEMORY_ACTIONS_SIZE 7 - -#include #include +#include +#include -namespace ekg { - typedef uint64_t id_t; - typedef uint64_t flags_t; - - enum class number { - f64, - f32, - u64, - i64, - u32, - i32, - u16, - i16, - u8, - i8 - }; +/** + * This is a macro because hash should enjoy of compile-time for generate valid hashes. + * Not really an expensive job for CPU. + **/ +#define ekg_generate_hash(distance, c32, u) static_cast(distance + c32 + u * 100); - template - void retreive_number_type_from_variable_type( - ekg::number &type_number - ) { - const std::type_info &info { - typeid(t) - }; +/** + * A low-level assert used in risks cases where virtual-address should be warned. + **/ +#define ekg_assert_low_level(state, alarm, end) if (state) alarm; end; - if (info == typeid(float)) { - type_number = ekg::number::f32; - } else if (info == typeid(double)) { - type_number = ekg::number::f64; - } else if (info == typeid(int64_t)) { - type_number = ekg::number::i64; - } else if (info == typeid(uint64_t)) { - type_number = ekg::number::u64; - } else if (info == typeid(int32_t)) { - type_number = ekg::number::i32; - } else if (info == typeid(uint32_t)) { - type_number = ekg::number::u32; - } else if (info == typeid(int16_t)) { - type_number = ekg::number::i16; - } else if (info == typeid(uint16_t)) { - type_number = ekg::number::u16; - } else if (info == typeid(int8_t)) { - type_number = ekg::number::i8; - } else if (info == typeid(uint8_t)) { - type_number = ekg::number::u8; - } - } +/** + * A dev-purpose log level untracked for EKG. + **/ +#define ekg_log_low_level(log_content) std::cout << log_content << std::endl; + +namespace ekg { + typedef size_t flags_t; + typedef size_t id_t; + typedef int32_t hash_t; enum result { - success = 2 << 1, - failed = 2 << 2, - could_not_find = 2 << 3, - not_implemented = 2 << 4 + success, + failed, + failed_unknown, + failed_not_implemented }; template constexpr bool has(ekg::flags_t bits, t bit) { - return bits & bit; + return (bits & bit) == bit; } template constexpr bool strip(ekg::flags_t &bits, t bit) { bits = bits & ~(bit); - return bits & bit; + return ekg::has(bits, bit); } template constexpr ekg::flags_t &put(ekg::flags_t &bits, t bit) { return (bits |= bit); } +} - template - class value { - protected: - t cache {}; - t *p_address {nullptr}; +/** + * Memory-pool and virtual address. + **/ +namespace ekg { + /** + * Broken heart hash.......... + **/ + constexpr ekg::id_t not_found {2942656639}; + + struct at_t { public: - bool was_changed {}; + static ekg::at_t not_found; public: - value() = default; - - value(t *p_address) { - this->p_address = p_address; - this->was_changed = true; + ekg::id_t unique_id {ekg::not_found}; + size_t index {ekg::not_found}; + ekg::flags_t flags {ekg::not_found}; + public: + bool operator == (ekg::at_t &at) { + return ( + this->flags == at.flags + && + this->index == at.index + && + this->unique_id == at.unique_id + ); } - value(t value) { - this->p_address = nullptr; - this->cache = value; - this->was_changed = true; + bool operator != (ekg::at_t &at) { + return !(*this == at); } + }; - value(const char *p_char) { - this->get_value() = p_char; - this->was_changed = true; - } + template + class pool { + protected: + std::vector loaded {}; + ekg::id_t highest_unique_id {}; + size_t virtual_memory_capacity {256}; + public: + pool() { + this->loaded.reserve(this->virtual_memory_capacity); + }; - t &move(t *p_address) { - this->p_address = p_address; - this->was_changed = true; - return this->get_value(); - } + t &push_back(t copy) { + this->loaded.push_back(copy); + + size_t index {this->loaded.size() - 1}; + t &descriptor {this->loaded.at(index)}; - t &set_value(t value) { - this->get_value() = value; - this->was_changed = true; - return this->get_value(); + descriptor.at.unique_id = this->highest_unique_id++; + descriptor.at.flags = t::type; + descriptor.at.index = index; + + return descriptor; } - t &get_value() { - return ( - this->p_address - ? - /** - * - * idk idc i want - * todo: add useless comment soon - * - **/ - *this->p_address - : - this->cache - ); + t &query(ekg::at_t &at) { + if (at.index == ekg::not_found) { + return t::not_found; + } + + if ( + at.index >= this->loaded.size() + || + this->loaded.at(at.index).at.unique_id != at.unique_id + ) { + size_t size {this->loaded.size()}; + for (size_t it {}; it < size; it++) { + t &descriptor {this->loaded.at(it)}; + descriptor.at.index = it; + if (descriptor.at.unique_id == at.unique_id) { + at.index = it; + return descriptor; + } + } + + return t::not_found; + } + + t &descriptor {this->loaded.at(at.index)}; + return descriptor; } + }; +} + +/** + * Value system. + **/ +namespace ekg { + template + class value { + protected: + t val {}; + t *p {}; + t previous {}; + bool changed {}; public: - operator t() { - return this->get_value(); - } + value() { + this->ownership(nullptr); + }; - /** - * static_cast fails - **/ - operator ekg::value() { - return ekg::value {}; + value(t *p_address) { + this->ownership(p_address); + this->changed = true; } - - template - ekg::value &operator = (s value) { - this->get_value() = value; - this->was_changed = true; - return *this; + + value(t val) { + this->get() = val; + this->changed = true; + } + + value(const char *p_char) { + this->get() = p_char; + this->changed = true; + } + + void set(const t &val) { + this->get() = p; + this->changed = true; + } + + t &get() { + return this->p ? *this->p : this->val; + } + + void ownership(t *p_address) { + if (p_address == nullptr) { + return; + } + + this->p = p_address; } + + bool was_changed() { + if (this->changed) { + this->changed = false; + return true; + } + + t &get {this->get()}; + if (this->previous != get) { + this->previous = get; + return true; + } + + return false; + } + }; + + struct mapped_address_sign_info_t { + public: + std::vector ats {}; + void *pv_address {}; }; + + extern struct signed_address_info_t { + public: + std::vector list {}; + size_t current {}; + } sign; + + void map(void *pv_address); + void unmap(void *pv_address); } namespace ekg::io { - constexpr uint64_t invalid_unique_id {static_cast(0)}; - + /** + * @TODO: add a complete docs here please. + **/ template constexpr t &any_static_cast(void *p_any) { return *static_cast(p_any); diff --git a/include/ekg/io/safety.hpp b/include/ekg/io/safety.hpp deleted file mode 100644 index 2296a840..00000000 --- a/include/ekg/io/safety.hpp +++ /dev/null @@ -1,250 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_IO_SAFETY_HPP -#define EKG_IO_SAFETY_HPP - -#include "ekg/core/runtime.hpp" -#include "ekg/io/log.hpp" - -#include "ekg/ui/button/button_widget.hpp" -#include "ekg/ui/frame/frame_widget.hpp" -#include "ekg/ui/label/label_widget.hpp" -#include "ekg/ui/checkbox/checkbox_widget.hpp" -#include "ekg/ui/scrollbar/scrollbar_widget.hpp" -#include "ekg/ui/slider/slider_widget.hpp" - -namespace ekg::io { - template - t *new_widget_instance() { - return dynamic_cast( - ekg::p_core->emplace_back_new_widget_safety( - dynamic_cast(new t {}) - ) - ); - } -} - -namespace ekg { - template - t &make(t descriptor) { - ekg::ui::abstract *p_created_widget { - nullptr - }; - - ekg::properties_t properties { - .tag = descriptor.tag, - .type = descriptor.type, - .unique_id = ekg::p_core->generate_unique_id(), - .is_enabled = true, - .is_alive = true, - .is_visible = true - }; - - ekg::properties_t *p_current_parent_properties { - ekg::p_core->get_current_parent_properties() - }; - - ekg::theme_t ¤t_global_theme {ekg::p_core->service_theme.get_current_theme()}; - - switch (descriptor.type) { - case ekg::type::button: { - ekg::button_t &button { - ekg::io::any_static_cast( - &descriptor - ) - }; - - ekg::ui::button *p_button { - ekg::io::new_widget_instance() - }; - - p_button->descriptor = button; - p_button->descriptor.theme = current_global_theme.button; - - properties.p_descriptor = &p_button->descriptor; - properties.p_widget = p_button; - properties.dock = button.dock; - properties.is_docknizable = false; - properties.is_parentable = true; - - p_created_widget = dynamic_cast(p_button); - p_created_widget->p_descriptor_rect = &p_button->descriptor.rect; - - break; - } - - case ekg::type::frame: { - ekg::frame_t &frame { - ekg::io::any_static_cast( - &descriptor - ) - }; - - ekg::ui::frame *p_frame { - ekg::io::new_widget_instance() - }; - - p_frame->descriptor = frame; - p_frame->descriptor.p_properties = &p_frame->properties; - p_frame->descriptor.theme = current_global_theme.frame; - - properties.p_descriptor = &p_frame->descriptor; - properties.p_widget = p_frame; - properties.dock = frame.dock; - properties.is_docknizable = true; - properties.is_parentable = frame.dock != 0; - - p_created_widget = dynamic_cast(p_frame); - p_created_widget->p_descriptor_rect = &p_frame->descriptor.rect; - - break; - } - - case ekg::type::label: { - ekg::label_t &label { - ekg::io::any_static_cast( - &descriptor - ) - }; - - ekg::ui::label *p_label { - ekg::io::new_widget_instance() - }; - - p_label->descriptor = label; - p_label->descriptor.theme = current_global_theme.label; - - properties.p_descriptor = &p_label->descriptor; - properties.p_widget = p_label; - properties.dock = label.dock; - properties.is_docknizable = false; - properties.is_parentable = true; - - p_created_widget = dynamic_cast(p_label); - p_created_widget->p_descriptor_rect = &p_label->descriptor.rect; - - break; - } - - case ekg::type::checkbox: { - ekg::checkbox_t &checkbox { - ekg::io::any_static_cast( - &descriptor - ) - }; - - ekg::ui::checkbox *p_checkbox { - ekg::io::new_widget_instance() - }; - - p_checkbox->descriptor = checkbox; - p_checkbox->descriptor.theme = current_global_theme.checkbox; - - properties.p_descriptor = &p_checkbox->descriptor; - properties.p_widget = p_checkbox; - properties.dock = checkbox.dock; - properties.is_docknizable = false; - properties.is_parentable = true; - - p_created_widget = dynamic_cast(p_checkbox); - p_created_widget->p_descriptor_rect = &p_checkbox->descriptor.rect; - - break; - } - - case ekg::type::scrollbar: { - ekg::scrollbar_t &scrollbar { - ekg::io::any_static_cast( - &descriptor - ) - }; - - ekg::ui::scrollbar *p_scrollbar { - ekg::io::new_widget_instance() - }; - - p_scrollbar->descriptor = scrollbar; - p_scrollbar->descriptor.theme = current_global_theme.scrollbar; - - properties.p_descriptor = &p_scrollbar->descriptor; - properties.p_widget = p_scrollbar; - properties.is_docknizable = false; - properties.is_parentable = true; - - p_created_widget = dynamic_cast(p_scrollbar); - - if (p_current_parent_properties) { - p_scrollbar->descriptor.p_binded_children = &p_current_parent_properties->children; - p_scrollbar->descriptor.p_binded_rect = ( - static_cast(p_current_parent_properties->p_widget)->p_descriptor_rect - ); - } - - break; - } - - default: - break; - } - - p_created_widget->properties = properties; - - if ( - p_created_widget->properties.is_parentable - && - p_current_parent_properties != nullptr - ) { - ekg::add_child_to_parent( - p_current_parent_properties, - &p_created_widget->properties - ); - } - - ekg::p_core->dispatch_widget_op( - p_created_widget, - ekg::io::operation::reload - ); - - if (p_created_widget->properties.is_docknizable) { - p_created_widget->properties.p_abs_parent = &p_created_widget->properties; - - ekg::p_core->set_current_parent_properties( - &p_created_widget->properties - ); - - ekg::p_core->dispatch_widget_op( - p_created_widget, - ekg::io::operation::layout_docknize - ); - } - - p_created_widget->on_create(); - return p_created_widget->properties; - } - - void pop(ekg::properties_t *p_properties = nullptr); -} - -#endif diff --git a/include/ekg/io/task.hpp b/include/ekg/io/task.hpp deleted file mode 100644 index 7a99f7b0..00000000 --- a/include/ekg/io/task.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef EKG_IO_TASK_HPP -#define EKG_IO_TASK_HPP - -#include -#include -#include - -#include "ekg/core/context.hpp" -#include "ekg/ui/properties.hpp" - -namespace ekg { - struct info_t { - public: - std::string_view tag {}; - ekg::properties_t *p_properties {}; - void *p_data {}; - }; - - struct task_t { - public: - ekg::info_t info {}; - std::function function {}; - bool was_dispatched {}; - bool unsafe_is_heap_memory_type {}; - public: - void *operator new (size_t mem_alloc_size) { - ekg::task_t *p_instance { - static_cast( - ::operator new(mem_alloc_size) - ) - }; - - p_instance->unsafe_is_heap_memory_type = true; - return p_instance; - } - }; - - enum class action { - motion, - press, - release, - active, - focus, - drag, - resize, - hover - }; - - class actions { - protected: - std::array tasks {}; - public: - ekg::task_t *&operator [] (ekg::action action) { - return tasks[static_cast(action)]; - } - }; -} - -namespace ekg::io { - enum operation { - swap, - reload, - layout_docknize, - scale_update, - high_frequency - }; - - void dispatch( - ekg::io::operation op - ); - - void trigger( - bool must_trigger, - ekg::action action, - ekg::actions actions, - ekg::properties_t *p_properties - ); - - template - constexpr t &set(t &must_set, t value) { - if (must_set == value) { - return must_set; - } - - ekg::viewport.redraw = true; - return ( - must_set = value - ); - } -} - -#endif diff --git a/include/ekg/io/timing.hpp b/include/ekg/io/timing.hpp new file mode 100644 index 00000000..92c2222e --- /dev/null +++ b/include/ekg/io/timing.hpp @@ -0,0 +1,71 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_IO_TIMING_HPP +#define EKG_IO_TIMING_HPP + +#include + +namespace ekg { + struct timing_t { + public: + /** + * The 1 second counter in ms. + **/ + static int64_t second; + + /** + * The total running ticks since the application was started. + **/ + static int64_t ticks; + public: + int64_t elapsed_ticks {}; + int64_t current_ticks {}; + int64_t ticks_going_on {}; + }; + + bool reach( + ekg::timing_t &timing, + int64_t ms + ); + + bool reset_if_reach( + ekg::timing_t &timing, + int64_t ms + ); + + bool reset( + ekg::timing_t &timing + ); + + bool extend( + ekg::timing_t &timing, + int64_t ms + ); + + int64_t interval( + ekg::timing_t &timing + ); +} + +#endif diff --git a/include/ekg/io/text.hpp b/include/ekg/io/utf.hpp similarity index 75% rename from include/ekg/io/text.hpp rename to include/ekg/io/utf.hpp index 880ae02b..4ceb9353 100644 --- a/include/ekg/io/text.hpp +++ b/include/ekg/io/utf.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,11 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_IO_UTF_HPP +#define EKG_IO_UTF_HPP -#ifndef EKG_UTIL_TEXT_HPP -#define EKG_UTIL_TEXT_HPP +#include "ekg/math/geometry.hpp" -#include +#include #include #include @@ -35,7 +36,7 @@ namespace ekg { * the UTF-32 unique char into a sequence of UTF-8 * chars. */ - std::string utf_char32_to_string( + std::string utf32_to_string( char32_t char32 ); @@ -44,7 +45,7 @@ namespace ekg { * and end index `size`. If string is empty, return * empty. */ - std::string utf_substr( + std::string utf8_substr( std::string_view string, uint64_t offset, uint64_t size @@ -53,7 +54,7 @@ namespace ekg { /** * Returns the `string` length considering UTF chars. */ - uint64_t utf_length( + uint64_t utf8_length( std::string_view string ); @@ -62,7 +63,7 @@ namespace ekg { * Possibles: * 3, 2, 1, and 0. */ - uint64_t utf_check_sequence( + uint64_t utf8_check_sequence( uint8_t &char8, char32_t &char32, std::string &utf_string, @@ -73,7 +74,7 @@ namespace ekg { /** * Returns a UTF-32 char32 based on UTF-8 sequence. */ - char32_t utf_string_to_char32( + char32_t utf8_to_utf32( std::string_view string ); @@ -81,25 +82,9 @@ namespace ekg { * Fast splitter specialized in `\n` or `\r\n` (non OS unix-based). * UTF to sinalize the string unicode-like suggested by EKG. */ - void utf_decode( + void utf8_split_new_line( std::string_view string, - std::vector &utf_decoded - ); - - /** - * Returns a customised float64 precision as a string. - **/ - std::string string_float64_precision( - double number, - int32_t precision - ); - - /** - * Returns a customised float precision as a string. - **/ - std::string string_float_precision( - float number, - int32_t precision + std::vector &utf8_split_new_lined ); /** @@ -107,11 +92,29 @@ namespace ekg { * then it must allocate and insert elements to * `p_string_split_list` ptr. */ - bool split( + bool utf8_split( std::vector &string_split_list, const std::string &string, char find_char ); + + template + void utf8_number_precision( + std::string &number_to_string, + t number, + size_t precision + ) { + number_to_string = std::to_string(number); + number_to_string = ( + number_to_string.substr( + 0, + ekg::clamp_max( + number_to_string.find('.') + precision + (1 * precision), + number_to_string.size() + ) + ) + ); + } } #endif diff --git a/include/ekg/layout/dimension.hpp b/include/ekg/layout/dimension.hpp deleted file mode 100644 index 5dbc9e20..00000000 --- a/include/ekg/layout/dimension.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef EKG_LAYOUT_DIMENSION_HPP -#define EKG_LAYOUT_DIMENSION_HPP - -#include "ekg/ui/abstract.hpp" - -namespace ekg::layout { - /** - * Estimate height from a container children list. - * Note: Recursive. - **/ - float estimate_docknizable_height( - ekg::ui::abstract *p_parent_widget - ); -} - -#endif diff --git a/include/ekg/layout/docknize.hpp b/include/ekg/layout/docknize.hpp index 78e1f694..22c847ed 100644 --- a/include/ekg/layout/docknize.hpp +++ b/include/ekg/layout/docknize.hpp @@ -1,13 +1,34 @@ -#ifndef EKG_LAYOUT_DOCKNIZE_HPP -#define EKG_LAYOUT_DOCKNIZE_HPP +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_LAYOUR_DOCKNIZE_HPP +#define EKG_LAYOUR_DOCKNIZE_HPP -#include "ekg/ui/abstract.hpp" -#include "ekg/math/geometry.hpp" -#include "ekg/math/floating_point.hpp" +#include "ekg/ui/property.hpp" namespace ekg::layout { /** - * Returns the dimensional extent based in count and the offset (space between rects). + * Returns the dimensional extent based on count and the offset (space between rects). * * The pixel imperfect issue was solved here... * For a long time I did not know what was going on with the pixels, @@ -83,8 +104,14 @@ namespace ekg::layout { } class mask { + public: + struct component_t { + public: + ekg::rect_t *p_rect {}; + ekg::flags_t dock {}; + }; protected: - std::vector rect_descriptor_list {}; + std::vector components {}; float respective_all {}; float respective_center {}; ekg::flags_t axis {}; @@ -98,7 +125,7 @@ namespace ekg::layout { ); void insert( - ekg::rect_descriptor_t rect_descriptor + const ekg::layout::mask::component_t &rect_descriptor ); void docknize(); @@ -110,7 +137,11 @@ namespace ekg::layout { * Note: Recursive. **/ void docknize_widget( - ekg::ui::abstract *p_parent_widget + ekg::property_t &property + ); + + float get_widget_height_by_children( + ekg::property_t &property ); } diff --git a/include/ekg/layout/extentnize.hpp b/include/ekg/layout/extentnize.hpp index a3dc3208..fa0a548e 100644 --- a/include/ekg/layout/extentnize.hpp +++ b/include/ekg/layout/extentnize.hpp @@ -1,15 +1,38 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef EKG_LAYOUT_EXTENTNIZE_HPP #define EKG_LAYOUT_EXTENTNIZE_HPP -#include "ekg/ui/abstract.hpp" +#include "docknize.hpp" namespace ekg::layout { struct extent_t { public: static ekg::layout::extent_t v_widget; static ekg::layout::extent_t h_widget; - static ekg::layout::extent_t v_rect_descriptor; - static ekg::layout::extent_t h_rect_descriptor; + static ekg::layout::extent_t v_mask; + static ekg::layout::extent_t h_mask; public: int32_t end_index {}; int32_t begin_index {}; @@ -46,8 +69,8 @@ namespace ekg::layout { * Obtain the remain extent size, from the latest rect descriptor index `in` * and return the new count `out`. **/ - void extentnize_rect_descriptor( - std::vector &rect_descriptor_list, + void extentnize_mask( + std::vector &components, ekg::vec3_t offset, ekg::flags_t flag_ok, ekg::flags_t flag_stop, @@ -61,7 +84,7 @@ namespace ekg::layout { * and return the new count `out`. **/ void extentnize_widget( - ekg::ui::abstract *p_widget, + ekg::property_t &property, ekg::flags_t flag_ok, ekg::flags_t flag_stop, ekg::flags_t flag_axis, diff --git a/include/ekg/layout/scale.hpp b/include/ekg/layout/scale.hpp deleted file mode 100644 index a8c39389..00000000 --- a/include/ekg/layout/scale.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef EKG_LAYOUT_SCALE_HPP -#define EKG_LAYOUT_SCALE_HPP - -namespace ekg::layout { - void scale_calculate(); -}; - -#endif diff --git a/include/ekg/layout/scalenize.hpp b/include/ekg/layout/scalenize.hpp new file mode 100644 index 00000000..2e76e692 --- /dev/null +++ b/include/ekg/layout/scalenize.hpp @@ -0,0 +1,31 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_LAYOUT_SCALENIZE_HPP +#define EKG_LAYOUT_SCALENIZE_HPP + +namespace ekg::layout { + void scalenize(); +} + +#endif diff --git a/include/ekg/math/floating_point.hpp b/include/ekg/math/floating_point.hpp index b94430ef..fddebb48 100644 --- a/include/ekg/math/floating_point.hpp +++ b/include/ekg/math/floating_point.hpp @@ -1,3 +1,26 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef EKG_MATH_FLOATING_POINT_HPP #define EKG_MATH_FLOATING_POINT_HPP diff --git a/include/ekg/math/geometry.hpp b/include/ekg/math/geometry.hpp index af952838..f38a3cb0 100644 --- a/include/ekg/math/geometry.hpp +++ b/include/ekg/math/geometry.hpp @@ -1,3 +1,26 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef EKG_MATH_GEOMETRY_HPP #define EKG_MATH_GEOMETRY_HPP @@ -353,6 +376,21 @@ namespace ekg { ); } + /** + * @TODO: implement min/max for `ekg::rect_t`, `ekg::vec4_t`, etc + * note: do not implement clamp for these cases because clamping rect/vec is stupid + **/ + + template + constexpr t min(t a, t b) { + return a < b ? a : b; + } + + template + constexpr t max(t a, t b) { + return a > b ? a : b; + } + template constexpr t clamp_min(t a, t b) { return a < b ? b : a; @@ -387,12 +425,6 @@ namespace ekg { return x + (y - x) * delta; } - struct rect_descriptor_t { - public: - ekg::rect_t *p_rect {}; - ekg::flags_t flags {}; - }; - struct aligned_t { public: float w {}; @@ -586,6 +618,9 @@ namespace ekg { ) / 255.0f; } + template + using rgba_t =ekg::vec4_t; + void ortho( float *p_mat4x4, float left, diff --git a/include/ekg/os/ekg_glfw.hpp b/include/ekg/os/ekg_glfw.hpp deleted file mode 100644 index dd1c5511..00000000 --- a/include/ekg/os/ekg_glfw.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef EKG_OS_GLFW_HPP -#define EKG_OS_GLFW_HPP - -#include "platform.hpp" - -#if defined(__ANDROID__) -// there is no glfw... for android I guess idk i do not give a fuck -#else -#include -#endif - -namespace ekg { - class glfw : public ekg::os::platform { - protected: - GLFWcursor *loaded_system_cursor_list[12] {}; - GLFWwindow *p_glfw_win {}; - public: - explicit glfw( - GLFWwindow *p_glfw_win, - ekg::flags_t modes = static_cast(0) - ); - public: - void init() override; - void quit() override; - void update_display_size() override; - void update() override; - void get_key_name(ekg::io::input_key_t &key, std::string &name) override; - void get_special_key(ekg::io::input_key_t &key, ekg::special_key_type &special_key) override; - const char *get_clipboard_text() override; - void set_clipboard_text(const char *p_text) override; - bool has_clipboard_text() override; - }; - - void glfw_window_size_callback( - int32_t w, - int32_t h - ); - - void glfw_char_callback( - uint32_t codepoint - ); - - void glfw_key_callback( - int32_t key, - int32_t scancode, - int32_t action, - int32_t mods - ); - - void glfw_cursor_pos_callback( - double x, - double y - ); - - void glfw_scroll_callback( - double dx, - double dy - ); - - void glfw_mouse_button_callback( - int32_t button, - int32_t action, - int32_t mods - ); -} - -#endif diff --git a/include/ekg/os/platform.hpp b/include/ekg/platform/base.hpp similarity index 66% rename from include/ekg/os/platform.hpp rename to include/ekg/platform/base.hpp index 3a2a467a..e72518e8 100644 --- a/include/ekg/os/platform.hpp +++ b/include/ekg/platform/base.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,30 +21,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_PLATFORM_HPP +#define EKG_PLATFORM_HPP -#ifndef EKG_OS_PLATFORM_HPP -#define EKG_OS_PLATFORM_HPP +#include "ekg/io/event.hpp" -#include "ekg/io/input.hpp" -#include "ekg/math/geometry.hpp" - -namespace ekg::os { - class platform { +namespace ekg::platform { + class base { public: ekg::rect_t display_size {}; - ekg::system_cursor_type system_cursor {}; - ekg::io::serialized_input_event_t serialized_input_event {}; + ekg::system_cursor system_cursor {}; + ekg::io::event_t event {}; ekg::flags_t modes {}; public: - virtual void init() {} - virtual void quit() {} - virtual void update_display_size() {} - virtual void update() {} - virtual void get_key_name(ekg::io::input_key_t &key, std::string &name) {} - virtual void get_special_key(ekg::io::input_key_t &key, ekg::special_key_type &special_key) {} + virtual void init() {}; + virtual void quit() {}; + virtual void update_display_size() {}; + virtual void update() {}; + virtual void get_key_name(ekg::input_key_t &key, std::string &name) {}; + virtual void get_special_key(ekg::input_key_t &key, ekg::special_key &espcial_key) {}; virtual const char *get_clipboard_text() { return nullptr; }; virtual void set_clipboard_text(const char *p_text) {}; - virtual bool has_clipboard_text() { return false; } + virtual bool has_clipboard_text() { return false; }; }; } diff --git a/include/ekg/os/ekg_sdl.hpp b/include/ekg/platform/sdl/sdl2.hpp similarity index 74% rename from include/ekg/os/ekg_sdl.hpp rename to include/ekg/platform/sdl/sdl2.hpp index f15085bc..bc63ad34 100644 --- a/include/ekg/os/ekg_sdl.hpp +++ b/include/ekg/platform/sdl/sdl2.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,25 +21,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - -#ifndef EKG_OS_SDL_HPP -#define EKG_OS_SDL_HPP +#ifndef EKG_PLATFORM_SDL_SDL2_HPP +#define EKG_PLATFORM_SDL_SDL2_HPP #if defined(__ANDROID__) -#include "SDL.h" + #include "SDL.h" #else -#include + #include #endif -#include "ekg/os/platform.hpp" +#include "ekg/platform/base.hpp" +#include namespace ekg { - class sdl : public ekg::os::platform { + class sdl2 : public ekg::platform::base { protected: - SDL_Cursor *loaded_system_cursor_list[12] {}; + std::array loaded_system_cursors {}; SDL_Window *p_sdl_win {}; public: - explicit sdl( + explicit sdl2( SDL_Window *p_sdl_win, ekg::flags_t modes = static_cast(0) ); @@ -48,14 +48,14 @@ namespace ekg { void quit() override; void update_display_size() override; void update() override; - void get_key_name(ekg::io::input_key_t &key, std::string &name) override; - void get_special_key(ekg::io::input_key_t &key, ekg::special_key_type &special_key) override; + void get_key_name(ekg::input_key_t &key, std::string &name) override; + void get_special_key(ekg::input_key_t &key, ekg::special_key &special_key) override; const char *get_clipboard_text() override; void set_clipboard_text(const char *p_text) override; bool has_clipboard_text() override; }; - void sdl_poll_event(SDL_Event &sdl_event); + void sdl2_poll_event(SDL_Event &sdl_event); } #endif diff --git a/include/ekg/service/theme.hpp b/include/ekg/service/theme.hpp deleted file mode 100644 index df5dc5f4..00000000 --- a/include/ekg/service/theme.hpp +++ /dev/null @@ -1,86 +0,0 @@ - -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_SERVICE_THEME_HPP -#define EKG_SERVICE_THEME_HPP - -#include -#include -#include - -#include "ekg/io/design.hpp" - -namespace ekg::service { - class theme { - protected: - std::map theme_map {}; - ekg::theme_t current_theme {}; - public: - /** - * Initialize default themes (dark, light, pinky etc) and update global theme scheme. - **/ - void init(); - - void quit(); - - /** - * Returns all mapped schemes from theme service. - * Note: Use property register/deregister methods; may be unsafe. - **/ - std::map &get_theme_map(); - - /** - * Set the current theme global. - * Note: You must set a registered theme. - **/ - ekg::flags_t set_current_theme(std::string_view name); - - /** - * Returns the current theme scheme global loaded. - **/ - ekg::theme_t &get_current_theme(); - - /** - * Dynamic registry one theme scheme on memory, you must not repeat themes name. - * Note: May you want save theme scheme in a file, use `ekg::service::theme::save` - * method to do that. - **/ - void add(ekg::theme_t theme); - - /** - * Local save one theme scheme to a file. - * You need to register a theme before use this. - **/ - void save(std::string_view name, std::string_view path); - - /** - * Read one theme scheme from a file and load it to memory; may replacing one - * if already exists. - **/ - void read(std::string_view path, ekg::theme_t *p_theme); - }; -} - -#endif diff --git a/include/ekg/ui/abstract.hpp b/include/ekg/ui/abstract.hpp index 0d09c587..0433b07b 100644 --- a/include/ekg/ui/abstract.hpp +++ b/include/ekg/ui/abstract.hpp @@ -21,49 +21,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #ifndef EKG_UI_ABSTRACT_HPP #define EKG_UI_ABSTRACT_HPP -#include "ekg/ui/properties.hpp" #include "ekg/math/geometry.hpp" -#include "ekg/io/input.hpp" -#include "ekg/io/task.hpp" - -#define EKG_ASSERT_VALUE(value) if (value.was_changed && !(value.was_changed = false)) ekg::p_core->dispatch_widget_op(this, ekg::io::operation::reload); +#include "ekg/ui/property.hpp" namespace ekg::ui { - class abstract { - public: - ekg::rect_t _blank_parent_rect {}; - ekg::vec4_t _blank_scroll_vec {}; - ekg::rect_t _blank_descriptor_rect {}; - public: - ekg::properties_t properties {}; - ekg::ui::states_t states {}; + ekg::rect_t &get_abs_rect( + ekg::property_t &property, + ekg::rect_t &descriptor_rect + ); - ekg::rect_t scissor {}; - ekg::vec2_t min_size {}; + void pre_event( + ekg::property_t &property, + ekg::rect_t &descriptor_rect, + bool is_top_level + ); - ekg::rect_t *p_descriptor_rect {}; - ekg::vec4_t *p_scroll_vec {}; - ekg::rect_t *p_parent_rect {}; - ekg::rect_t *p_parent_scissor_rect {}; - public: - ekg::rect_t &get_abs_rect(); - public: - virtual void on_create(); - virtual void on_destroy(); - virtual void on_reload(); - virtual void on_event(ekg::io::stage stage); - virtual void on_update(); - virtual void on_draw(); - public: - template - operator t() { - return *static_cast(this->properties.p_descriptor); - } - }; + void post_event( + ekg::property_t &property + ); } #endif diff --git a/include/ekg/ui/button/button.hpp b/include/ekg/ui/button/button.hpp index e60b06ce..895ecef9 100644 --- a/include/ekg/ui/button/button.hpp +++ b/include/ekg/ui/button/button.hpp @@ -1,40 +1,77 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef EKG_UI_BUTTON_HPP #define EKG_UI_BUTTON_HPP +#include "ekg/io/descriptor.hpp" +#include "ekg/io/font.hpp" #include "ekg/math/geometry.hpp" -#include "ekg/io/typography.hpp" -#include "ekg/io/gpu.hpp" -#include "ekg/ui/types.hpp" -#include "ekg/io/task.hpp" namespace ekg { - struct button_theme_t { + struct button_color_scheme_t { public: - ekg::vec4_t background {}; - ekg::vec4_t text {}; - ekg::vec4_t outline {}; - ekg::vec4_t active {}; - ekg::vec4_t active_outline {}; - ekg::vec4_t highlight {}; + ekg::rgba_t text_foreground {}; - /** - * [0] ekg::button_t; - **/ - ekg::layer_t<1> layers {}; + ekg::rgba_t background {}; + ekg::rgba_t outline {}; + ekg::rgba_t highlight {}; + + ekg::rgba_t box_background {}; + ekg::rgba_t box_outline {}; + ekg::rgba_t box_highlight {}; + ekg::rgba_t box_active {}; }; struct button_t { + public: + struct check_t { + public: + struct widget_t { + public: + ekg::rect_t rect_text {}; + ekg::rect_t rect_box {}; + }; + public: + ekg::value text {}; + ekg::font font_size {ekg::font::medium}; + bool is_check_box {}; + ekg::flags_t dock {}; + ekg::button_t::check_t::widget_t widget {}; + }; + + static ekg::button_t not_found; + static constexpr ekg::type type {ekg::type::button}; + public: + ekg::at_t property_at {}; public: std::string tag {}; - ekg::flags_t dock {ekg::dock::left}; - ekg::value text {}; - ekg::flags_t text_dock {ekg::dock::left}; - ekg::value value {}; - ekg::font text_font_size {ekg::font::normal}; + ekg::flags_t dock {}; ekg::rect_t rect {}; - ekg::type type {ekg::type::button}; - ekg::actions actions {}; - ekg::button_theme_t theme {}; + std::vector checks {}; + ekg::button_color_scheme_t color_scheme {}; + public: + ekg_descriptor(ekg::button_t); }; } diff --git a/include/ekg/ui/menu/ui_menu_widget.hpp b/include/ekg/ui/button/widget.hpp similarity index 62% rename from include/ekg/ui/menu/ui_menu_widget.hpp rename to include/ekg/ui/button/widget.hpp index f185e8be..22a47155 100644 --- a/include/ekg/ui/menu/ui_menu_widget.hpp +++ b/include/ekg/ui/button/widget.hpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,15 +21,43 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef EKG_UI_BUTTON_WIDGET_HPP +#define EKG_UI_BUTTON_WIDGET_HPP -#ifndef EKG_UI_MENU_WIDGET_H -#define EKG_UI_MENU_WIDGET_H - -#include "ekg/ui/abstract/ui_abstract_widget.hpp" +#include "button.hpp" +#include "ekg/ui/property.hpp" +#include "ekg/io/event.hpp" namespace ekg::ui { - class menu_widget : public ekg::ui::abstract_widget { - }; + void reload( + ekg::property_t &property, + ekg::button_t &button + ); + + void event( + ekg::property_t &property, + ekg::button_t &button, + const ekg::io::stage &stage + ); + + void high_frequency( + ekg::property_t &property, + ekg::button_t &button + ); + + void pass( + ekg::property_t &property, + ekg::button_t &button + ); + + void buffering( + ekg::property_t &property, + ekg::button_t &button + ); + + void unmap( + ekg::button_t &button + ); } -#endif \ No newline at end of file +#endif diff --git a/include/ekg/ui/checkbox/checkbox.hpp b/include/ekg/ui/checkbox/checkbox.hpp deleted file mode 100644 index 4555a799..00000000 --- a/include/ekg/ui/checkbox/checkbox.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef EKG_UI_CHECKBOX_HPP -#define EKG_UI_CHECKBOX_HPP - -#include "ekg/math/geometry.hpp" -#include "ekg/io/gpu.hpp" -#include "ekg/io/typography.hpp" -#include "ekg/ui/types.hpp" -#include "ekg/io/task.hpp" - -namespace ekg { - struct checkbox_theme_t { - public: - ekg::vec4_t background {}; - ekg::vec4_t text {}; - ekg::vec4_t outline {}; - ekg::vec4_t active {}; - ekg::vec4_t highlight {}; - ekg::vec4_t box_background {}; - ekg::vec4_t box_outline {}; - ekg::vec4_t box_highlight {}; - ekg::vec4_t box_active {}; - - /** - * [0] ekg::checkbox_t; - * [1] ekg::checkbox_t::box; - **/ - ekg::layer_t<2> layers {}; - }; - - struct checkbox_t { - public: - constexpr static ekg::flags_t box {1}; - public: - std::string tag {}; - ekg::flags_t dock {}; - ekg::value text {}; - ekg::flags_t text_dock {ekg::dock::left}; - ekg::value value {}; - ekg::font text_font_size {ekg::font::normal}; - ekg::flags_t box_dock {ekg::dock::left}; - ekg::rect_t rect {}; - ekg::type type {ekg::type::checkbox}; - ekg::actions actions {}; - ekg::checkbox_theme_t theme {}; - }; -} - -#endif diff --git a/include/ekg/ui/checkbox/checkbox_widget.hpp b/include/ekg/ui/checkbox/checkbox_widget.hpp deleted file mode 100644 index d0e84d17..00000000 --- a/include/ekg/ui/checkbox/checkbox_widget.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EKG_UI_CHECKBOX_WIDGET_HPP -#define EKG_UI_CHECKBOX_WIDGET_HPP - -#include "ekg/ui/abstract.hpp" -#include "ekg/ui/checkbox/checkbox.hpp" - -namespace ekg::ui { - class checkbox : public ekg::ui::abstract { - public: - ekg::checkbox_t descriptor {}; - public: - ekg::rect_t text_rect {}; - ekg::rect_t box_rect {}; - public: - void on_reload() override; - void on_event(ekg::io::stage stage) override; - void on_draw() override; - }; -} - -#endif diff --git a/include/ekg/ui/combobox/combobox.hpp b/include/ekg/ui/combobox/combobox.hpp deleted file mode 100644 index bec1cac1..00000000 --- a/include/ekg/ui/combobox/combobox.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef EKG_UI_COMBOBOX_HPP -#define EKG_UI_COMBOBOX_HPP - -#include "ekg/math/geometry.hpp" - -namespace ekg { - struct combobox_theme_t { - public: - }; - - struct combobox_t { - public: - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/frame/frame.hpp b/include/ekg/ui/frame/frame.hpp index d43b6aa9..e986ba0c 100644 --- a/include/ekg/ui/frame/frame.hpp +++ b/include/ekg/ui/frame/frame.hpp @@ -1,43 +1,73 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef EKG_UI_FRAME_HPP #define EKG_UI_FRAME_HPP +#include "ekg/io/descriptor.hpp" #include "ekg/math/geometry.hpp" -#include "ekg/ui/properties.hpp" -#include "ekg/io/gpu.hpp" -#include "ekg/io/task.hpp" namespace ekg { - struct frame_theme_t { + struct frame_color_scheme_t { public: - ekg::vec4_t background {}; - ekg::vec4_t border {}; - ekg::vec4_t outline {}; - ekg::pixel_thickness_t actions_margin_pixel_thickness {}; - - /** - * [0] ekg::frame_t; - **/ - ekg::layer_t<1> layers {}; + ekg::pixel_thickness_t actions_margin_pixel_thickness {5}; + ekg::rgba_t background {}; + ekg::rgba_t highlight {}; + ekg::rgba_t outline {}; + ekg::rgba_t active {}; + ekg::rgba_t focused_background {}; + ekg::rgba_t focused_outline {}; + ekg::rgba_t warning_outline {}; }; struct frame_t { + public: + struct widget_t { + public: + ekg::flags_t target_dock_drag {}; + ekg::flags_t target_dock_resize {}; + ekg::docker_t docker_drag {}; + ekg::docker_t docker_resize {}; + ekg::rect_t rect_delta {}; + ekg::rect_t rect_cache {}; + }; + public: + static ekg::frame_t not_found; + static constexpr ekg::type type {ekg::type::frame}; + public: + ekg::at_t top_level_at {}; + ekg::at_t property_at {}; public: std::string tag {}; + ekg::dock dock {ekg::dock::none}; ekg::rect_t rect {}; - ekg::flags_t dock {}; - ekg::flags_t drag_dock {}; - ekg::flags_t resize_dock {}; - ekg::type type {ekg::type::frame}; - ekg::top_level_t top_level {}; - ekg::level level {ekg::level::bottom}; // wtfffffffffffffffff - ekg::actions actions {}; - ekg::frame_theme_t theme {}; - public: - ekg::properties_t *p_properties {}; + ekg::flags_t drag {}; + ekg::flags_t resize {}; + bool set_top_level {}; + ekg::frame_color_scheme_t color_scheme {}; + ekg::frame_t::widget_t widget {}; public: - operator ekg::top_level_t() { - return this->p_properties; - } + ekg_descriptor(ekg::frame_t); }; } diff --git a/include/ekg/ui/frame/frame_widget.hpp b/include/ekg/ui/frame/frame_widget.hpp deleted file mode 100644 index 4a11e8a4..00000000 --- a/include/ekg/ui/frame/frame_widget.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef EKG_UI_FRAME_WIDGET_HPP -#define EKG_UI_FRAME_WIDGET_HPP - -#include "frame.hpp" -#include "ekg/ui/abstract.hpp" - -namespace ekg::ui { - class frame : public ekg::ui::abstract { - public: - ekg::frame_t descriptor {}; - public: - ekg::flags_t target_dock_drag {}; - ekg::flags_t target_dock_resize {}; - - ekg::docker_t docker_drag {}; - ekg::docker_t docker_resize {}; - - ekg::rect_t rect_delta {}; - ekg::rect_t rect_cache {}; - public: - void on_create() override; - void on_destroy() override; - void on_reload() override; - void on_event(ekg::io::stage stage) override; - void on_update() override; - void on_draw() override; - }; -} - -#endif diff --git a/include/ekg/ui/frame/widget.hpp b/include/ekg/ui/frame/widget.hpp new file mode 100644 index 00000000..d8990510 --- /dev/null +++ b/include/ekg/ui/frame/widget.hpp @@ -0,0 +1,63 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_UI_FRAME_WIDGET_HPP +#define EKG_UI_FRAME_WIDGET_HPP + +#include "ekg/ui/property.hpp" +#include "ekg/ui/frame/frame.hpp" +#include "ekg/io/event.hpp" + +namespace ekg::ui { + void reload( + ekg::property_t &property, + ekg::frame_t &frame + ); + + void event( + ekg::property_t &property, + ekg::frame_t &frame, + const ekg::io::stage &stage + ); + + void high_frequency( + ekg::property_t &property, + ekg::frame_t &frame + ); + + void pass( + ekg::property_t &property, + ekg::frame_t &frame + ); + + void buffering( + ekg::property_t &property, + ekg::frame_t &frame + ); + + void unmap( + ekg::frame_t &frame + ); +} + +#endif diff --git a/include/ekg/ui/label/label.hpp b/include/ekg/ui/label/label.hpp deleted file mode 100644 index fdcc6903..00000000 --- a/include/ekg/ui/label/label.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef EKG_UI_LABEL_HPP -#define EKG_UI_LABEL_HPP - -#include "ekg/math/geometry.hpp" -#include "ekg/io/typography.hpp" -#include "ekg/io/gpu.hpp" -#include "ekg/ui/types.hpp" -#include "ekg/io/task.hpp" - -namespace ekg { - struct label_theme_t { - public: - ekg::vec4_t text {}; - ekg::vec4_t outline {}; - ekg::vec4_t background {}; - - /** - * [0] ekg::label_t; - **/ - ekg::layer_t<1> layers {}; - }; - - struct label_t { - public: - std::string tag {}; - ekg::flags_t dock {}; - ekg::value text {}; - ekg::flags_t text_dock {ekg::dock::left}; - ekg::font text_font_size {ekg::font::normal}; - ekg::rect_t rect {}; - ekg::type type {ekg::type::label}; - ekg::actions actions {}; - ekg::label_theme_t theme {}; - }; -} - -#endif diff --git a/include/ekg/ui/label/label_widget.hpp b/include/ekg/ui/label/label_widget.hpp deleted file mode 100644 index 3afcf048..00000000 --- a/include/ekg/ui/label/label_widget.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef EKG_UI_LABEL_WIDGET_HPP -#define EKG_UI_LABEL_WIDGET_HPP - -#include "ekg/ui/abstract.hpp" -#include "ekg/ui/label/label.hpp" - -namespace ekg::ui { - class label : public ekg::ui::abstract { - public: - ekg::properties_t properties {}; - ekg::label_t descriptor {}; - public: - ekg::rect_t text_rect {}; - public: - void on_reload() override; - void on_draw() override; - }; -} - -#endif diff --git a/include/ekg/ui/listbox/listbox.hpp b/include/ekg/ui/listbox/listbox.hpp deleted file mode 100644 index f81bb2ea..00000000 --- a/include/ekg/ui/listbox/listbox.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef EKG_UI_LISTBOX_HPP -#define EKG_UI_LISTBOX_HPP - -#include "ekg/math/geometry.hpp" - -namespace ekg { - struct listbox_theme_t { - public: - ekg::vec4_t header_background {}; - ekg::vec4_t header_highlight_outline {}; - ekg::vec4_t header_highlight {}; - ekg::vec4_t header_outline {}; - ekg::vec4_t header_string {}; - ekg::vec4_t item_background {}; - ekg::vec4_t item_highlight_outline {}; - ekg::vec4_t item_highlight {}; - ekg::vec4_t item_focused {}; - ekg::vec4_t item_focused_outline {}; - ekg::vec4_t item_string {}; - ekg::vec4_t item_outline {}; - ekg::vec4_t outline {}; - ekg::vec4_t background {}; - ekg::vec4_t line_separator {}; - ekg::vec4_t drag_background {}; - ekg::vec4_t drag_outline {}; - - float subitem_offset_space {4.0f}; - }; - - struct listbox_t { - public: - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/listbox/ui_listbox.hpp b/include/ekg/ui/listbox/ui_listbox.hpp deleted file mode 100644 index 60d6b25a..00000000 --- a/include/ekg/ui/listbox/ui_listbox.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_LISTBOX_H -#define EKG_UI_LISTBOX_H - -#include "ekg/ui/abstract/ui_abstract.hpp" -#include "ekg/util/io.hpp" -#include "ekg/ui/display.hpp" -#include "ekg/ui/abstract/ui_abstract_widget.hpp" - -namespace ekg::ui { - class listbox : public ekg::ui::abstract, public ekg::value_t { - protected: - ekg::font item_font_size {}; - ekg::font column_header_font_size {}; - ekg::mode current_mode {}; - - ekg::flags column_header_dock_flags {}; - int32_t column_header_scaled_height {1}; - int32_t item_scaled_height {1}; - public: - ekg::ui::listbox *set_item_font_size(ekg::font font_size); - - ekg::font get_item_font_size(); - - ekg::ui::listbox *set_column_header_font_size(ekg::font font_size); - - ekg::font get_column_header_font_size(); - - ekg::ui::listbox *set_width(float w); - - float get_width(); - - ekg::ui::listbox *set_scaled_height(int32_t h); - - int32_t get_scaled_height(); - - float get_height(); - - ekg::ui::listbox *set_place(ekg::flags dock); - - ekg::ui::listbox *set_mode(ekg::mode mode); - - ekg::mode get_mode(); - - ekg::ui::listbox *set_column_header_scaled_height(int32_t h); - - int32_t get_column_header_scaled_height(); - - ekg::ui::listbox *set_item_scaled_height(int32_t h); - - int32_t get_item_scaled_height(); - - ekg::ui::listbox *set_column_header_align(ekg::flags dock); - - ekg::flags get_column_header_align(); - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/listbox/ui_listbox_widget.hpp b/include/ekg/ui/listbox/ui_listbox_widget.hpp deleted file mode 100644 index 0116d5e6..00000000 --- a/include/ekg/ui/listbox/ui_listbox_widget.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_LISTBOX_WIDGET_H -#define EKG_UI_LISTBOX_WIDGET_H - -#include "ekg/ui/abstract/ui_abstract_widget.hpp" -#include "ekg/util/io.hpp" -#include "ekg/ui/scrollbar/ui_scrollbar_embedded_widget.hpp" -#include "ekg/ui/listbox/ui_listbox.hpp" -#include "ekg/service/theme.hpp" -#include "ekg/draw/font_renderer.hpp" - -namespace ekg::ui { - class listbox_widget : public ekg::ui::abstract_widget { - public: - enum op_mode { - cached_update, - recursive_tree_update, - }; - public: - void render_item( - ekg::item &item_header, - ekg::placement &placement_header, - ekg::rect widget_absolute_rect_scissor, - ekg::rect scrollable_rect, - ekg::rect content_scissor_bounding, - float bottom_place, - float normalized_horizontal_scroll, - bool is_header_targeted, - bool is_column_header_top, - bool is_multicolumn, - ekg::draw::font_renderer &f_renderer - ); - public: - ekg::ui::scrollbar_embedded_widget embedded_scroll {}; - ekg::item item_rendering_cache {}; - ekg::rect rect_content_abs {}; - ekg::rect rect_content_place {}; - ekg::rect rect_header_delta {}; - ekg::rect rect_header_target {}; - ekg::rect rect_targeting_header {}; - ekg::rect rect_current_dragging_targeted_header {}; - ekg::rect rect_original_dragging_targeted_header {}; - - float column_header_height {}; - float header_relative_x {}; - - bool was_hovered {}; - bool was_selected {}; - bool must_update_items {}; - bool must_force_tree_refresh {}; - - int32_t targeting_header_to_resize {}; - int32_t targeting_header_to_drag {}; - int32_t target_resizing {-1}; - int32_t target_dragging {-1}; - int32_t latest_target_dragging {-1}; - int32_t target_drag_set_index {-1}; - public: - void on_create() override; - - void on_reload() override; - - void on_update() override; - - void on_post_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_pre_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_draw_refresh() override; - }; -} - -namespace ekg::ui { - void listbox_extent_align(ekg::rect &rect); - - void listbox_template_reload( - ekg::item &rendering_cache, - ekg::item &parent, - ekg::rect &ui_rect, - ekg::rect &header_rect, - ekg::font &item_font, - ekg::rect &relative_rect, - int32_t item_scaled_height, - uint64_t header_index, - uint64_t &arbitrary_index_pos, - uint64_t &rendering_cache_arbitrary_index_pos, - bool &opened, - ekg::mode mode, - bool *p_semaphore - ); - - void listbox_template_on_event( - ekg::os::io_event_serial &io_event_serial, - uint64_t &arbitrary_index_pos, - ekg::item &rendering_cache, - bool motion, - bool released, - bool pressed_select_many, - bool pressed_select, - bool pressed_open, - bool &was_selected, - bool was_hovered, - ekg::item &parent, - ekg::vec2 &ui_pos, - ekg::rect &ui_rect, - ekg::rect &relative_rect, - ekg::mode mode - ); -} - -#endif diff --git a/include/ekg/ui/menu/menu.hpp b/include/ekg/ui/menu/menu.hpp deleted file mode 100644 index 709419b5..00000000 --- a/include/ekg/ui/menu/menu.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef EKG_UI_MENU_HPP -#define EKG_UI_MENU_HPP - -#include "ekg/math/geometry.hpp" - -namespace ekg { - struct menu_theme_t { - public: - }; - - struct menu_t { - public: - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/popup/popup.hpp b/include/ekg/ui/popup/popup.hpp deleted file mode 100644 index cd6241f5..00000000 --- a/include/ekg/ui/popup/popup.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EKG_UI_POPUP_HPP -#define EKG_UI_POPUP_HPP - -#include "ekg/math/geometry.hpp" - -namespace ekg { - struct popup_theme_t { - public: - ekg::vec4_t background {}; - ekg::vec4_t text {}; - ekg::vec4_t outline {}; - ekg::vec4_t highlight {}; - ekg::vec4_t separator {}; - int64_t drop_animation_delay {}; - }; - - struct popup_t { - public: - }; -} - -#endif diff --git a/include/ekg/ui/popup/ui_popup.hpp b/include/ekg/ui/popup/ui_popup.hpp deleted file mode 100644 index c0b4ac16..00000000 --- a/include/ekg/ui/popup/ui_popup.hpp +++ /dev/null @@ -1,82 +0,0 @@ - -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_POPUP_H -#define EKG_UI_POPUP_H - -#include "ekg/ui/abstract/ui_abstract.hpp" -#include "ekg/util/io.hpp" -#include "ekg/ui/display.hpp" - -namespace ekg::ui { - class popup : public ekg::ui::abstract { - protected: - std::vector item_list {}; - - ekg::flags text_flags {}; - int32_t scaled_height {}, token_id {}; - ekg::font font_size; - public: - ekg::ui::popup *insert(const std::vector &item_list); - - ekg::ui::popup *insert(std::string_view item_name); - - ekg::ui::popup *link(std::string_view item_name, ekg::ui::popup *p_popup); - - ekg::ui::popup *erase(std::string_view item_name); - - std::string &emplace_back(); - - int64_t contains(std::string_view item_name); - - ekg::ui::item &get(uint64_t index); - - std::vector &get_item_list(); - - ekg::ui::popup *set_pos(float x, float y); - - ekg::vec2 get_pos(); - - ekg::ui::popup *set_text_align(ekg::flags dock); - - ekg::flags get_text_align(); - - ekg::ui::popup *set_width(float w); - - float get_width(); - - ekg::ui::popup *set_scaled_height(int32_t h); - - int32_t get_scaled_height(); - - float get_height(); - - ekg::ui::popup *set_font_size(ekg::font font); - - ekg::font get_font_size(); - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/popup/ui_popup_widget.hpp b/include/ekg/ui/popup/ui_popup_widget.hpp deleted file mode 100644 index 86537e69..00000000 --- a/include/ekg/ui/popup/ui_popup_widget.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_POPUP_WIDGET_H -#define EKG_UI_POPUP_WIDGET_H - -#include "ekg/ui/abstract/ui_abstract_widget.hpp" - -// @TODO State for components/elements disabled -namespace ekg::ui { - class popup_widget : public ekg::ui::abstract_widget { - public: - float scissor_opened_height {}; - float separator_offset {}; - - uint64_t elapsed_animation_ticks {}; - bool parent_id_popup_opened {false}; - - int32_t hovered_element {}; - int32_t popup_opened {-1}; - int32_t top_level_popup {}; - public: - bool is_hovering_any_popup(int32_t top_level); - - void unset_visible_all_sub_popup(); - - void get_popup_path(std::string &path); - public: - void on_reload() override; - - void on_pre_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_update() override; - - void on_draw_refresh() override; - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/property.hpp b/include/ekg/ui/property.hpp new file mode 100644 index 00000000..28f02667 --- /dev/null +++ b/include/ekg/ui/property.hpp @@ -0,0 +1,85 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EKG_UI_PROPERTY_HPP +#define EKG_UI_PROPERTY_HPP + +#include "ekg/io/descriptor.hpp" +#include "ekg/math/geometry.hpp" + +namespace ekg { + struct property_t { + public: + struct operation_t { + public: + bool should_reload {}; + bool should_docknize {}; + bool should_enable_high_frequency {}; + }; + + struct scroll_t { + public: + ekg::vec4_t position {}; + ekg::vec2_t is_enabled {}; + ekg::vec2_t is_scrolling {}; + ekg::pixel_thickness_t nearest_scroll_bar_thickness {}; + }; + + struct widget_t { + public: + ekg::rect_t rect_scissor {}; + ekg::vec2_t min_size {}; + ekg::rect_t rect {}; + bool is_childnizate {}; + bool is_children_docknizable {}; + bool is_targeting_absolute_parent {}; + bool is_absolute {}; + bool is_active {}; + bool is_hovering {}; + bool is_visible {}; + bool is_enabled {}; + bool is_highlight {}; + bool is_focused {}; + bool is_warning {}; + bool is_high_frequency {}; + bool should_refresh_size {}; + bool should_buffering {}; + }; + public: + static ekg::property_t not_found; + static constexpr ekg::type type {ekg::type::property}; + public: + ekg::at_t parent_at {ekg::at_t::not_found}; + ekg::at_t abs_parent_at {ekg::at_t::not_found}; + ekg::at_t descriptor_at {ekg::at_t::not_found}; + std::vector children {}; + public: + ekg::property_t::widget_t widget {}; + ekg::property_t::scroll_t scroll {}; + ekg::property_t::operation_t operation {}; + public: + ekg_descriptor(ekg::property_t); + }; +} + +#endif diff --git a/include/ekg/ui/scrollbar/scrollbar.hpp b/include/ekg/ui/scrollbar/scrollbar.hpp deleted file mode 100644 index e5a8ba12..00000000 --- a/include/ekg/ui/scrollbar/scrollbar.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef EKG_UI_SCROLLBAR_HPP -#define EKG_UI_SCROLLBAR_HPP - -#include "ekg/math/geometry.hpp" -#include "ekg/ui/properties.hpp" -#include "ekg/io/gpu.hpp" -#include "ekg/io/task.hpp" - -namespace ekg { - struct scrollbar_theme_t { - public: - ekg::vec4_t background {}; - ekg::vec4_t outline {}; - ekg::vec4_t highlight {}; - ekg::vec4_t active {}; - ekg::pixel_thickness_t pixel_thickness {}; - float min_bar_size {}; - - /** - * [0] ekg::scrollbar_t::horizontal; - * [1] ekg::scrollbar_t::vertical; - **/ - ekg::layer_t<2> layers {}; - }; - - struct scrollbar_t { - public: - constexpr static ekg::flags_t horizontal {0}; - constexpr static ekg::flags_t vertical {1}; - public: - std::string tag {}; - std::vector *p_binded_children {}; - ekg::rect_t *p_binded_rect {}; - ekg::type type {ekg::type::scrollbar}; - ekg::actions actions {}; - ekg::scrollbar_theme_t theme {}; - }; -} - -#endif diff --git a/include/ekg/ui/scrollbar/scrollbar_widget.hpp b/include/ekg/ui/scrollbar/scrollbar_widget.hpp deleted file mode 100644 index 93c02d25..00000000 --- a/include/ekg/ui/scrollbar/scrollbar_widget.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef EKG_UI_SCROLLBAR_WIDGET_HPP -#define EKG_UI_SCROLLBAR_WIDGET_HPP - -#include "ekg/ui/abstract.hpp" -#include "scrollbar.hpp" - -namespace ekg::ui { - class scrollbar : public ekg::ui::abstract { - public: - ekg::scrollbar_t descriptor {}; - public: - ekg::rect_t scrollable_area {}; - ekg::rect_t scissor {}; - ekg::rect_t bar_horizontal {}; - ekg::rect_t bar_vertical {}; - ekg::vec4_t scroll {}; - ekg::vec2_t delta {}; - ekg::vec2_t acceleration {}; - - ekg::ui::states_t bar_horizontal_states {}; - ekg::ui::states_t bar_vertical_states {}; - public: - void clamp_scroll(); - void reset_scroll(); - void check_scroll(); - bool is_scrolling(bool state); - float get_horizontal_scroll_normalized(); - float get_vertical_scroll_normalized(); - public: - void on_create() override; - void on_reload() override; - void on_event(ekg::io::stage stage) override; - void on_update() override; - void on_draw() override; - }; -} - -#endif diff --git a/include/ekg/ui/slider/slider.hpp b/include/ekg/ui/slider/slider.hpp deleted file mode 100644 index 9eb39712..00000000 --- a/include/ekg/ui/slider/slider.hpp +++ /dev/null @@ -1,477 +0,0 @@ -#ifndef EKG_UI_SLIDER_HPP -#define EKG_UI_SLIDER_HPP - -#include "ekg/math/geometry.hpp" -#include -#include "ekg/io/memory.hpp" - -namespace ekg { - struct slider_theme_t { - public: - ekg::vec4_t background {}; - ekg::vec4_t bar_background {}; - ekg::vec4_t text {}; - ekg::vec4_t outline {}; - ekg::vec4_t active {}; - ekg::vec4_t bar {}; - ekg::vec4_t bar_outline {}; - ekg::vec4_t highlight {}; - int32_t bar_thickness {}; - int32_t target_thickness {}; - }; - - class slider_range { - protected: - ekg::number number_type {}; - - ekg::value f64 {}; - ekg::value f64_min {}; - ekg::value f64_max {}; - - ekg::value f32 {}; - ekg::value f32_min {}; - ekg::value f32_max {}; - - ekg::value u64 {}; - ekg::value u64_min {}; - ekg::value u64_max {}; - - ekg::value i64 {}; - ekg::value i64_min {}; - ekg::value i64_max {}; - - ekg::value u32 {}; - ekg::value u32_min {}; - ekg::value u32_max {}; - - ekg::value i32 {}; - ekg::value i32_min {}; - ekg::value i32_max {}; - - ekg::value u16 {}; - ekg::value u16_min {}; - ekg::value u16_max {}; - - ekg::value i16 {}; - ekg::value i16_min {}; - ekg::value i16_max {}; - - ekg::value u8 {}; - ekg::value u8_min {}; - ekg::value u8_max {}; - - ekg::value i8 {}; - ekg::value i8_min {}; - ekg::value i8_max {}; - public: - template - void range( - t value, - t min, - t max - ) { - ekg::retreive_number_type_from_variable_type( - this->number_type - ); - - switch (this->number_type) { - case ekg::number::f64: - this->f64.set_value(value); - this->f64_min.set_value(min); - this->f64_max.set_value(max); - break; - case ekg::number::f32: - this->f32.set_value(value); - this->f32_min.set_value(min); - this->f32_max.set_value(max); - break; - case ekg::number::i64: - this->i64.set_value(value); - this->i64_min.set_value(min); - this->i64_max.set_value(max); - break; - case ekg::number::u64: - this->u64.set_value(value); - this->u64_min.set_value(min); - this->u64_max.set_value(max); - break; - case ekg::number::i32: - this->i32.set_value(value); - this->i32_min.set_value(min); - this->i32_max.set_value(max); - break; - case ekg::number::u32: - this->u32.set_value(value); - this->u32_min.set_value(min); - this->u32_max.set_value(max); - break; - case ekg::number::i16: - this->i16.set_value(value); - this->i16_min.set_value(min); - this->i16_max.set_value(max); - break; - case ekg::number::u16: - this->u16.set_value(value); - this->u16_min.set_value(min); - this->u16_max.set_value(max); - break; - case ekg::number::i8: - this->i8.set_value(value); - this->i8_min.set_value(min); - this->i8_max.set_value(max); - break; - case ekg::number::u8: - this->u8.set_value(value); - this->u8_min.set_value(min); - this->u8_max.set_value(max); - break; - } - } - - template - ekg::value &value() { - switch (this->number_type) { - case ekg::number::f64: - return ekg::io::any_static_cast>(&this->f64); - case ekg::number::f32: - return ekg::io::any_static_cast>(&this->f32); - case ekg::number::i64: - return ekg::io::any_static_cast>(&this->i64); - case ekg::number::u64: - return ekg::io::any_static_cast>(&this->u64); - case ekg::number::i32: - return ekg::io::any_static_cast>(&this->i32); - case ekg::number::u32: - return ekg::io::any_static_cast>(&this->u32); - case ekg::number::i16: - return ekg::io::any_static_cast>(&this->i16); - case ekg::number::u16: - return ekg::io::any_static_cast>(&this->u16); - case ekg::number::i8: - return ekg::io::any_static_cast>(&this->i8); - default: - break; - } - - return ekg::io::any_static_cast>(&this->u8); - } - - template - ekg::value &value( - t val - ) { - switch (this->number_type) { - case ekg::number::f64: - this->f64.set_value(val); - return ekg::io::any_static_cast>(&this->f64); - case ekg::number::f32: - this->f32.set_value(val); - return ekg::io::any_static_cast>(&this->f32); - case ekg::number::i64: - this->i64.set_value(val); - return ekg::io::any_static_cast>(&this->i64); - case ekg::number::u64: - this->u64.set_value(val); - return ekg::io::any_static_cast>(&this->u64); - case ekg::number::i32: - this->i32.set_value(val); - return ekg::io::any_static_cast>(&this->i32); - case ekg::number::u32: - this->u32.set_value(val); - return ekg::io::any_static_cast>(&this->u32); - case ekg::number::i16: - this->i16.set_value(val); - return ekg::io::any_static_cast>(&this->i16); - case ekg::number::u16: - this->u16.set_value(val); - return ekg::io::any_static_cast>(&this->u16); - case ekg::number::i8: - this->i8.set_value(val); - return ekg::io::any_static_cast>(&this->i8); - default: - break; - } - - this->u8.set_value(val); - return ekg::io::any_static_cast>(&this->u8); - } - - template - ekg::value &value( - t *p_val - ) { - switch (this->number_type) { - case ekg::number::f64: - this->f64.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->f64); - case ekg::number::f32: - this->f32.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->f32); - case ekg::number::i64: - this->i64.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->i64); - case ekg::number::u64: - this->u64.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->u64); - case ekg::number::i32: - this->i32.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->i32); - case ekg::number::u32: - this->u32.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->u32); - case ekg::number::i16: - this->i16.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->i16); - case ekg::number::u16: - this->u16.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->u16); - case ekg::number::i8: - this->i8.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->i8); - default: - break; - } - - this->u8.move(ekg::io::any_static_cast(&p_val)); - return ekg::io::any_static_cast>(&this->u8); - } - - template - ekg::value &min() { - switch (this->number_type) { - case ekg::number::f64: - return ekg::io::any_static_cast>(&this->f64_min); - case ekg::number::f32: - return ekg::io::any_static_cast>(&this->f32_min); - case ekg::number::i64: - return ekg::io::any_static_cast>(&this->i64_min); - case ekg::number::u64: - return ekg::io::any_static_cast>(&this->u64_min); - case ekg::number::i32: - return ekg::io::any_static_cast>(&this->i32_min); - case ekg::number::u32: - return ekg::io::any_static_cast>(&this->u32_min); - case ekg::number::i16: - return ekg::io::any_static_cast>(&this->i16_min); - case ekg::number::u16: - return ekg::io::any_static_cast>(&this->u16_min); - case ekg::number::i8: - return ekg::io::any_static_cast>(&this->i8_min); - default: - break; - } - - return ekg::io::any_static_cast>(&this->u8_min); - } - - template - ekg::value &min( - t min - ) { - switch (this->number_type) { - case ekg::number::f64: - this->f64_min.set_value(min); - return ekg::io::any_static_cast>(&this->f64_min); - case ekg::number::f32: - this->f32_min.set_value(min); - return ekg::io::any_static_cast>(&this->f32_min); - case ekg::number::i64: - this->i64_min.set_value(min); - return ekg::io::any_static_cast>(&this->i64_min); - case ekg::number::u64: - this->u64_min.set_value(min); - return ekg::io::any_static_cast>(&this->u64_min); - case ekg::number::i32: - this->i32_min.set_value(min); - return ekg::io::any_static_cast>(&this->i32_min); - case ekg::number::u32: - this->u32_min.set_value(min); - return ekg::io::any_static_cast>(&this->u32_min); - case ekg::number::i16: - this->i16_min.set_value(min); - return ekg::io::any_static_cast>(&this->i16_min); - case ekg::number::u16: - this->u16_min.set_value(min); - return ekg::io::any_static_cast>(&this->u16_min); - case ekg::number::i8: - this->i8_min.set_value(min); - return ekg::io::any_static_cast>(&this->i8_min); - default: - break; - } - - this->u8_min.set_value(min); - return ekg::io::any_static_cast>(&this->u8_min); - } - - template - ekg::value &min( - t *p_min - ) { - switch (this->number_type) { - case ekg::number::f64: - this->f64_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->f64_min); - case ekg::number::f32: - this->f32_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->f32_min); - case ekg::number::i64: - this->i64_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->i64_min); - case ekg::number::u64: - this->u64_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->u64_min); - case ekg::number::i32: - this->i32_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->i32_min); - case ekg::number::u32: - this->u32_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->u32_min); - case ekg::number::i16: - this->i16_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->i16_min); - case ekg::number::u16: - this->u16_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->u16_min); - case ekg::number::i8: - this->i8_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->i8_min); - default: - break; - } - - this->u8_min.move(static_cast(p_min)); - return ekg::io::any_static_cast>(&this->u8_min); - } - - template - ekg::value &max() { - switch (this->number_type) { - case ekg::number::f64: - return ekg::io::any_static_cast>(&this->f64_max); - case ekg::number::f32: - return ekg::io::any_static_cast>(&this->f32_max); - case ekg::number::i64: - return ekg::io::any_static_cast>(&this->i64_max); - case ekg::number::u64: - return ekg::io::any_static_cast>(&this->u64_max); - case ekg::number::i32: - return ekg::io::any_static_cast>(&this->i32_max); - case ekg::number::u32: - return ekg::io::any_static_cast>(&this->u32_max); - case ekg::number::i16: - return ekg::io::any_static_cast>(&this->i16_max); - case ekg::number::u16: - return ekg::io::any_static_cast>(&this->u16_max); - case ekg::number::i8: - return ekg::io::any_static_cast>(&this->i8_max); - default: - break; - } - - return ekg::io::any_static_cast>(&this->u8_max); - } - - template - ekg::value &max( - t max - ) { - switch (this->number_type) { - case ekg::number::f64: - this->f64_max.set_value(max); - return ekg::io::any_static_cast>(&this->f64_max); - case ekg::number::f32: - this->f32_max.set_value(max); - return ekg::io::any_static_cast>(&this->f32_max); - case ekg::number::i64: - this->i64_max.set_value(max); - return ekg::io::any_static_cast>(&this->i64_max); - case ekg::number::u64: - this->u64_max.set_value(max); - return ekg::io::any_static_cast>(&this->u64_max); - case ekg::number::i32: - this->i32_max.set_value(max); - return ekg::io::any_static_cast>(&this->i32_max); - case ekg::number::u32: - this->u32_max.set_value(max); - return ekg::io::any_static_cast>(&this->u32_max); - case ekg::number::i16: - this->i16_max.set_value(max); - return ekg::io::any_static_cast>(&this->i16_max); - case ekg::number::u16: - this->u16_max.set_value(max); - return ekg::io::any_static_cast>(&this->u16_max); - case ekg::number::i8: - this->i8_max.set_value(max); - return ekg::io::any_static_cast>(&this->i8_max); - default: - break; - } - - this->u8_max.set_value(max); - return ekg::io::any_static_cast>(&this->u8_max); - } - - template - ekg::value &max( - t *p_max - ) { - switch (this->number_type) { - case ekg::number::f64: - this->f64_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->f64_max); - case ekg::number::f32: - this->f32_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->f32_max); - case ekg::number::i64: - this->i64_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->i64_max); - case ekg::number::u64: - this->u64_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->u64_max); - case ekg::number::i32: - this->i32_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->i32_max); - case ekg::number::u32: - this->u32_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->u32_max); - case ekg::number::i16: - this->i16_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->i16_max); - case ekg::number::u16: - this->u16_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->u16_max); - case ekg::number::i8: - this->i8_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->i8_max); - default: - break; - } - - this->u8_max.move(static_cast(p_max)); - return ekg::io::any_static_cast>(&this->u8_max); - } - }; - - struct slider_t { - public: - std::string tag {}; - ekg::flags_t dock {ekg::dock::left}; - std::vector ranges {}; - ekg::font text_font_size {ekg::font::normal}; - ekg::rect_t rect {}; - ekg::type type {ekg::type::slider}; - ekg::actions actions {}; - ekg::slider_theme_t theme {}; - public: - ekg::slider_range &operator [] (size_t index) { - if (index >= this->ranges.size()) { - this->ranges.resize(index + 1); - } - - return this->ranges.at(index); - } - }; -} - -#endif diff --git a/include/ekg/ui/slider/slider_widget.hpp b/include/ekg/ui/slider/slider_widget.hpp deleted file mode 100644 index 845ed0eb..00000000 --- a/include/ekg/ui/slider/slider_widget.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef EKG_UI_SLIDER_WIDGET_HPP -#define EKG_UI_SLIDER_WIDGET_HPP - -#include "slider.hpp" - -#endif diff --git a/include/ekg/ui/slider/ui_slider.hpp b/include/ekg/ui/slider/ui_slider.hpp deleted file mode 100644 index 46dfe729..00000000 --- a/include/ekg/ui/slider/ui_slider.hpp +++ /dev/null @@ -1,344 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_SLIDER_H -#define EKG_UI_SLIDER_H - -#include "ekg/ui/abstract/ui_abstract.hpp" -#include "ekg/ui/display.hpp" -#include "ekg/util/io.hpp" - -namespace ekg::ui { - class slider : - public ekg::ui::abstract - { - public: - struct range_t { - public: - ekg::value_t f64 {}; - double f64_min {}; - double f64_max {}; - - ekg::value_t f32 {}; - float f32_min {}; - float f32_max {}; - - ekg::value_t u64 {}; - uint64_t u64_min {}; - uint64_t u64_max {}; - - ekg::value_t i64 {}; - int64_t i64_min {}; - int64_t i64_max {}; - - ekg::value_t u32 {}; - uint32_t u32_min {}; - uint32_t u32_max {}; - - ekg::value_t i32 {}; - int32_t i32_min {}; - int32_t i32_max {}; - - ekg::value_t u16 {}; - uint16_t u16_min {}; - uint16_t u16_max {}; - - ekg::value_t i16 {}; - int16_t i16_min {}; - int16_t i16_max {}; - - ekg::value_t u8 {}; - uint8_t u8_min {}; - uint8_t u8_max {}; - - ekg::value_t i8 {}; - int8_t i8_min {}; - int8_t i8_max {}; - - int32_t display_precision {2}; - public: - // idc meow - }; - protected: - ekg::number number {}; - ekg::flags dock_text {}; - ekg::axis axis {}; - ekg::font font_size {}; - std::map range_map {}; - float bar_offset {1.0f}; - public: - /** - * Returns a range-based slider with `ekg::dock::value_t` option. - **/ - template - ekg::ui::slider::range_t &range(uint64_t index = 0) { - if (!this->range_map.count(index)) { - this->range_map.insert( - {index, ekg::ui::slider::range_t {}} - ); - } - - return this->range_map.at(index); - }; - - /** - * Set a new value to an existing range. - * Returns an reference to the range. - **/ - template - ekg::ui::slider::range_t &value(uint64_t index, t val) { - ekg::ui::slider::range_t &range {this->range(index)}; - switch (this->number) { - case ekg::number::float64: - range.f64.set_value(static_cast(val)); - range.f64.registry(this); - range.f64.align_ownership_mem_if_necessary(); - break; - case ekg::number::float32: - range.f32.set_value(static_cast(val)); - range.f32.registry(this); - range.f32.align_ownership_mem_if_necessary(); - break; - case ekg::number::int64: - range.i64.set_value(static_cast(val)); - range.i64.registry(this); - range.i64.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint64: - range.u64.set_value(static_cast(val)); - range.u64.registry(this); - range.u64.align_ownership_mem_if_necessary(); - break; - case ekg::number::int32: - range.i32.set_value(static_cast(val)); - range.i32.registry(this); - range.i32.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint32: - range.u32.set_value(static_cast(val)); - range.u32.registry(this); - range.u32.align_ownership_mem_if_necessary(); - break; - case ekg::number::int16: - range.i16.set_value(static_cast(val)); - range.i16.registry(this); - range.i16.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint16: - range.u16.set_value(static_cast(val)); - range.u16.registry(this); - range.u16.align_ownership_mem_if_necessary(); - break; - case ekg::number::int8: - range.i8.set_value(static_cast(val)); - range.i8.registry(this); - range.i8.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint8: - range.u8.set_value(static_cast(val)); - range.u8.registry(this); - range.u8.align_ownership_mem_if_necessary(); - break; - } - - return range; - } - - /** - * Create a new range but instead of returning the range, - * returns the UI. - **/ - template - ekg::ui::slider *range(uint64_t index, t val, t min, t max, int32_t display_precision = -1) { - ekg::ui::slider::range_t &range {this->range(index)}; - if (display_precision != -1) { - range.display_precision = display_precision; - } - - switch (this->number) { - case ekg::number::float64: - range.f64.set_value(static_cast(val)); - range.f64_min = min; - range.f64_max = max; - range.f64.registry(this); - range.f64.align_ownership_mem_if_necessary(); - break; - case ekg::number::float32: - range.f32.set_value(static_cast(val)); - range.f32_min = min; - range.f32_max = max; - range.f32.registry(this); - range.f32.align_ownership_mem_if_necessary(); - break; - case ekg::number::int64: - range.i64.set_value(static_cast(val)); - range.i64_min = min; - range.i64_max = max; - range.i64.registry(this); - range.i64.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint64: - range.u64.set_value(static_cast(val)); - range.u64_min = min; - range.u64_max = max; - range.u64.registry(this); - range.u64.align_ownership_mem_if_necessary(); - break; - case ekg::number::int32: - range.i32.set_value(static_cast(val)); - range.i32_min = min; - range.i32_max = max; - range.i32.registry(this); - range.i32.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint32: - range.u32.set_value(static_cast(val)); - range.u32_min = min; - range.u32_max = max; - range.u32.registry(this); - range.u32.align_ownership_mem_if_necessary(); - break; - case ekg::number::int16: - range.i16.set_value(static_cast(val)); - range.i16_min = min; - range.i16_max = max; - range.i16.registry(this); - range.i16.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint16: - range.u16.set_value(static_cast(val)); - range.u16_min = min; - range.u16_max = max; - range.u16.registry(this); - range.u16.align_ownership_mem_if_necessary(); - break; - case ekg::number::int8: - range.i8.set_value(static_cast(val)); - range.i8_min = min; - range.i8_max = max; - range.i8.registry(this); - range.i8.align_ownership_mem_if_necessary(); - break; - case ekg::number::uint8: - range.u8.set_value(static_cast(val)); - range.u8_min = min; - range.u8_max = max; - range.u8.registry(this); - range.u8.align_ownership_mem_if_necessary(); - break; - } - - return this; - } - public: - /** - * Set the numbebr, used to determine diff number serializer formats. - * Unsafe due the loss-ptr address from serializer. - **/ - ekg::ui::slider *unsafe_set_number(ekg::number number); - public: - /** - * Returns the size of range list (multi-ranger slider). - **/ - uint64_t get_range_count(); - - /** - * Get the number type used in slider value. - **/ - ekg::number get_number(); - - /** - * Set the current slider axis. Horizontal or vertical. - **/ - ekg::ui::slider *set_axis(ekg::axis new_axis); - - /** - * Get the current axis set. - **/ - ekg::axis get_axis(); - - /** - * Set the base font size. - * Note: Only `ekg::dock::left` or `ekg::dock::right` applies. - **/ - ekg::ui::slider *set_font_size(ekg::font font); - - /** - * Get the font size base. - **/ - ekg::font get_font_size(); - - /** - * Set the dock place. - **/ - ekg::ui::slider *set_place(ekg::flags dock); - - /** - * Set a new width. - **/ - ekg::ui::slider *set_width(float w); - - /** - * Get the width. - **/ - float get_width(); - - /** - * Set the scaled height. - **/ - ekg::ui::slider *set_scaled_height(int32_t h); - - /** - * Get the scaled height. - **/ - int32_t get_scaled_height(); - - /** - * Get the height. - **/ - float get_height(); - - /** - * Set the text dock align. - **/ - ekg::ui::slider *set_text_align(ekg::flags dock); - - /** - * Get the text dock align flags. - **/ - ekg::flags get_text_align(); - - /** - * Set the offset between bars and text. - **/ - ekg::ui::slider *set_bar_offset(float offset); - - /** - * Get the offset between bars and text. - **/ - float get_bar_offset(); - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/slider/ui_slider_widget.hpp b/include/ekg/ui/slider/ui_slider_widget.hpp deleted file mode 100644 index aab1bb3b..00000000 --- a/include/ekg/ui/slider/ui_slider_widget.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_SLIDER_WIDGET_H -#define EKG_UI_SLIDER_WIDGET_H - -#include "ekg/ui/abstract/ui_abstract_widget.hpp" -#include "ekg/ui/slider/ui_slider.hpp" -#include "ekg/draw/font_renderer.hpp" - -namespace ekg::ui { - class slider_widget : public ekg::ui::abstract_widget { - public: - struct range { - public: - ekg::rect rect {}; - ekg::rect target {}; - ekg::rect rect_text {}; - std::string text {}; - ekg::font font_size {}; - float last_dimension {}; - }; - public: - std::vector range_list {}; - uint64_t targetted_range_index {}; - public: - void on_reload() override; - - void on_pre_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_draw_refresh() override; - }; -} - -namespace ekg::ui { - /** - * Calculate the target (value) position from widget dimension. - * Based on all supported number formats. - **/ - float slider_widget_calculate_target_pos( - ekg::ui::slider *&p_ui, - ekg::number number, - float dimension, - uint64_t index - ); - - /** - * Calculate the value by drag factor. - * Based on all supported number formats. - **/ - void slider_widget_calculate_value( - ekg::ui::slider *&p_ui, - ekg::number number, - float factor, - float dimension, - uint64_t index - ); - - /** - * Returns the valeu label in string. - * Based on all supported number formats and precision (`ekg::float32`, `ekg::float64`). - **/ - void slider_widget_get_value_label( - ekg::ui::slider *&p_ui, - ekg::number number, - uint64_t index, - std::string &content - ); - - /** - * Returns both (min, max) text width metrics. - * Based on all supported number formats. - **/ - void slider_widget_get_metrics( - ekg::ui::slider *&p_ui, - ekg::number number, - uint64_t index, - ekg::draw::font_renderer &f_renderer, - float *p_min_text_width, - float *p_max_text_width - ); -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/stack.hpp b/include/ekg/ui/stack.hpp index 94d7bda6..77bc4803 100644 --- a/include/ekg/ui/stack.hpp +++ b/include/ekg/ui/stack.hpp @@ -21,21 +21,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #ifndef EKG_UI_STACK_HPP #define EKG_UI_STACK_HPP -#include -#include +#include "ekg/io/memory.hpp" +#include "ekg/io/descriptor.hpp" -#include "ekg/ui/abstract.hpp" +#include +#include namespace ekg { struct stack_t { public: - std::string_view tag {}; - std::vector children {}; - uint64_t counter {}; + static constexpr ekg::type type {ekg::type::stack}; + static ekg::stack_t not_found; + public: + std::string tag {}; + std::vector widgets {}; + public: + ekg_descriptor(ekg::stack_t); }; } diff --git a/include/ekg/ui/textbox/textbox.hpp b/include/ekg/ui/textbox/textbox.hpp deleted file mode 100644 index f38947da..00000000 --- a/include/ekg/ui/textbox/textbox.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EKG_UI_TEXTBOX_HPP -#define EKG_UI_TEXTBOX_HPP - -#include "ekg/math/geometry.hpp" - -namespace ekg { - struct textbox_theme_t { - public: - ekg::vec4_t text {}; - ekg::vec4_t background {}; - ekg::vec4_t outline {}; - ekg::vec4_t cursor {}; - ekg::vec4_t select {}; - }; - - struct textbox_t { - public: - }; -} - -#endif diff --git a/include/ekg/ui/textbox/ui_textbox.hpp b/include/ekg/ui/textbox/ui_textbox.hpp deleted file mode 100644 index e2143730..00000000 --- a/include/ekg/ui/textbox/ui_textbox.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_TEXTBOX_H -#define EKG_UI_TEXTBOX_H - -#include "ekg/ui/abstract/ui_abstract.hpp" -#include "ekg/ui/display.hpp" -#include "ekg/util/io.hpp" - -namespace ekg::ui { - class textbox : public ekg::ui::abstract, public ekg::value_t, ekg::ui::textbox> { - protected: - ekg::font font_size {}; - std::string formatted_text {}; - bool must_format_text {}; - ekg::state typing_state {}; - uint8_t tab_size {4}; - - uint64_t max_lines {UINT32_MAX}; - uint64_t max_chars_per_line {UINT32_MAX}; - public: - /** - * Set UI `must_format_text` value. - * If true `ekg::ui::listbox::get_text` re-format the content, - * else false content is not re-formatted. - */ - ekg::ui::textbox *set_must_format_text(bool state); - - /** - * Return UI `must_format_text` value. - */ - bool is_must_format_text(); - - ekg::ui::textbox *set_font_size(ekg::font font); - - ekg::font get_font_size(); - - ekg::ui::textbox *set_place(ekg::flags dock); - - int32_t get_scaled_height(); - - ekg::ui::textbox *set_width(float w); - - float get_width(); - - ekg::ui::textbox *set_scaled_height(int32_t h); - - float get_height(); - - /** - * Return formatted text from text box content. - * if `must_format_text` is true, then re-format the text, - * and automatically set `must_format_text` to false. - */ - std::string get_text(); - - ekg::ui::textbox *set_tab_size(uint8_t size); - - uint8_t get_tab_size(); - - ekg::ui::textbox *set_max_chars_per_line(uint64_t chars_per_line); - - uint64_t get_max_chars_per_line(); - - ekg::ui::textbox *set_max_lines(uint64_t lines); - - uint64_t get_max_lines(); - - /** - * Set enabled/disabled typing state. - * Textbox will not accept-type response until typing state is enabled. - **/ - ekg::ui::textbox *set_typing_state(ekg::state state); - - /** - * Returns typing state: enabled/disabled. - **/ - ekg::state get_typing_state(); - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/textbox/ui_textbox_widget.hpp b/include/ekg/ui/textbox/ui_textbox_widget.hpp deleted file mode 100644 index c4ab4c1c..00000000 --- a/include/ekg/ui/textbox/ui_textbox_widget.hpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UI_TEXT_BOX_WIDGET_H -#define EKG_UI_TEXT_BOX_WIDGET_H - -#include -#include "ekg/ui/scrollbar/ui_scrollbar_embedded_widget.hpp" -#include "ekg/ui/abstract/ui_abstract_widget.hpp" - -/* start of `ekg_textbox_clamp_text_chunk_size` macro */ -#define ekg_textbox_clamp_text_chunk_size(text_chunk_list, max_size) \ - if (p_ui->p_value->size() > max_size) p_ui->p_value->erase(p_ui->p_value->begin() + max_size, p_ui->p_value->end()) -/* end of `ekg_textbox_clamp_text_chunk_size` macro */ - -/* start of `ekg_textbox_clamp_text_chunk_size` macro */ -#define ekg_textbox_clamp_line(line, max_size) \ - if (line.size() > max_size) line.erase(line.begin() + max_size, line.end()) -/* end of `ekg_textbox_clamp_line` macro */ - -/* start of `ekg_textbox_get_cursor_text` macro */ -#define ekg_textbox_get_cursor_text(cursor) \ - p_ui->p_value->at(cursor.chunk_index) -/* end of `ekg_textbox_clamp_line` macro */ - -namespace ekg::ui { - class textbox_widget : public ekg::ui::abstract_widget { - public: - enum action { - add_text, - erase_text, - break_line - }; - - struct cursor_pos { - public: - int64_t select_index {}; - int64_t chunk_index {}; - int64_t text_index {}; - int64_t last_text_index {}; - public: - inline bool operator>(ekg::ui::textbox_widget::cursor_pos &r) { - return (this->text_index > r.text_index && this->chunk_index == r.chunk_index) || - this->chunk_index > r.chunk_index; - } - - inline bool operator<(ekg::ui::textbox_widget::cursor_pos &r) { - return (this->text_index < r.text_index && this->chunk_index == r.chunk_index) || - this->chunk_index < r.chunk_index; - } - - inline bool operator>=(ekg::ui::textbox_widget::cursor_pos &r) { - return (this->text_index >= r.text_index && this->chunk_index == r.chunk_index) || - this->chunk_index > r.chunk_index; - } - - inline bool operator<=(ekg::ui::textbox_widget::cursor_pos &r) { - return (this->text_index <= r.text_index && this->chunk_index == r.chunk_index) || - this->chunk_index < r.chunk_index; - } - - inline bool operator==(ekg::ui::textbox_widget::cursor_pos &r) { - return this->text_index == r.text_index && this->chunk_index == r.chunk_index; - } - - inline bool operator!=(ekg::ui::textbox_widget::cursor_pos &r) { - return !(*this == r); - } - }; - - struct cursor { - public: - /* - * pos[0] is the cursor position A, - * pos[1] is the cursor position B, - * pos[2] is the initial select position, - * pos[3] is the select target side. - */ - ekg::ui::textbox_widget::cursor_pos pos[4] {}; - }; - public: - std::vector cursor_draw_data_list {}; - std::vector text_utf_char_index_list {}; - uint64_t last_text_chunk_size {}; - - std::string widget_side_text {}; - std::vector loaded_multi_cursor_list { - ekg::ui::textbox_widget::cursor() - }; - - ekg::rect rect_text {}; - ekg::rect rect_cursor {}; - ekg::ui::scrollbar_embedded_widget embedded_scroll {}; - - float cursor_char_wsize[3] {}; - float text_offset {}; - float text_height {}; - ekg::vec2 cursor_delta {}; - - bool text_edited {}; - bool update_ui_text {}; - bool is_ui_enabled {}; - bool is_action_modifier_enable {}; - bool is_action_select_enable {}; - bool was_typed {}; - - bool is_clipboard_copy {}; - bool is_clipboard_cut {}; - bool is_clipboard_paste {}; - - int64_t total_utf_chars {}; - std::string cached_tab_size {}; - uint64_t visible_text[4] {}; - uint64_t latest_size_until_refresh {}; - public: - bool find_cursor( - ekg::ui::textbox_widget::cursor &target_cursor, - int64_t total_it, - int64_t it_chunk, - bool last_line_utf_char_index - ); - - void check_cursor_text_bounding(ekg::ui::textbox_widget::cursor &cursor, bool reset_second_cursor_pos); - - void process_text( - ekg::ui::textbox_widget::cursor &cursor, - std::string_view text, - ekg::ui::textbox_widget::action action, - int64_t direction - ); - - void move_cursor(ekg::ui::textbox_widget::cursor_pos &cursor, int64_t x, int64_t y); - - void update_ui_text_data(); - - void check_nearest_word(ekg::ui::textbox_widget::cursor &cursor, int64_t &x, int64_t &y); - - void move_target_cursor(ekg::ui::textbox_widget::cursor &cursor, int64_t x, int64_t y); - - void clamp_text_chunk_size(); - - public: - void on_create() override; - - void on_reload() override; - - void on_pre_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_post_event(ekg::os::io_event_serial &io_event_serial) override; - - void on_update() override; - - void on_draw_refresh() override; - }; -} - -#endif \ No newline at end of file diff --git a/include/ekg/ui/types.hpp b/include/ekg/ui/types.hpp deleted file mode 100644 index 71310643..00000000 --- a/include/ekg/ui/types.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef EKG_UI_TYPES_HPP -#define EKG_UI_TYPES_HPP - -#include "ekg/math/geometry.hpp" - -namespace ekg { - enum type { - abstract, - button, - checkbox, - combobox, - frame, - label, - listbox, - menu, - popup, - scrollbar, - slider, - textbox - }; - - enum class level { - bottom, - top - }; -} - -namespace ekg::ui { - struct states_t { - public: - bool is_hovering {}; - bool is_highlighting {}; - - /** - * X if horizontal is scrolling; - * Y if vertical is scrolling; - * Z if horizontal is scrollable; - * W if vertical is scrollable; - **/ - ekg::vec4_t is_scrolling {}; - - /** - * The nearest-bar pixel-thicnkess; - **/ - ekg::pixel_thickness_t nearest_scroll_bar_thickness {}; - - bool is_active {}; - bool is_focused {}; - - /** - * Generic use-case reserved - **/ - bool is[4] {}; - - bool is_high_frequency {}; - bool is_targeting_absolute_parent {}; - bool is_absolute {}; - - bool was_reloaded {}; - bool was_layout_docknized {}; - bool was_just_created {}; - }; -} - -#endif diff --git a/include/ekg/util/geometry.hpp b/include/ekg/util/geometry.hpp deleted file mode 100644 index ba80da21..00000000 --- a/include/ekg/util/geometry.hpp +++ /dev/null @@ -1,210 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UTIL_GEOMETRY_H -#define EKG_UTIL_GEOMETRY_H - -#include -#include -#include -#include -#include - -#define ekg_equals_float(x, y) ((fabsf((x) - (y))) <= FLT_EPSILON * fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))) -#define ekg_pixel_div_2 (0.500000f) -#define ekg_pixel (1.000000f) -#define ekg_pi (3.141592653589793238462643383279502884) - -#define ekg_no_update_placement (0.0f) - -/* - * The STL std:::min and std::max implementation, - * are not suitable to perform the required comparions inside of EKG - * without making the code confuse. - * - * Max STL impl: - * ``` - * (a < b) ? b : a; - * ``` - * - * Max EKG impl: - * ``` - * (a > b) ? b : a; - * ``` - */ -#define ekg_min(a, b) ((a < b) ? b : a) -#define ekg_max(a, b) ((a > b) ? b : a) -#define ekg_clamp(a, b, c) ((a < b) ? b : ((a > c) ? c : a)) - -namespace ekg { - enum dock { - none = 2 << 1, - free = 2 << 2, - top = 2 << 3, - bottom = 2 << 4, - right = 2 << 5, - left = 2 << 6, - center = 2 << 7, - full = 2 << 8, - next = 2 << 9, - fill = 2 << 10, - resize = 2 << 11, - bind = 2 << 12 - }; - - /* - * Enum linked to dock. - */ - enum axis { - vertical = 2 << 13, - horizontal = 2 << 14 - }; - - struct vec2 { - public: - float x {}; - float y {}; - }; - - ekg::vec2 operator/(const ekg::vec2 &, float); - - struct vec3 { - public: - float x {}; - float y {}; - float z {}; - }; - - struct vec4 { - public: - float x {}; - float y {}; - float z {}; - float w {}; - }; - - struct rect { - public: - float x {}; - float y {}; - float w {}; - float h {}; - public: - /** - * Logs the dimensions. - **/ - operator std::string() { - std::ostringstream buffer {}; - - buffer << - " x: " << this->x << - " y: " << this->y << - " w: " << this->w << - " h: " << this->h; - - return buffer.str(); - } - }; - - bool operator==(const ekg::rect &, const ekg::rect &); - - bool operator!=(const ekg::rect &, const ekg::rect &); - - bool operator==(const ekg::rect &l, const ekg::vec4 &r); - - bool operator!=(const ekg::rect &l, const ekg::vec4 &r); - - ekg::rect operator-(const ekg::rect &l, const ekg::rect &r); - - ekg::rect operator+(const ekg::rect &l, const ekg::rect &r); - - ekg::rect operator-(const ekg::rect &l, const ekg::vec4 &r); - - ekg::rect operator+(const ekg::rect &l, const ekg::vec4 &r); - - ekg::vec4 operator-(const ekg::vec4 &l, const ekg::vec4 &r); - - ekg::vec4 operator+(const ekg::vec4 &l, const ekg::vec4 &r); - - ekg::vec4 operator-(const ekg::vec4 &l, const ekg::rect &r); - - ekg::vec4 operator+(const ekg::vec4 &l, const ekg::rect &r); - - ekg::vec4 operator/(const ekg::vec4 &l, float r); - - struct docker { - public: - ekg::rect left {}; - ekg::rect right {}; - ekg::rect top {}; - ekg::rect bottom {}; - ekg::rect center {}; - ekg::rect rect {}; - }; - - struct dock_rect { - public: - ekg::rect *p_rect {}; - uint16_t dock {}; - }; - - struct placement { - public: - ekg::rect rect {}; - ekg::rect rect_extent {}; - - ekg::rect rect_text {}; - uint16_t text_dock_flags {}; - float offset {}; - }; - - /** - * Get a index n-size by scroll amount. - * Note: You must have a compatible scroll-size with the n-index-size. - **/ - uint64_t get_index_by_scroll(float scroll, float dimension, uint64_t size); - - /** - * Get a index n-size by scroll amount. - * Note: You must have a compatible scroll-size with the n-index-size. - **/ - uint64_t get_index_by_normalized_scroll(float normalized, uint64_t size); - - bool rect_collide_rect(const ekg::rect &a, const ekg::rect &b); - bool rect_collide_vec(const ekg::rect &rect, const ekg::vec4 &vec); - bool rect_collide_vec_precisely(const ekg::rect &rect, const ekg::vec4 &vec); - - void set_rect_clamped(ekg::rect &rect, float min_size); - void set_dock_scaled(const ekg::rect &rect, const ekg::vec2 &offset, ekg::docker &docker); - void ortho(float *p_matrix, float left, float right, float bottom, float top); - - float find_min_offset(float width, float offset); - float smooth(float duration, uint64_t ticks); - float lerp(float a, float b, float dt); - - ekg::vec4 color(int32_t r, int32_t g, int32_t b, int32_t a); - int32_t find_collide_dock(ekg::docker &docker, uint16_t flags, const ekg::vec4 &vec); -} - -#endif diff --git a/include/ekg/util/gui.hpp b/include/ekg/util/gui.hpp deleted file mode 100644 index ee306ddb..00000000 --- a/include/ekg/util/gui.hpp +++ /dev/null @@ -1,149 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UTIL_EVENT_H -#define EKG_UTIL_EVENT_H - -#include -#include - -#include "ekg/ui/abstract/ui_abstract_widget.hpp" -#include "ekg/core/task.hpp" - -namespace ekg { - enum class env { - refresh, - swap, - reload, - synclayout, - gc, - redraw - }; - - /** - * A widget stack collector, used in many things. - * You may want to create GUI scenes with auto-destruction. - * - * E.g code: - * showcase = ekg::stack("gui-stack"); - * // insert widgets - * showcase.end(); // detatch stack - * - * // on GUI close - * showcase.destroy(); - * // or destructor - * ~showcase(); - **/ - struct stack { - protected: - bool was_destroyed {}; - public: - std::string_view tag {}; - std::vector ordered_list {}; - int32_t target_id {}; - bool target_id_found {}; - public: - stack() = default; - - /** - * Create a stack and update on runtime, to disable it - * set true `do_not_trigger_stack_update` param var. - **/ - stack(std::string_view tag, bool do_not_trigger_stack_update = false); - - /** - * Destructor where all widgets stacked are killed. - **/ - ~stack(); - - /** - * Push this stack to runtime, then collect widgets generated. - **/ - void push(); - - /** - * Stop to stack widgets. - **/ - void pop(); - - /** - * Destroy all widgets stacked. - **/ - void destroy(); - - /** - * Just clear the stack without kill. - **/ - void clear(); - - /** - * Find for a UI based-by the tag. - * Returns nullptr if not found. - **/ - template - constexpr t *find(std::string_view tag) { - for (ekg::ui::abstract_widget *&p_widgets : this->ordered_list) { - if (p_widgets->p_data->get_tag() == tag) { - return (t*) (p_widgets->p_data); - } - } - - return nullptr; - } - }; -} - -namespace ekg { - void dispatch(task *p_ekg_event); - - void dispatch(ekg::env env); - - void reload(int32_t id); - - void reload(ekg::ui::abstract_widget *p_widget); - - void synclayout(int32_t id); - - void synclayout(ekg::ui::abstract_widget *p_widgets); - - void refresh(int32_t id); - - void refresh(ekg::ui::abstract_widget *p_widget); - - void update_high_frequency(int32_t id); - - void update_high_frequency(ekg::ui::abstract_widget *p_widget); - - void push_back_stack(ekg::ui::abstract_widget *p_widget, ekg::stack &stack); - - ekg::ui::abstract_widget *find_absolute_parent_master(ekg::ui::abstract_widget *p_widget); - - int32_t &set(int32_t &value, int32_t result); - - bool &set(bool &value, bool result); - - std::string &set(std::string &value, std::string_view result); -} - -#endif \ No newline at end of file diff --git a/include/ekg/util/image.hpp b/include/ekg/util/image.hpp deleted file mode 100644 index 023be07b..00000000 --- a/include/ekg/util/image.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EKG_UTIL_IMAGE_HPP -#define EKG_UTIL_IMAGE_HPP - -#include -#include FT_FREETYPE_H - -#include - -namespace ekg { - enum format_convert_result { - success, - failed - }; - - ekg::format_convert_result image_src_r8_convert_to_r8g8b8a8( - FT_Vector size, - const unsigned char *p_src, - std::vector &dst - ); -} - -#endif \ No newline at end of file diff --git a/include/ekg/util/io.hpp b/include/ekg/util/io.hpp deleted file mode 100644 index b58e8cfb..00000000 --- a/include/ekg/util/io.hpp +++ /dev/null @@ -1,352 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UTIL_IO_H -#define EKG_UTIL_IO_H - -#include -#include -#include -#include -#include -#include -#include - -#if defined(__ANDROID__) -#include -#endif - -#include "geometry.hpp" -#include "ekg/ui/display.hpp" - -#define ekg_bitwise_contains(flags, flag) (flags & flag) -#define ekg_bitwise_remove(flags, flag) (flags &= ~flag) -#define ekg_bitwise_add(flags, flag) (flags |= flag) - -/** - * LOG NO-TRACE dev-case reserved. - * Note: this macro call console out (`std::cout`) directly, not safe for official log trace. - **/ -#define ekg_log(msg) std::cout << msg << std::endl; -#define ekg_log_assert(assert, msg) if (!assert) ekg_log(msg); - -namespace ekg { - typedef uint32_t flags; - typedef uint32_t id; -} - -namespace ekg { - class item : public std::vector { - protected: - std::string value {}; - ekg::flags attr_bits {}; - ekg::placement placement {}; - uint64_t visible_count {}; - bool *p_semaphore {nullptr}; - ekg::item *p_addressed {}; - public: - explicit item() = default; - - item( - std::string_view insert_value, - ekg::flags insert_attr_bits = 0 - ); - - item( - std::string_view insert_value, - std::vector insert_item_list, - ekg::flags insert_attr_bits = 0 - ); - - /** - * Idk my brain buuuobmbobm obmormobmmbrmfdf - **/ - item(std::initializer_list __l) : std::vector(__l) {}; - - ~item(); - public: - /** - * Set the addressed item which point to the primordial item. - * Unsafe due the nullptr crash possibility. - **/ - void unsafe_set_addressed(ekg::item *p_set_addressed); - - /** - * Get the primordial item, used in rendering cache. - **/ - ekg::item *unsafe_get_addressed(); - - /** - * Set the semaphore used in rendering-cache, to invoke changes on - * widget rendering. - * Unsafe by the performance directly effect. - **/ - void unsafe_set_semaphore(bool *p_set_semaphore); - - /** - * Get the item-placement used for rendering. - * Unsafe due the return as reference, bypassing the rendering states. - **/ - ekg::placement &unsafe_get_placement(); - - /** - * Set the visible count used in rendering cache. - * Unsafe due the performanceless if visible count is higher than the truly-visible. - **/ - void unsafe_set_visible_count(uint64_t count); - public: - void set_value(std::string_view new_value); - - std::string get_value(); - - void set_attr(ekg::flags bits); - - ekg::flags get_attr(); - - void set_text_align(ekg::flags dock); - - ekg::flags get_text_align(); - - /** - * Get the amount of visible items. - * Note: Reserved purpose-only for rendering cache. - **/ - uint64_t get_visible_count(); - }; - - /** - * Number range types, used in specific-features. - **/ - enum class number { - float64, - float32, - int64, - uint64, - int32, - uint32, - int16, - uint16, - int8, - uint8, - }; - - /** - * Base class for specific-features. - * Note: None. - **/ - struct feature {}; - - template - ekg::number retreive_number_type_from_var_type() { - if (typeid(t) == typeid(double)) { - return ekg::number::float64; - } else if (typeid(t) == typeid(float)) { - return ekg::number::float32; - } else if (typeid(t) == typeid(int64_t)) { - return ekg::number::int64; - } else if (typeid(t) == typeid(uint64_t)) { - return ekg::number::uint64; - } else if (typeid(t) == typeid(int32_t)) { - return ekg::number::int32; - } else if (typeid(t) == typeid(uint32_t)) { - return ekg::number::uint32; - } else if (typeid(t) == typeid(int16_t)) { - return ekg::number::int16; - } else if (typeid(t) == typeid(uint16_t)) { - return ekg::number::uint16; - } else if (typeid(t) == typeid(int8_t)) { - return ekg::number::int8; - } - - // typeid(t) == typeid(uint8_t) - return ekg::number::uint8; - } - - template - struct value_t { - protected: - bool changed {}; - bool not_self_ownership {}; - protected: - s *p_mommy_s {}; - public: - t self {}; - t *p_value {nullptr}; - public: - value_t() { - this->p_value = &this->self; - }; - - operator bool() { - return this->changed; - } - public: - void registry(s *p_mommy) { - if (this->p_mommy_s == nullptr) { - this->p_mommy_s = p_mommy; - } - } - - s *box(t val) { - this->self = val; - return this->p_mommy_s; - } - - s *transfer_ownership(t *p_address) { - this->p_value = p_address; - this->not_self_ownership = true; - return this->p_mommy_s; - } - - s *reset_ownership() { - this->p_value = &this->self; - this->not_self_ownership = false; - return this->p_mommy_s; - } - - s *set_value(t val) { - this->changed = *this->p_value != val; - *this->p_value = val; - - if (this->changed) { - ekg::ui::redraw = true; - } - - return this->p_mommy_s; - } - - t &get_value() { - return *this->p_value; - } - - bool was_changed() { - bool was {this->changed}; - this->changed = false; - return was; - } - - bool was_not_ownership_transfered() { - return this->not_self_ownership; - } - - void align_ownership_mem_if_necessary() { - if (!this->not_self_ownership) { - this->p_value = &this->self; - } - } - }; - - enum attr { - disabled = 2 << 1, - focused = 2 << 2, - hovering = 2 << 3, - locked = 2 << 4, - opened = 2 << 5, - - contains_separator = 2 << 6, - contains_icon = 2 << 7, - - // legacy compatibility - separator = 2 << 7 - }; - - struct timing { - public: - /** - * The 1 second counter in ms. - */ - static int64_t second; - - /** - * The total running ticks since the application was started. - */ - static int64_t ticks; - public: - int64_t elapsed_ticks {}; - int64_t current_ticks {}; - int64_t ticks_going_on {}; - }; - - struct flag { - bool highlight {}; - bool hovered {}; - bool activity {}; - bool focused {}; - bool state {}; - bool extra_state {}; - bool absolute {}; - bool was_hovered {}; - bool scrolling {}; - }; - - /** - * Deprecated! - * - **/ - enum class clipboard { - copy = 1073741948, // SDLK_COPY - cut = 1073741947, // SDLK_CUT - paste = 1073741949 // SDLK_PASTE - }; -} - -namespace ekg { - bool file_to_string(std::string &file_content, std::string_view path); - - bool reach(ekg::timing &timing, int64_t ms); - - bool reset(ekg::timing &timing); - - bool extend(ekg::timing &timing, int64_t ms); - - int64_t interval(ekg::timing &timing); -} - -namespace ekg::input { - bool action(std::string_view action_key); - - bool receive(std::string_view input_key); - - void clipboard(ekg::clipboard clipboard_op); - - void fire(std::string_view action_key); - - void bind(std::string_view action_key, std::string_view input_key); - - void bind(std::string_view action_key, const std::vector &bind_list); - - bool motion(); - - bool released(); - - bool pressed(); - - bool wheel(); - - bool typed(); - - ekg::vec4 &interact(); -} - -#endif diff --git a/include/ekg/util/text.hpp b/include/ekg/util/text.hpp deleted file mode 100644 index a989535c..00000000 --- a/include/ekg/util/text.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef EKG_UTIL_TEXT_HPP -#define EKG_UTIL_TEXT_HPP - -#include -#include -#include - -#include "ekg/util/io.hpp" - -namespace ekg { - /** - * Returns a UTF string by `char32` converting - * the UTF-32 unique char into a sequence of UTF-8 - * chars. - */ - std::string utf_char32_to_string(char32_t char32); - - /** - * Returns a string subtracted by stride begin `offset` - * and end index `size`. If string is empty, return - * empty. - */ - std::string utf_substr(std::string_view string, uint64_t offset, uint64_t size); - - /** - * Returns the `string` length considering UTF chars. - */ - uint64_t utf_length(std::string_view string); - - /** - * Returns index size that represent an UTF-8 char. - * Possibles: - * 3, 2, 1, and 0. - */ - uint64_t utf_check_sequence( - uint8_t &char8, - char32_t &char32, - std::string &utf_string, - std::string_view string, - uint64_t index - ); - - /** - * Returns a UTF-32 char32 based on UTF-8 sequence. - */ - char32_t utf_string_to_char32(std::string_view string); - - /** - * Fast splitter specialized in `\n` or `\r\n` (non OS unix-based). - * UTF to sinalize the string unicode-like suggested by EKG. - */ - void utf_decode(std::string_view string, std::vector &utf_decoded); - - /** - * Returns a customised float64 precision as a string. - **/ - std::string string_float64_precision(double number, int32_t precision); - - /** - * Returns a customised float precision as a string. - **/ - std::string string_float_precision(float number, int32_t precision); - - /** - * Return the index of last attribute-token and insert bits. - */ - uint8_t check_attribute_flags(std::string_view text, ekg::flags &flags); - - /** - * Return true if `string` contains `find_char`, - * then it must allocate and insert elements to - * `p_string_split_list` ptr. - */ - bool split(std::vector *p_string_split_list, const std::string &string, char find_char); -} - -#endif diff --git a/src/core/context.cpp b/src/core/context.cpp deleted file mode 100644 index 7669f806..00000000 --- a/src/core/context.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "ekg/core/context.hpp" - -FT_Library ekg::freetype_library {}; -ekg::viewport_t ekg::viewport {}; -ekg::dpi_t ekg::dpi {}; -ekg::tweaks_t ekg::tweaks {}; -ekg::current_t ekg::current {}; diff --git a/include/ekg/os/ekg_vulkan.hpp b/src/core/pools.cpp similarity index 79% rename from include/ekg/os/ekg_vulkan.hpp rename to src/core/pools.cpp index abc3ce5b..93e57195 100644 --- a/include/ekg/os/ekg_vulkan.hpp +++ b/src/core/pools.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,17 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/core/pools.hpp" +#include "ekg/core/runtime.hpp" -#ifndef EKG_OS_VULKAN_HPP -#define EKG_OS_VULKAN_HPP +void ekg::core::registry(ekg::property_t &property) { + ekg::p_core->registry.push_back(property.at); -#include "ekg/gpu/api.hpp" - -namespace ekg { - class vulkan : public ekg::gpu::api { - protected: - // VkDescriptorPool *p_descriptor_pool {}; - }; + ekg::io::dispatch( + ekg::io::operation::swap, + property.at + ); } - -#endif diff --git a/src/core/runtime.cpp b/src/core/runtime.cpp index f4f1044d..1a46dd4a 100644 --- a/src/core/runtime.cpp +++ b/src/core/runtime.cpp @@ -1,259 +1,252 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include "ekg/core/runtime.hpp" -#include "ekg/layout/scale.hpp" +#include "ekg/layout/scalenize.hpp" +#include "ekg/io/log.hpp" +#include "ekg/core/pools.hpp" #include "ekg/core/context.hpp" -#include "ekg/draw/shape.hpp" - -ekg::runtime *ekg::p_core {}; - -void ekg::runtime::init() { - this->service_handler.init(); - - /** - * Allocate order is important here, all pre-allocated tasks MUST follow - * the `ekg::io::operation` order. - * - * Example: - * ```c++ - * enum operation { - * swap, // first - * // etc - * // etc - * redraw // MUST BE the last one - * }; - * ``` - **/ - - this->swap_target_collector.unique_id = ekg::io::invalid_unique_id; - this->service_handler.allocate() = new ekg::task_t { - .info = ekg::info_t { - .tag = "swap", - .p_properties = nullptr, - .p_data = nullptr - }, - .function = [this](ekg::info_t &info) { - if (this->swap_target_collector.unique_id == ekg::io::invalid_unique_id) { - return; - } - - ekg::flags_t result {}; - std::vector top_level_widget_list {}; - this->context_widget_list.clear(); - - for (std::unique_ptr &p_widget : this->loaded_widget_list) { - if ( - p_widget->properties.p_parent != nullptr - || - /** - * TODO: check this right - **/ - p_widget->properties.p_abs_parent->p_widget == nullptr - ) { - continue; - } - - this->swap_target_collector.was_target_found = false; - this->swap_target_collector.storage.clear(); - - result = ekg::io::push_back_widget_tree_recursively( - &this->swap_target_collector, - static_cast(p_widget->properties.p_abs_parent->p_widget) - ); - - if (result != ekg::result::success) { - continue; - } - - if (this->swap_target_collector.was_target_found) { - top_level_widget_list.insert( - top_level_widget_list.begin(), - this->swap_target_collector.storage.begin(), - this->swap_target_collector.storage.end() - ); - } else { - this->context_widget_list.insert( - this->context_widget_list.end(), - this->swap_target_collector.storage.begin(), - this->swap_target_collector.storage.end() - ); - } - } +#include "ekg/layout/docknize.hpp" - this->context_widget_list.insert( - this->context_widget_list.end(), - top_level_widget_list.begin(), - top_level_widget_list.end() - ); +void ekg::core::swap_collector( + bool &was_found, + ekg::at_t &property_at +) { + if (property_at == ekg::at_t::not_found) { + return; + } - this->swap_target_collector.storage.clear(); - this->swap_target_collector.unique_id = ekg::io::invalid_unique_id; - } + ekg::property_t &parent_property { + ekg::query(property_at) }; - this->service_handler.allocate() = new ekg::task_t { - .info = { - .tag = "reload", - .p_properties = nullptr, - .p_data = nullptr - }, - .function = [this](ekg::info_t &info) { - for (ekg::ui::abstract *&p_widgets : this->reload_widget_list) { - if (!p_widgets->states.was_reloaded) { - continue; - } - - p_widgets->on_reload(); - p_widgets->states.was_reloaded = false; - } + if ( + parent_property.at == ekg::gui.bind.swap_at + ) { + was_found = true; + } - this->reload_widget_list.clear(); - ekg::viewport.redraw = true; - } - }; + ekg::p_core->collector.push_back(property_at); - this->service_handler.allocate() = new ekg::task_t { - .info = { - .tag = "layout-docknize", - .p_properties = nullptr, - .p_data = nullptr - }, - .function = [this](ekg::info_t &info) { - for (ekg::ui::abstract *&p_widgets : this->layout_docknize_list) { - if (!p_widgets->states.was_layout_docknized) { - continue; - } - - ekg::layout::docknize_widget(p_widgets); - p_widgets->states.was_layout_docknized = false; + if (parent_property.widget.is_childnizate && parent_property.widget.is_children_docknizable) { + for (ekg::at_t &at : parent_property.children) { + ekg::property_t &property {ekg::query(at)}; + if (property == ekg::property_t::not_found) { + continue; } - this->layout_docknize_list.clear(); + ekg::core::swap_collector(was_found, at); } - }; + } +} - this->service_handler.allocate() = new ekg::task_t { - .info = { - .tag = "scale-update", - .p_properties = nullptr, - .p_data = nullptr - }, - .function = [this](ekg::info_t &info) { - ekg::layout::scale_calculate(); - - ekg::dpi.font_scale = ekg::clamp( - ekg::dpi.font_scale, - static_cast(ekg::minimum_font_height), - static_cast(UINT8_MAX) - ); +void ekg::core::swap(ekg::info_t &info) { + if ( + ekg::p_core == nullptr + || + ekg::gui.bind.swap_at == ekg::at_t::not_found + ) { + return; + } - uint32_t font_size { - ekg::clamp( - static_cast( - ekg::dpi.font_scale - * - ekg::dpi.factor_scale - ), - 0, - UINT8_MAX - ) - }; - - if (this->draw_fr_normal.font_size != font_size) { - this->draw_fr_small.set_size( - ekg::clamp_min( - font_size - ekg::dpi.font_offset.x, - ekg::minimum_small_font_height - ) - ); - - this->draw_fr_normal.set_size( - ekg::clamp_min( - font_size, - ekg::minimum_font_height - ) - ); - - this->draw_fr_big.set_size( - ekg::clamp_min( - font_size + ekg::dpi.font_offset.y, - ekg::minimum_big_font_height - ) - ); - } + bool was_found {}; + ekg::p_core->top_level_stack.clear(); - for (ekg::ui::abstract *&p_widgets : this->context_widget_list) { - if (p_widgets == nullptr || !p_widgets->properties.is_docknizable) { - continue; - } + for (ekg::at_t &at : ekg::p_core->registry) { + ekg::property_t &property {ekg::query(at)}; - this->layout_docknize_list.push_back(p_widgets); - p_widgets->states.was_layout_docknized = true; - p_widgets->properties.must_refresh_size = true; - } + if ( + property == ekg::property_t::not_found + || + property.parent_at != ekg::at_t::not_found + ) { + continue; + } - ekg::io::dispatch( - ekg::io::operation::reload - ); + // recurse + ekg::p_core->collector.clear(); + ekg::core::swap_collector(was_found, at); - ekg::io::dispatch( - ekg::io::operation::layout_docknize + if (was_found) { + ekg::p_core->top_level_stack.insert( + ekg::p_core->top_level_stack.begin(), + ekg::p_core->collector.begin(), + ekg::p_core->collector.end() + ); + } else { + ekg::p_core->stack.insert( + ekg::p_core->stack.begin(), + ekg::p_core->collector.begin(), + ekg::p_core->collector.end() ); } - }; + } + + ekg::p_core->stack.insert( + ekg::p_core->stack.end(), + ekg::p_core->top_level_stack.begin(), + ekg::p_core->top_level_stack.end() + ); + + ekg::p_core->collector.clear(); + ekg::gui.bind.swap_at = ekg::at_t::not_found; +} + +void ekg::core::reload(ekg::info_t &info) { + if (ekg::p_core == nullptr) { + return; + } + + for (ekg::at_t &at : ekg::p_core->reload) { + ekg::property_t &property {ekg::query(at)}; + if (property == ekg::property_t::not_found) { + continue; + } + + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, + ekg::ui::reload(property, descriptor); + ); + + property.operation.should_reload = false; + } + + ekg::p_core->reload.clear(); +} - this->gpu_allocator.init(); - this->service_theme.init(); - this->service_input.init(); +void ekg::core::docknize(ekg::info_t &info) { + if (ekg::p_core == nullptr) { + return; + } - ekg::log() << "Doing font-rendering tweaks, and pre-setting viewport scale..."; + for (ekg::at_t &at : ekg::p_core->reload) { + ekg::property_t &property {ekg::query(at)}; + if (property == ekg::property_t::not_found) { + continue; + } - this->draw_fr_small.atlas_texture_sampler.gl_protected_active_index = true; - this->draw_fr_small.set_size(16); - this->draw_fr_small.bind_allocator(&this->gpu_allocator); + ekg::layout::docknize_widget( + property + ); - this->draw_fr_normal.atlas_texture_sampler.gl_protected_active_index = true; - this->draw_fr_normal.set_size(18); - this->draw_fr_normal.bind_allocator(&this->gpu_allocator); + property.operation.should_docknize = false; + } - this->draw_fr_big.atlas_texture_sampler.gl_protected_active_index = true; - this->draw_fr_big.set_size(24); - this->draw_fr_big.bind_allocator(&this->gpu_allocator); + ekg::p_core->reload.clear(); } -void ekg::runtime::quit() { - this->service_handler.quit(); - this->service_theme.quit(); - this->service_input.quit(); - this->gpu_allocator.quit(); +void ekg::core::scalenize(ekg::info_t &info) { + if (ekg::p_core == nullptr) { + return; + } + + ekg::layout::scalenize(); + + ekg::dpi.font_scale = ekg::clamp( + ekg::dpi.font_scale, + static_cast(4), + static_cast(UINT8_MAX) + ); + + uint32_t font_size { + ekg::clamp( + static_cast( + ekg::dpi.font_scale + * + ekg::dpi.factor_scale + ), + 0, + UINT8_MAX + ) + }; + + if (ekg::p_core->draw_font_medium.font_size != font_size) { + ekg::p_core->draw_font_small.set_size( + ekg::clamp_min( + font_size - ekg::dpi.font_offset.x, + ekg::minimum_small_font_height + ) + ); + + ekg::p_core->draw_font_medium.set_size( + ekg::clamp_min( + font_size, + ekg::minimum_font_height + ) + ); + + ekg::p_core->draw_font_big.set_size( + ekg::clamp_min( + font_size + ekg::dpi.font_offset.y, + ekg::minimum_big_font_height + ) + ); + } } -void ekg::runtime::poll_events() { - this->service_input.on_event(); +void ekg::core::poll_event() { + if (ekg::p_core == nullptr) { + return; + } + + ekg::p_core->handler_input.poll_event(); - ekg::input_t &input { - this->service_input.input + ekg::input_info_t &input { + ekg::p_core->handler_input.input }; - bool is_on_scrolling_timeout {!ekg::reach(&input.ui_scrolling_timing, 100)}; - ekg::current.unique_id *= !(input.was_pressed || input.was_released || input.has_motion); + bool is_on_scrolling_timeout {!ekg::reach(input.ui_scrolling_timing, 100)}; + ekg::gui.ui.hovered_at = ( + (input.was_pressed || input.was_released || input.has_motion) + ? ekg::at_t::not_found : ekg::gui.ui.hovered_at + ); + ekg::property_t &abs_widget {ekg::query(ekg::gui.ui.abs_widget_at)}; if ( - this->p_abs_activity_widget != nullptr + abs_widget != ekg::property_t::not_found && - (this->p_abs_activity_widget->states.is_absolute || is_on_scrolling_timeout) + (abs_widget.widget.is_absolute || is_on_scrolling_timeout) ) { - this->p_abs_activity_widget->on_event(ekg::io::stage::pre); - this->p_abs_activity_widget->on_event(ekg::io::stage::process); + ekg_abstract_todo( + abs_widget.descriptor_at.flags, + abs_widget.descriptor_at, - if ( - this->p_abs_activity_widget->states.is_scrolling.x + ekg::ui::event(abs_widget, descriptor, ekg::io::stage::pre); + ekg::ui::event(abs_widget, descriptor, ekg::io::stage::process); + + if ( + abs_widget.scroll.is_scrolling.x || - this->p_abs_activity_widget->states.is_scrolling.y + abs_widget.scroll.is_scrolling.y ) { - ekg::reset(&input.ui_scrolling_timing); - } + ekg::reset(input.ui_scrolling_timing); + } + + ekg::ui::event(abs_widget, descriptor, ekg::io::stage::post); + ); - this->p_abs_activity_widget->on_event(ekg::io::stage::post); return; } @@ -261,276 +254,122 @@ void ekg::runtime::poll_events() { return; } - this->p_abs_activity_widget = nullptr; + ekg::gui.ui.abs_widget_at = ekg::at_t::not_found; bool hovered {}; bool first_absolute {}; - ekg::ui::abstract *p_widget_focused {}; - for (ekg::ui::abstract *&p_widgets : this->context_widget_list) { - if (p_widgets == nullptr || !p_widgets->properties.is_alive) { + ekg::at_t focused_at {ekg::at_t::not_found}; + for (ekg::at_t &at : ekg::p_core->stack) { + ekg::property_t &property { + ekg::query(at) + }; + + if (property == ekg::property_t::not_found) { continue; } - p_widgets->on_event(ekg::io::stage::pre); + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, - /** - * Text input like textbox and keyboard events should not update stack, only mouse events. - **/ - hovered = ( - !( - this->p_os_platform->serialized_input_event.type == ekg::io::input_event_type::key_down - || - this->p_os_platform->serialized_input_event.type == ekg::io::input_event_type::key_up - || - this->p_os_platform->serialized_input_event.type == ekg::io::input_event_type::text_input - ) - && - p_widgets->states.is_hovering - && - p_widgets->properties.is_visible - && - p_widgets->properties.is_enabled - ); + ekg::ui::event(property, descriptor, ekg::io::stage::pre); - if (hovered) { - p_widget_focused && (p_widget_focused->states.is_hovering = false); - p_widget_focused = p_widgets; - first_absolute = false; - } + hovered = ( + !( + ekg::p_core->p_platform_base->event.type == ekg::io::event_type::key_down + || + ekg::p_core->p_platform_base->event.type == ekg::io::event_type::key_up + || + ekg::p_core->p_platform_base->event.type == ekg::io::event_type::text_input + ) + && + property.widget.is_hovering + && + property.widget.is_visible + && + property.widget.is_enabled + ); - /** - * The absolute/top-level system check for the first absolute fired widget, - * everytime a widget is hovered then reset again the checking state. - * - * The order of scrollable widgets like scroll widget is not sequentially, - * e.g, the mouse is hovering some children of frame 2 and absolute widget scroll from frame 2 is fired: - * - * frame 1 // hovered, check for the first absolute - * - * frame 2 (frame 1) // hovered, then reset and find for the first absolute again - * widgets... // hovering some of children widgets, then reset over again - * scroll (frame 2) // found the first absolute, target it - * - * frame 3 (frame 1) // not hovering, then does not reset any absolute checking - * ... - * - * scroll (frame 1) // do not target this fired absolute widget. - * end of e.g. - **/ - if (p_widgets->states.is_absolute && !first_absolute) { - p_widget_focused = p_widgets; - first_absolute = true; - } + if (hovered) { + ekg::property_t &focused_property { + ekg::query(focused_at) + }; - p_widgets->on_event(ekg::io::stage::post); - if (!hovered && !p_widgets->states.is_absolute) { - p_widgets->states.is_hovering = false; - p_widgets->on_event(ekg::io::stage::process); - } + focused_property != ekg::property_t::not_found + && (focused_property.widget.is_hovering = false); + + focused_at = at; + first_absolute = false; + } + + if (property.widget.is_absolute && !first_absolute) { + focused_at = at; + first_absolute = true; + } + + ekg::ui::event(property, descriptor, ekg::io::stage::post); + if (!hovered && !property.widget.is_absolute) { + ekg::ui::event(property, descriptor, ekg::io::stage::process); + } + ); } - ekg::current.type = ekg::type::abstract; + ekg::gui.ui.hovered_type = ekg::type::unknown; + + ekg::property_t &focused_property { + ekg::query(focused_at) + }; + + if (focused_property != ekg::property_t::not_found) { + ekg_abstract_todo( + focused_at.flags, + focused_at, - if (p_widget_focused) { - p_widget_focused->states.is_absolute && (this->p_abs_activity_widget = p_widget_focused); - ekg::current.type = p_widget_focused->properties.type; - ekg::current.unique_id = p_widget_focused->properties.unique_id; + ekg::gui.ui.hovered_type = static_cast(focused_at.flags); + ekg::gui.ui.hovered_at = focused_at; - p_widget_focused->on_event(ekg::io::stage::pre); - p_widget_focused->on_event(ekg::io::stage::process); - p_widget_focused->on_event(ekg::io::stage::post); + if (focused_property.widget.is_absolute) { + ekg::gui.ui.abs_widget_at = focused_at; + } + + ekg::ui::event(focused_property, descriptor, ekg::io::stage::pre); + ekg::ui::event(focused_property, descriptor, ekg::io::stage::process); + ekg::ui::event(focused_property, descriptor, ekg::io::stage::post); + ); } + // @TODO: ref this later + if (input.was_pressed) { - ekg::current.pressed = ekg::current.unique_id; + ekg::gui.ui.pressed_at = ekg::gui.ui.hovered_at; ( - // if p_widget_focused != nullptr ? p_widget_focused->properties.type : ekg::type::abstract - (p_widget_focused && (ekg::current.pressed_type = p_widget_focused->properties.type)) + // if p_widget_focused != nullptr ? focused_at.flags : ekg::type::unknown + (focused_property != ekg::property_t::not_found && (ekg::gui.ui.pressed_type = static_cast(focused_at.flags))) || - (ekg::current.pressed_type = ekg::type::abstract) + (ekg::gui.ui.pressed_type = ekg::type::unknown) ); } else if (input.was_released) { - ekg::current.released = ekg::current.unique_id; + ekg::gui.ui.released_at = ekg::gui.ui.hovered_at; ( - (p_widget_focused && (ekg::current.released_type = p_widget_focused->properties.type)) + (focused_property != ekg::property_t::not_found && (ekg::gui.ui.released_type = static_cast(focused_at.flags))) || - (ekg::current.released_type = ekg::type::abstract) + (ekg::gui.ui.released_type = ekg::type::unknown) ); } if ( - ekg::current.last != ekg::current.unique_id + ekg::gui.ui.last_hovered_at != ekg::gui.ui.hovered_at && - ekg::current.unique_id != ekg::io::invalid_unique_id + ekg::gui.ui.hovered_at != ekg::at_t::not_found && ( input.was_pressed || input.was_released ) ) { - this->swap_target_collector.unique_id = ekg::current.unique_id; - ekg::current.last = ekg::current.unique_id; - + ekg::gui.bind.swap_at = ekg::gui.ui.hovered_at; + ekg::gui.ui.last_hovered_at = ekg::gui.ui.hovered_at; + ekg::gui.ui.redraw = true; ekg::io::dispatch(ekg::io::operation::swap); - ekg::viewport.redraw = true; } } - -void ekg::runtime::update() { - if (!this->high_frequency_widget_list.empty()) { - size_t size {}; - for (size_t it {}; it < (size = this->high_frequency_widget_list.size()); it++) { - if (it >= size) { - break; - } - - ekg::ui::abstract *&p_widget {this->high_frequency_widget_list.at(it)}; - p_widget->on_update(); - - if (!p_widget->states.is_high_frequency) { - this->high_frequency_widget_list.erase( - this->high_frequency_widget_list.begin() + it - ); - } - } - } - - this->service_input.on_update(); - this->service_handler.on_update(); - - ekg::log::flush(); -} - -void ekg::runtime::render() { - if (ekg::viewport.redraw) { - ekg::viewport.redraw = false; - - /** - * The allocator starts here, the GPU data instance - * and geometry resources are clear/reseted here. - **/ - this->gpu_allocator.invoke(); - - for (ekg::ui::abstract *&p_widgets : this->context_widget_list) { - if (p_widgets != nullptr && p_widgets->properties.is_alive && p_widgets->properties.is_visible) { - /** - * Each time this statement is called, one GPU data is - * allocated/filled. - * - * The order of rendering depends on which are functions are invoked first. - * - * E.g: - * gpu-data-group-1 (on_draw()) - * gpu-data-group-2 - * gpu-data-group-3 - * - * `gpu-data-group-3` is always hovering all the previous GPU data groups. - **/ - p_widgets->on_draw(); - } - } - - /** - * The allocator does not need to be called all the time, - * cause it is require more CPU-side calls and GPU-communication/synchronization. - * - * The use of the word "revoke" means that the `invoked signal` ended with a - * possible `revoke`, i.e all the generated/populated/filled GPU-data is processed and - * converted to a geometry GPU-buffer. Removing all the unnecessary GPU-data. - **/ - this->gpu_allocator.revoke(); - } - - /** - * The allocator renderize an serialized GPU data-list. - * With some conditions: - * - * 1- A concave shape does not have a rectangle form, and can render text(s) and - * batched quad(s) at once. - * 2- A convex shape have a unique form: rectangle. - * - * The convex form is rendered one per one draw call. It is stupid I know, but with this is possible add cool effects. - * While concave have the possibility of rendering a lot of shapes at once, is not viable to add many custom effects. - * - * The native GPU API calls are apart from allocator, depends on current GPU API selected. - * Rendering-hardware interface also known as RHI implements different each GPU API. - * OpenGL 3/4/ES 3, Vulkan, MoltenVK, DirectX11/12. - * May differ the features or runtime. - * - * But check all allocator comments. - **/ - this->gpu_allocator.draw(); -} - -void ekg::runtime::set_current_parent_properties(ekg::properties_t *p_properties) { - this->p_current_parent_properties = p_properties; -} - -ekg::properties_t *ekg::runtime::get_current_parent_properties() { - return this->p_current_parent_properties; -} - -ekg::ui::abstract *ekg::runtime::emplace_back_new_widget_safety( - ekg::ui::abstract *p_widget -) { - return this->context_widget_list.emplace_back() = - ( - this->loaded_widget_list.emplace_back( - std::unique_ptr(p_widget) - ).get() - ); -} - -ekg::id_t ekg::runtime::generate_unique_id() { - return ++this->global_id; -} - -void ekg::runtime::dispatch_widget_op( - ekg::ui::abstract *p_widget, - ekg::io::operation op -) { - if (p_widget == nullptr) { - return; - } - - bool is_cancelled {}; - switch (op) { - case ekg::io::operation::swap: - this->swap_target_collector.unique_id = p_widget->properties.unique_id; - break; - case ekg::io::operation::reload: - if ( - !(is_cancelled = p_widget->states.was_reloaded) - ) { - this->reload_widget_list.push_back(p_widget); - p_widget->states.was_reloaded = true; - } - break; - case ekg::io::operation::layout_docknize: - if ( - !(is_cancelled = p_widget->states.was_layout_docknized) - ) { - this->layout_docknize_list.push_back(p_widget); - p_widget->states.was_layout_docknized = true; - } - break; - case ekg::io::operation::high_frequency: - if ( - !p_widget->states.is_high_frequency - ) { - this->high_frequency_widget_list.push_back(p_widget); - p_widget->states.is_high_frequency = true; - } - - is_cancelled = true; - break; - } - - if (is_cancelled) { - return; - } - - this->service_handler.dispatch_pre_allocated_task( - static_cast(op) - ); -} diff --git a/src/draw/allocator.cpp b/src/draw/allocator.cpp new file mode 100644 index 00000000..332184ad --- /dev/null +++ b/src/draw/allocator.cpp @@ -0,0 +1,280 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ekg/draw/allocator.hpp" +#include "ekg/core/runtime.hpp" +#include "ekg/io/log.hpp" +#include "ekg/core/context.hpp" + +bool ekg::draw::allocator::enable_high_priority {}; +bool ekg::draw::allocator::is_simple_shape {}; + +void ekg::draw::allocator::init() { + ekg::log() << "Initializing GPU allocator"; +} + +void ekg::draw::allocator::quit() { + ekg::log() << "Quitting GPU allocator"; +} + +void ekg::draw::allocator::invoke() { + this->data_instance = 0; + this->stride_instance.x = 0; + this->stride_instance.y = 0; + this->simple_shape_instance = 0; + this->geometry_instance = 0; + + /** + * inserting a simple triangle mesh, + * is necessary to make work the simple-shape rendering. + **/ + this->push_back_geometry(0.0f, 0.0f, 0.0f, 0.0f); + this->push_back_geometry(0.0f, 1.0f, 0.0f, 1.0f); + this->push_back_geometry(1.0f, 0.0f, 1.0f, 0.0f); + this->push_back_geometry(1.0f, 1.0f, 1.0f, 1.0f); + + if (this->data_instance >= this->gpu_data_buffer.size()) { + this->gpu_data_buffer.emplace_back(); + } + + this->stride_instance.x += this->stride_instance.y; + this->stride_instance.y = 0; +} + +void ekg::draw::allocator::bind_texture(ekg::sampler_t &sampler) { + if (sampler == ekg::sampler_t::not_found) { + return; + } + + ekg::gpu::data_t &data {this->gpu_data_buffer.at(this->data_instance)}; + data.sampler_at = ekg::p_core->p_gpu_api->bind_sampler(sampler); +} + +void ekg::draw::allocator::dispatch() { + ekg::gpu::data_t *p_data {/* stupid */}; + + if (ekg::draw::allocator::enable_high_priority) { + this->high_priority_gpu_data_buffer.push_back(this->gpu_data_buffer.at(this->data_instance)); + + p_data = ( + &this->high_priority_gpu_data_buffer.at(this->high_priority_data_instance) + ); + + this->data_instance -= this->data_instance > 0; + this->high_priority_data_instance++; + } else { + p_data = &this->gpu_data_buffer.at(this->data_instance); + } + + /** + * Scissor must be externally synchned. + **/ + p_data->buffer[8] = this->scissor_instance.x; + p_data->buffer[9] = this->scissor_instance.y; + p_data->buffer[10] = this->scissor_instance.w; + p_data->buffer[11] = this->scissor_instance.h; + + /** + * the point of re-using a simple shape stride makes performance a little better, + * due the index rendering, with only one triangle for rectangles. + **/ + + if (ekg::draw::allocator::is_simple_shape) { + this->stride_instance.y = 0; + + /** + * Simple shade contains only 4 vertices because it is indexed-rendered. + **/ + p_data->begin_stride = this->simple_shape_instance; + p_data->end_stride = 4; + } else { + /** + * Peek `ekg/gpu/gl/shaders.cpp`. + * Any value less than -1.0 is considered a concave by the vertex shader. + **/ + p_data->buffer[3] = -1.1f; + + p_data->begin_stride = this->stride_instance.x; + p_data->end_stride = this->stride_instance.y; + } + + if (!this->was_hash_changed) { + this->was_hash_changed = ( + this->previous_hash != p_data->hash + ); + } + + this->stride_instance.x += this->stride_instance.y; + this->stride_instance.y = 0; + this->data_instance++; +} + +void ekg::draw::allocator::revoke() { + this->data_instance -= this->data_instance > 0; + + if (!this->high_priority_gpu_data_buffer.empty()) { + uint64_t high_priority_gpu_data_buffer_size { + this->high_priority_gpu_data_buffer.size() + }; + + this->gpu_data_buffer.insert( + this->gpu_data_buffer.begin() + this->data_instance + 1, + this->high_priority_gpu_data_buffer.begin(), + this->high_priority_gpu_data_buffer.end() + ); + + this->data_instance += high_priority_gpu_data_buffer_size; + this->high_priority_gpu_data_buffer.clear(); + this->high_priority_data_instance = 0; + } + + if ( + this->last_geometry_buffer_size != this->geometry_instance + || + this->was_hash_changed + ) { + ekg::p_core->p_gpu_api->pass_geometry_buffer_to_gpu( + this->geometry_buffer.data(), + this->geometry_buffer.size() + ); + } + + this->last_geometry_buffer_size = this->geometry_instance; + this->was_hash_changed = false; + ekg::metrics.gpu_data_count = this->data_instance; +} + +void ekg::draw::allocator::to_gpu() { + ekg::p_core->p_gpu_api->pass_gpu_data_buffer_to_gpu( + this->gpu_data_buffer + ); +} + +void ekg::draw::allocator::clear_current_data() { + if (!ekg::draw::allocator::enable_high_priority && this->data_instance >= this->gpu_data_buffer.size()) { + this->gpu_data_buffer.emplace_back(); + } + + /* allocator handle automatically the size of data */ + ekg::gpu::data_t &data {this->gpu_data_buffer.at(this->data_instance)}; + data.line_thickness = 0; + data.sampler_at = ekg::sampler_t::not_found; + + this->previous_hash = data.hash; +} + +ekg::gpu::data_t &ekg::draw::allocator::bind_current_data() { + this->clear_current_data(); + return this->gpu_data_buffer.at(this->data_instance); +} + +size_t ekg::draw::allocator::get_current_data_id() { + return this->data_instance; +} + +ekg::gpu::data_t &ekg::draw::allocator::get_data_by_index(size_t index) { + if (index >= this->gpu_data_buffer.size()) { + return ekg::gpu::data_t::not_found; + } + + return this->gpu_data_buffer[index]; +} + +bool ekg::draw::allocator::sync_scissor( + ekg::rect_t &rect_scissor, + ekg::rect_t &rect_child, + ekg::rect_t &rect_parent_scissor, + bool is_parented +) { + rect_scissor.x = rect_child.x; + rect_scissor.y = rect_child.y; + rect_scissor.w = rect_child.w; + rect_scissor.h = rect_child.h; + + if (is_parented) { + bool only_if {}; + + only_if = rect_scissor.x < rect_parent_scissor.x; + rect_scissor.w -= only_if * (rect_parent_scissor.x - rect_scissor.x); + rect_scissor.x = ( + (only_if * rect_parent_scissor.x) + (rect_scissor.x * !only_if) + ); + + only_if = rect_scissor.y < rect_parent_scissor.y; + rect_scissor.h -= only_if * (rect_parent_scissor.y - rect_scissor.y); + rect_scissor.y = (only_if * rect_parent_scissor.y) + (rect_scissor.y * !only_if); + + only_if = rect_scissor.x + rect_scissor.w > rect_parent_scissor.x + rect_parent_scissor.w; + rect_scissor.w -= only_if * ((rect_scissor.x + rect_scissor.w) - (rect_parent_scissor.x + rect_parent_scissor.w)); + + only_if = rect_scissor.y + rect_scissor.h > rect_parent_scissor.y + rect_parent_scissor.h; + rect_scissor.h -= only_if * ((rect_scissor.y + rect_scissor.h) - (rect_parent_scissor.y + rect_parent_scissor.h)); + + this->scissor_instance.x = rect_scissor.x; + this->scissor_instance.y = rect_scissor.y; + this->scissor_instance.w = rect_scissor.w; + this->scissor_instance.h = rect_scissor.h; + + return ( + rect_scissor.x < rect_parent_scissor.x + rect_parent_scissor.w + && + rect_scissor.x + rect_scissor.w > rect_parent_scissor.x + && + rect_scissor.y < rect_parent_scissor.y + rect_parent_scissor.h + && + rect_scissor.y + rect_scissor.h > rect_parent_scissor.y + ); + } + + this->scissor_instance.x = rect_scissor.x; + this->scissor_instance.y = rect_scissor.y; + this->scissor_instance.w = rect_scissor.w; + this->scissor_instance.h = rect_scissor.h; + + return true; +} + +void ekg::draw::allocator::unsafe_set_scissor_rect( + float x, + float y, + float w, + float h +) { + this->scissor_instance.x = x; + this->scissor_instance.y = y; + this->scissor_instance.w = w; + this->scissor_instance.h = h; +} + +void ekg::draw::allocator::push_back_geometry( + float x, float y, + float u, float v +) { + this->stride_instance.y++; + this->geometry_buffer.push_back(x); + this->geometry_buffer.push_back(y); + this->geometry_buffer.push_back(u); + this->geometry_buffer.push_back(v); + this->geometry_instance += 4; +} diff --git a/src/draw/shape.cpp b/src/draw/shape/shape.cpp similarity index 56% rename from src/draw/shape.cpp rename to src/draw/shape/shape.cpp index b0721c9e..e00839e6 100644 --- a/src/draw/shape.cpp +++ b/src/draw/shape/shape.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,66 +22,66 @@ * SOFTWARE. */ -#include "ekg/draw/shape.hpp" +#include "ekg/draw/shape/shape.hpp" #include "ekg/core/runtime.hpp" void ekg::draw::rect( const ekg::rect_t &rect, - const ekg::vec4_t &color, - int32_t line_thickness, - ekg::sampler_t *p_sampler + const ekg::rgba_t &color, + ekg::pixel_thickness_t line_thickness, + ekg::sampler_t &sampler ) { ekg::draw::rect( rect.x, rect.y, rect.w, rect.h, color, line_thickness, - p_sampler + sampler ); } void ekg::draw::rect( float x, float y, float w, float h, - const ekg::vec4_t &color, - int32_t line_thickness, - ekg::sampler_t *p_sampler + const ekg::rgba_t &color, + ekg::pixel_thickness_t line_thickness, + ekg::sampler_t &sampler ) { - if (color.w < 0.1f) { + if (color.w < 10) { return; } - ekg::io::gpu_data_t &data {ekg::p_core->gpu_allocator.bind_current_data()}; + ekg::gpu::data_t &data { + ekg::p_core->draw_allocator.bind_current_data() + }; - data.buffer_content[0] = x; - data.buffer_content[1] = y; - data.buffer_content[2] = w; - data.buffer_content[3] = h; - data.buffer_content[4] = color.x; - data.buffer_content[5] = color.y; - data.buffer_content[6] = color.z; - data.buffer_content[7] = color.w; + data.buffer[0] = x; + data.buffer[1] = y; + data.buffer[2] = w; + data.buffer[3] = h; + data.buffer[4] = static_cast(color.x) / 255; + data.buffer[5] = static_cast(color.y) / 255; + data.buffer[6] = static_cast(color.z) / 255; + data.buffer[7] = static_cast(color.w) / 255; data.line_thickness = static_cast(line_thickness); - data.factor = 1; + data.hash = 1; - ekg::p_core->gpu_allocator.bind_texture(p_sampler); - ekg::p_core->gpu_allocator.dispatch(); + ekg::draw::allocator::is_simple_shape = true; + ekg::p_core->draw_allocator.bind_texture(sampler); + ekg::p_core->draw_allocator.dispatch(); } void ekg::draw::scissor( - float x, - float y, - float w, - float h + const ekg::rect_t &rect ) { - ekg::p_core->gpu_allocator.unsafe_set_scissor_placement( - x, y, w, h + ekg::p_core->draw_allocator.unsafe_set_scissor_rect( + rect.x, rect.y, rect.w, rect.h ); } -void ekg::draw::enable_high_priority() { - ekg::gpu::allocator::high_priority = true; -} - -void ekg::draw::disable_high_priority() { - ekg::gpu::allocator::high_priority = false; +void ekg::draw::scissor( + float x, float y, float w, float h +) { + ekg::p_core->draw_allocator.unsafe_set_scissor_rect( + x, y, w, h + ); } diff --git a/src/draw/font_renderer.cpp b/src/draw/typography/font.cpp similarity index 67% rename from src/draw/font_renderer.cpp rename to src/draw/typography/font.cpp index d64f78b3..0b2348f0 100644 --- a/src/draw/font_renderer.cpp +++ b/src/draw/typography/font.cpp @@ -1,7 +1,9 @@ +#include "ekg/draw/typography/font.hpp" + /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,16 +24,57 @@ * SOFTWARE. */ -#include "ekg/draw/font_renderer.hpp" -#include "ekg/io/text.hpp" +#include "ekg/draw/typography/font.hpp" +#include "ekg/io/utf.hpp" #include "ekg/core/runtime.hpp" #include "ekg/core/context.hpp" +#include "ekg/io/log.hpp" +#include "ekg/core/pools.hpp" + +void ekg::draw::font::init() { + if (this->was_initialized) { + return; + } + + this->was_initialized = true; + + this->new_glyphs_to_atlas.resize(256); + for (char32_t char32 {}; char32 < 256; char32++) { + this->new_glyphs_to_atlas.at(char32) = char32; + } + + ekg::log() << "Initializing 256 default chars!"; +} + +void ekg::draw::font::quit() { + ekg::io::font_face_t &text_font_face {this->faces[ekg::io::font_face_type::text]}; + ekg::io::font_face_t &emojis_font_face {this->faces[ekg::io::font_face_type::emojis]}; + ekg::io::font_face_t &kanjis_font_face {this->faces[ekg::io::font_face_type::kanjis]}; + + if (text_font_face.was_loaded) { + FT_Done_Face(text_font_face.ft_face); + text_font_face.was_loaded = false; + } + + if (emojis_font_face.was_loaded) { + FT_Done_Face(emojis_font_face.ft_face); + emojis_font_face.was_loaded = false; + } -ekg::sampler_t *ekg::draw::font_renderer::get_atlas_texture_sampler() { - return &this->atlas_texture_sampler; + if (kanjis_font_face.was_loaded) { + FT_Done_Face(kanjis_font_face.ft_face); + kanjis_font_face.was_loaded = false; + } } -float ekg::draw::font_renderer::get_text_width(std::string_view text, int32_t &lines) { +ekg::sampler_t &ekg::draw::font::get_atlas_texture_sampler() { + return ekg::query(this->atlas_texture_sampler_at); +} + +float ekg::draw::font::get_text_width( + const std::string_view &text, + int32_t &lines +) { if ( text.empty() || @@ -64,7 +107,7 @@ float ekg::draw::font_renderer::get_text_width(std::string_view text, int32_t &l for (uint64_t it {}; it < text_size; it++) { char8 = static_cast(text.at(it)); - it += ekg::utf_check_sequence(char8, char32, utf_string, text, it); + it += ekg::utf8_check_sequence(char8, char32, utf_string, text, it); break_text = char8 == '\n'; if (break_text || (r_n_break_text = (char8 == '\r' && it < text_size && text.at(it + 1) == '\n'))) { it += static_cast(r_n_break_text); @@ -93,9 +136,9 @@ float ekg::draw::font_renderer::get_text_width(std::string_view text, int32_t &l text_width += static_cast(ft_vector_previous_char.x >> 6); } - ekg::io::glyph_char_t &char_data {this->mapped_glyph_char_data[char32]}; + ekg::io::glyph_t &glyph {this->mapped_glyph[char32]}; - if (!char_data.was_sampled) { + if (!glyph.was_sampled) { if ( FT_Load_Char( ft_face, @@ -106,13 +149,13 @@ float ekg::draw::font_renderer::get_text_width(std::string_view text, int32_t &l continue; } - char_data.wsize = static_cast(static_cast(ft_glyph_slot->advance.x >> 6)); - this->loaded_sampler_generate_list.emplace_back(char32); - char_data.was_sampled = true; + glyph.wsize = static_cast(static_cast(ft_glyph_slot->advance.x >> 6)); + this->new_glyphs_to_atlas.emplace_back(char32); + glyph.was_sampled = true; } ft_uint_previous = char32; - text_width += this->mapped_glyph_char_data[char32].wsize; + text_width += this->mapped_glyph[char32].wsize; } lines = ekg::clamp_min(lines, lines_count); @@ -121,7 +164,7 @@ float ekg::draw::font_renderer::get_text_width(std::string_view text, int32_t &l return largest_text_width; } -float ekg::draw::font_renderer::get_text_width(std::string_view text) { +float ekg::draw::font::get_text_width(const std::string_view &text) { if ( text.empty() || @@ -152,7 +195,7 @@ float ekg::draw::font_renderer::get_text_width(std::string_view text) { for (uint64_t it {}; it < text_size; it++) { char8 = static_cast(text.at(it)); - it += ekg::utf_check_sequence(char8, char32, utf_string, text, it); + it += ekg::utf8_check_sequence(char8, char32, utf_string, text, it); break_text = char8 == '\n'; if (break_text || (r_n_break_text = (char8 == '\r' && it < text_size && text.at(it + 1) == '\n'))) { @@ -181,9 +224,9 @@ float ekg::draw::font_renderer::get_text_width(std::string_view text) { text_width += static_cast(ft_vector_previous_char.x >> 6); } - ekg::io::glyph_char_t &char_data {this->mapped_glyph_char_data[char32]}; + ekg::io::glyph_t &glyph {this->mapped_glyph[char32]}; - if (!char_data.was_sampled) { + if (!glyph.was_sampled) { if ( FT_Load_Char( ft_face, @@ -194,24 +237,24 @@ float ekg::draw::font_renderer::get_text_width(std::string_view text) { continue; } - char_data.wsize = static_cast(static_cast(ft_glyph_slot->advance.x >> 6)); - this->loaded_sampler_generate_list.emplace_back(char32); - char_data.was_sampled = true; + glyph.wsize = static_cast(static_cast(ft_glyph_slot->advance.x >> 6)); + this->new_glyphs_to_atlas.emplace_back(char32); + glyph.was_sampled = true; } ft_uint_previous = char32; - text_width += char_data.wsize; + text_width += glyph.wsize; } largest_text_width = ekg::clamp_min(largest_text_width, text_width); return largest_text_width; } -float ekg::draw::font_renderer::get_text_height() { +float ekg::draw::font::get_text_height() { return this->text_height; } -void ekg::draw::font_renderer::set_font(std::string_view path) { +void ekg::draw::font::set_font(const std::string_view &path) { ekg::io::font_face_t &font_face { this->faces[ekg::io::font_face_type::text] }; @@ -225,7 +268,7 @@ void ekg::draw::font_renderer::set_font(std::string_view path) { } } -void ekg::draw::font_renderer::set_font_emoji(std::string_view path) { +void ekg::draw::font::set_font_emoji(const std::string_view &path) { ekg::io::font_face_t &font_face { this->faces[ekg::io::font_face_type::emojis] }; @@ -239,9 +282,9 @@ void ekg::draw::font_renderer::set_font_emoji(std::string_view path) { } } -void ekg::draw::font_renderer::set_size(uint32_t size) { +void ekg::draw::font::set_size(uint32_t size) { if (this->font_size != size) { - for (size_t it {}; it < ekg::io::supported_faces_size; it++) { + for (size_t it {}; it < ekg::io::enum_font_face_type_size; it++) { ekg::io::font_face_t &font_face { this->faces[it] }; @@ -255,21 +298,26 @@ void ekg::draw::font_renderer::set_size(uint32_t size) { } } -void ekg::draw::font_renderer::reload() { +void ekg::draw::font::reload() { if (this->font_size == 0) { return; } + if (this->atlas_texture_sampler_at == ekg::at_t::not_found) { + ekg::sampler_t &sampler {ekg::make({})}; + this->atlas_texture_sampler_at = sampler.at; + } + size_t functional_fonts {}; ekg::flags_t flags {}; - for (size_t it {}; it < ekg::io::supported_faces_size; it++) { + for (size_t it {}; it < ekg::io::enum_font_face_type_size; it++) { ekg::io::font_face_t &font_face { this->faces[it] }; - flags = ekg::io::refresh_font_face( - &font_face + flags = ekg::io::font( + font_face ); if (ekg::has(flags, ekg::result::success)) { @@ -304,7 +352,7 @@ void ekg::draw::font_renderer::reload() { FT_Face ft_face {}; ekg::io::font_face_t *p_font_face_picked {}; - for (char32_t &char32 : this->loaded_sampler_generate_list) { + for (char32_t &char32 : this->new_glyphs_to_atlas) { switch (char32 < 256 || !emojis_font_face.was_loaded) { case true: { ft_face = text_font_face.ft_face; @@ -327,28 +375,28 @@ void ekg::draw::font_renderer::reload() { continue; } - ekg::io::glyph_char_t &char_data { - this->mapped_glyph_char_data[char32] + ekg::io::glyph_t &glyph { + this->mapped_glyph[char32] }; - char_data.w = static_cast(ft_glyph_slot->bitmap.width); - char_data.h = static_cast(ft_glyph_slot->bitmap.rows); + glyph.w = static_cast(ft_glyph_slot->bitmap.width); + glyph.h = static_cast(ft_glyph_slot->bitmap.rows); - char_data.left = static_cast(ft_glyph_slot->bitmap_left); - char_data.top = static_cast(ft_glyph_slot->bitmap_top); - char_data.wsize = static_cast(static_cast(ft_glyph_slot->advance.x >> 6)); + glyph.left = static_cast(ft_glyph_slot->bitmap_left); + glyph.top = static_cast(ft_glyph_slot->bitmap_top); + glyph.wsize = static_cast(static_cast(ft_glyph_slot->advance.x >> 6)); - this->atlas_rect.w += static_cast(char_data.w); - this->atlas_rect.h = ekg::clamp_min(this->atlas_rect.h, static_cast(char_data.h)); + this->atlas_rect.w += static_cast(glyph.w); + this->atlas_rect.h = ekg::clamp_min(this->atlas_rect.h, static_cast(glyph.h)); p_font_face_picked->highest_glyph_size.x = ekg::clamp_min( static_cast(p_font_face_picked->highest_glyph_size.x), - static_cast(char_data.w) + static_cast(glyph.w) ); p_font_face_picked->highest_glyph_size.y = ekg::clamp_min( static_cast(p_font_face_picked->highest_glyph_size.y), - static_cast(char_data.h) + static_cast(glyph.h) ); } @@ -356,35 +404,29 @@ void ekg::draw::font_renderer::reload() { this->offset_text_height = static_cast(this->text_height / 6) / 2; ekg::p_core->p_gpu_api->gen_font_atlas_and_map_glyph( - &this->atlas_texture_sampler, + ekg::query(this->atlas_texture_sampler_at), &text_font_face, &emojis_font_face, - nullptr, // must impl kanjis + nullptr, // must impl kanjis wmwmwm this->atlas_rect, - this->loaded_sampler_generate_list, - this->mapped_glyph_char_data, + this->new_glyphs_to_atlas, + this->mapped_glyph, this->non_swizzlable_range ); } -void ekg::draw::font_renderer::bind_allocator(ekg::gpu::allocator *p_allocator_bind) { - this->p_allocator = p_allocator_bind; -} - -void ekg::draw::font_renderer::blit( - std::string_view text, +void ekg::draw::font::blit( + const std::string_view &text, float x, float y, - const ekg::vec4_t &color + const ekg::rgba_t &color ) { if ( - this->p_allocator == nullptr + !this->is_any_functional_font_face_loaded || color.w < 0.1f || text.empty() - || - !this->is_any_functional_font_face_loaded ) { return; } @@ -392,25 +434,24 @@ void ekg::draw::font_renderer::blit( x = static_cast(static_cast(x)); y = static_cast(static_cast(y - this->offset_text_height)); - ekg::io::gpu_data_t &data {this->p_allocator->bind_current_data()}; + ekg::gpu::data_t &data {ekg::p_core->draw_allocator.bind_current_data()}; - data.buffer_content[0] = x; - data.buffer_content[1] = y; - data.buffer_content[2] = static_cast(-this->non_swizzlable_range); - data.buffer_content[3] = static_cast(ekg::gpu::allocator::concave); + data.buffer[0] = x; + data.buffer[1] = y; + data.buffer[2] = static_cast(-this->non_swizzlable_range); - data.buffer_content[4] = color.x; - data.buffer_content[5] = color.y; - data.buffer_content[6] = color.z; - data.buffer_content[7] = color.w; + data.buffer[4] = static_cast(color.x / 255); + data.buffer[5] = static_cast(color.y / 255); + data.buffer[6] = static_cast(color.z / 255); + data.buffer[7] = static_cast(color.w / 255); - ekg::rect_t vertices {}; - ekg::rect_t coordinates {}; + ekg::rect_t vertex {}; + ekg::rect_t uv {}; x = 0.0f; y = 0.0f; - data.factor = 1; + data.hash = 1; char32_t char32 {}; uint8_t char8 {}; @@ -430,7 +471,7 @@ void ekg::draw::font_renderer::blit( for (uint64_t it {}; it < text_size; it++) { char8 = static_cast(text.at(it)); - it += ekg::utf_check_sequence(char8, char32, utf_string, text, it); + it += ekg::utf8_check_sequence(char8, char32, utf_string, text, it); break_text = char8 == '\n'; if ( @@ -442,10 +483,11 @@ void ekg::draw::font_renderer::blit( ) ) ) { - ekg::io::glyph_char_t &char_data {this->mapped_glyph_char_data[char32]}; + + ekg::io::glyph_t &glyph {this->mapped_glyph[char32]}; it += static_cast(r_n_break_text); - data.factor += ekg::draw::generate_factor_hash(y, char32, char_data.x); + data.hash += ekg_generate_hash(y, char32, glyph.x); y += this->text_height; x = 0.0f; @@ -469,133 +511,101 @@ void ekg::draw::font_renderer::blit( x += static_cast(ft_vector_previous_char.x >> 6); } - ekg::io::glyph_char_t &char_data {this->mapped_glyph_char_data[char32]}; + ekg::io::glyph_t &glyph {this->mapped_glyph[char32]}; - if (!char_data.was_sampled) { - this->loaded_sampler_generate_list.emplace_back(char32); - char_data.was_sampled = true; + if (!glyph.was_sampled) { + this->new_glyphs_to_atlas.emplace_back(char32); + glyph.was_sampled = true; } - vertices.x = x + char_data.left; - vertices.y = y + this->atlas_rect.h - char_data.top; + vertex.x = x + glyph.left; + vertex.y = y + this->atlas_rect.h - glyph.top; - vertices.w = char_data.w; - vertices.h = char_data.h; + vertex.w = glyph.w; + vertex.h = glyph.h; - coordinates.x = char_data.x; - coordinates.w = vertices.w / this->atlas_rect.w; - coordinates.h = vertices.h / this->atlas_rect.h; + uv.x = glyph.x; + uv.w = vertex.w / this->atlas_rect.w; + uv.h = vertex.h / this->atlas_rect.h; - this->p_allocator->push_back_geometry( - vertices.x, - vertices.y, - coordinates.x, - coordinates.y + ekg::p_core->draw_allocator.push_back_geometry( + vertex.x, + vertex.y, + uv.x, + uv.y ); - this->p_allocator->push_back_geometry( - vertices.x, - vertices.y + vertices.h, - coordinates.x, - coordinates.y + coordinates.h + ekg::p_core->draw_allocator.push_back_geometry( + vertex.x, + vertex.y + vertex.h, + uv.x, + uv.y + uv.h ); - this->p_allocator->push_back_geometry( - vertices.x + vertices.w, - vertices.y + vertices.h, - coordinates.x + coordinates.w, - coordinates.y + coordinates.h + ekg::p_core->draw_allocator.push_back_geometry( + vertex.x + vertex.w, + vertex.y + vertex.h, + uv.x + uv.w, + uv.y + uv.h ); - this->p_allocator->push_back_geometry( - vertices.x + vertices.w, - vertices.y + vertices.h, - coordinates.x + coordinates.w, - coordinates.y + coordinates.h + ekg::p_core->draw_allocator.push_back_geometry( + vertex.x + vertex.w, + vertex.y + vertex.h, + uv.x + uv.w, + uv.y + uv.h ); - this->p_allocator->push_back_geometry( - vertices.x + vertices.w, - vertices.y, - coordinates.x + coordinates.w, - coordinates.y + ekg::p_core->draw_allocator.push_back_geometry( + vertex.x + vertex.w, + vertex.y, + uv.x + uv.w, + uv.y ); - this->p_allocator->push_back_geometry( - vertices.x, - vertices.y, - coordinates.x, - coordinates.y + ekg::p_core->draw_allocator.push_back_geometry( + vertex.x, + vertex.y, + uv.x, + uv.y ); - x += char_data.wsize; + x += glyph.wsize; ft_uint_previous = char32; - data.factor += ekg::draw::generate_factor_hash(x, char32, char_data.x); + /** + * Peek `ekg/io/memory.hpp` for better hash definition and purpose. + **/ + data.hash += ekg_generate_hash(x, char32, glyph.x); } - this->flush(); - this->p_allocator->bind_texture(&this->atlas_texture_sampler); - this->p_allocator->dispatch(); + + ekg::draw::allocator::is_simple_shape = false; + ekg::p_core->draw_allocator.bind_texture(ekg::query(this->atlas_texture_sampler_at)); + ekg::p_core->draw_allocator.dispatch(); } -void ekg::draw::font_renderer::flush() { - uint64_t size {this->loaded_sampler_generate_list.size()}; +void ekg::draw::font::flush() { + uint64_t size {this->new_glyphs_to_atlas.size()}; if (this->last_sampler_generate_list_size != size) { ekg::log() << "Sampler updated from-to: " << this->last_sampler_generate_list_size << " " << size; this->reload(); this->last_sampler_generate_list_size = size; - ekg::viewport.redraw = true; - } -} - -void ekg::draw::font_renderer::init() { - if (this->was_initialized) { - return; - } - - this->was_initialized = true; - - this->loaded_sampler_generate_list.resize(256); - for (char32_t char32 {}; char32 < 256; char32++) { - this->loaded_sampler_generate_list.at(char32) = char32; - } - - ekg::log() << "Initializing 256 default chars!"; -} - -void ekg::draw::font_renderer::quit() { - ekg::io::font_face_t &text_font_face {this->faces[ekg::io::font_face_type::text]}; - ekg::io::font_face_t &emojis_font_face {this->faces[ekg::io::font_face_type::emojis]}; - ekg::io::font_face_t &kanjis_font_face {this->faces[ekg::io::font_face_type::kanjis]}; - - if (text_font_face.was_loaded) { - FT_Done_Face(text_font_face.ft_face); - text_font_face.was_loaded = false; - } - - if (emojis_font_face.was_loaded) { - FT_Done_Face(emojis_font_face.ft_face); - emojis_font_face.was_loaded = false; - } - - if (kanjis_font_face.was_loaded) { - FT_Done_Face(kanjis_font_face.ft_face); - kanjis_font_face.was_loaded = false; + ekg::gui.ui.redraw = true; } } -ekg::draw::font_renderer &ekg::draw::get_font_renderer(ekg::font font) { +ekg::draw::font &ekg::draw::get_font_renderer(ekg::font font) { switch (font) { case ekg::font::small: - return ekg::p_core->draw_fr_small; + return ekg::p_core->draw_font_small; case ekg::font::big: - return ekg::p_core->draw_fr_big; + return ekg::p_core->draw_font_big; default: break; } - return ekg::p_core->draw_fr_normal; + return ekg::p_core->draw_font_medium;; } diff --git a/src/ekg.cpp b/src/ekg.cpp index b8cd79aa..a30e5210 100644 --- a/src/ekg.cpp +++ b/src/ekg.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,66 +21,151 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #include "ekg/ekg.hpp" -#include "ekg/os/platform.hpp" +#include "ekg/io/log.hpp" +#include "ekg/core/context.hpp" +#include "ekg/core/pools.hpp" -void ekg::init( - ekg::runtime *p_ekg_runtime, - ekg::runtime_property_t *p_ekg_runtime_property -) { - ekg::log() << "Initializing FreeType library"; +#include "ekg/ui/button/widget.hpp" - if (FT_Init_FreeType(&ekg::freetype_library)) { - ekg::log() << "Error: Failed to init FreeType library"; - return; +ekg::runtime_t *ekg::p_core {nullptr}; +ekg::metrics_t ekg::metrics {}; +ekg::dpi_t ekg::dpi {}; +ekg::gui_t ekg::gui {}; +ekg::pools_t ekg::pools {}; + +std::ostringstream ekg::log::buffer {}; +bool ekg::log::buffered {}; + +ekg::flags_t ekg::init( + ekg::runtime_properties_info_t &runtime_properties_info, + ekg::runtime_t *p_runtime +) { + if (p_runtime == nullptr) { + ekg::log("~ERROR~ invalid (?) `ekg::runtime_t` pointer address: nullptr"); + return ekg::result::failed; } - ekg::log() << "Initializing built-in OS platform-interface"; + ekg::log() << "Initializing..."; + + ekg::p_core = p_runtime; - p_ekg_runtime->p_os_platform = p_ekg_runtime_property->p_os_platform; - p_ekg_runtime->p_os_platform->init(); + ekg::p_core->p_platform_base = runtime_properties_info.p_platform_base; + ekg::p_core->p_gpu_api = runtime_properties_info.p_gpu_api; + ekg::p_core->ft_library = runtime_properties_info.ft_library; - ekg::log() << "Initializing built-in GPU hardware-interface"; + ekg::p_core->p_platform_base->init(); + ekg::p_core->p_gpu_api->init(); - p_ekg_runtime->p_gpu_api = p_ekg_runtime_property->p_gpu_api; - p_ekg_runtime->p_gpu_api->init(); - p_ekg_runtime->p_gpu_api->log_vendor_details(); + ekg::p_core->handler_callback.init(); + ekg::p_core->handler_input.init(); + ekg::p_core->handler_theme.init(); + ekg::p_core->draw_allocator.init(); - ekg::log() << "Pre-Initializing EKG"; + /* deprecated soon */ - p_ekg_runtime->draw_fr_small.init(); - p_ekg_runtime->draw_fr_small.set_font(p_ekg_runtime_property->font_path); - p_ekg_runtime->draw_fr_small.set_font_emoji(p_ekg_runtime_property->font_path_emoji); + ekg::p_core->draw_font_small.init(); + ekg::p_core->draw_font_small.set_font(runtime_properties_info.default_font_path_text); + ekg::p_core->draw_font_small.set_font_emoji(runtime_properties_info.default_font_path_emoji); + ekg::p_core->draw_font_small.get_atlas_texture_sampler().gl_protected_active_index = true;; - p_ekg_runtime->draw_fr_normal.init(); - p_ekg_runtime->draw_fr_normal.set_font(p_ekg_runtime_property->font_path); - p_ekg_runtime->draw_fr_normal.set_font_emoji(p_ekg_runtime_property->font_path_emoji); + ekg::p_core->draw_font_medium.init(); + ekg::p_core->draw_font_medium.set_font(runtime_properties_info.default_font_path_text); + ekg::p_core->draw_font_medium.set_font_emoji(runtime_properties_info.default_font_path_emoji); + ekg::p_core->draw_font_medium.get_atlas_texture_sampler().gl_protected_active_index = true;; - p_ekg_runtime->draw_fr_big.init(); - p_ekg_runtime->draw_fr_big.set_font(p_ekg_runtime_property->font_path); - p_ekg_runtime->draw_fr_big.set_font_emoji(p_ekg_runtime_property->font_path_emoji); + ekg::p_core->draw_font_big.init(); + ekg::p_core->draw_font_big.set_font(runtime_properties_info.default_font_path_text); + ekg::p_core->draw_font_big.set_font_emoji(runtime_properties_info.default_font_path_emoji); + ekg::p_core->draw_font_big.get_atlas_texture_sampler().gl_protected_active_index = true; - ekg::log() << "Initializing EKG"; + ekg::info_t info {}; + ekg::core::scalenize(info); - ekg::p_core = p_ekg_runtime; - ekg::p_core->init(); + ekg::log() << "Successfully initialized"; + + return ekg::result::success; } void ekg::quit() { - ekg::p_core->p_os_platform->quit(); - ekg::p_core->p_gpu_api->quit(); - ekg::p_core->quit(); - - ekg::log() << "Shutdown complete - Thank you for using EKG ;) <3"; + if (ekg::p_core == nullptr) { + return; + } } void ekg::update() { - ekg::p_core->update(); - ekg::p_core->p_os_platform->update(); - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::none; + if (ekg::p_core == nullptr) { + return; + } + + ekg::p_core->handler_input.update(); + + size_t size {ekg::p_core->high_frequency.size()}; + for (size_t it {}; it < size; it++) { + ekg::property_t &property { + ekg::query(ekg::p_core->high_frequency.at(it)) + }; + + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, + ekg::ui::high_frequency(property, descriptor); + ); + + if ( + // no not-found-check because `t::not_found` bool fields are always false + !property.widget.is_high_frequency + ) { + property.operation.should_enable_high_frequency = false; // same here + + ekg::p_core->high_frequency.erase( + ekg::p_core->high_frequency.begin() + it + ); + + size = ekg::p_core->high_frequency.size(); + } + } + + ekg::p_core->handler_callback.update(); + ekg::p_core->p_platform_base->update(); + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::none; + + ekg::log::flush(); } void ekg::render() { - ekg::p_core->render(); + if (ekg::p_core == nullptr) { + return; + } + + if (ekg::gui.ui.redraw) { + ekg::p_core->draw_allocator.invoke(); + + for (ekg::at_t &at : ekg::p_core->stack) { + ekg::property_t &property { + ekg::query(at) + }; + + if (property == ekg::property_t::not_found) { + continue; + } + + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, + + ekg::ui::pass(property, descriptor); + if (!property.widget.should_buffering) { + continue; + } + + ekg::ui::buffering(property, descriptor); + ); + } + + ekg::p_core->draw_allocator.revoke(); + } + + ekg::gui.ui.redraw = false; + ekg::p_core->draw_allocator.to_gpu(); } diff --git a/src/gpu/allocator.cpp b/src/gpu/allocator.cpp deleted file mode 100644 index 9dac2854..00000000 --- a/src/gpu/allocator.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/gpu/allocator.hpp" -#include "ekg/core/runtime.hpp" - -// TODO: add capacity mapped-gbuffer - -bool ekg::gpu::allocator::high_priority {}; -float ekg::gpu::allocator::concave {-2.0f}; -uint64_t ekg::gpu::allocator::current_rendering_data_count {}; - -void ekg::gpu::allocator::invoke() { - this->data_instance_index = 0; - this->begin_stride_count = 0; - this->end_stride_count = 0; - this->simple_shape_index = 0; - this->geometry_resource_index = 0; - - /** - * inserting a simple triangle mesh, - * is necessary to make work the simple-shape rendering. - **/ - this->push_back_geometry(0.0f, 0.0f, 0.0f, 0.0f); - this->push_back_geometry(0.0f, 1.0f, 0.0f, 1.0f); - this->push_back_geometry(1.0f, 0.0f, 1.0f, 0.0f); - this->push_back_geometry(1.0f, 1.0f, 1.0f, 1.0f); - - if (this->data_instance_index >= this->data_list.size()) { - this->data_list.emplace_back(); - } - - //this->data_list.at(this->data_instance_index).begin_stride = this->end_stride_count; - this->begin_stride_count += this->end_stride_count; - this->end_stride_count = 0; -} - -void ekg::gpu::allocator::bind_texture(ekg::sampler_t *p_sampler) { - if (p_sampler == nullptr) { - return; - } - - ekg::io::gpu_data_t &data {this->data_list.at(this->data_instance_index)}; - data.sampler_index = ekg::p_core->p_gpu_api->bind_sampler(p_sampler); -} - -void ekg::gpu::allocator::dispatch() { - ekg::io::gpu_data_t *p_data {}; - - if (ekg::gpu::allocator::high_priority) { - if (this->high_priority_data_instance_index >= this->high_priority_data_list.size()) { - this->high_priority_data_list.emplace_back(); - } - - p_data = &( - this->high_priority_data_list.at(this->high_priority_data_instance_index++) = ( - ekg::io::gpu_data_t {this->data_list.at(this->data_instance_index)} - ) - ); - - this->data_instance_index -= this->data_instance_index > 0; - } else { - p_data = &this->data_list.at(this->data_instance_index); - } - - /** - * Scissor must be synchned externally to update the scissor context. - **/ - - p_data->buffer_content[8] = this->scissor_instance.x; - p_data->buffer_content[9] = this->scissor_instance.y; - p_data->buffer_content[10] = this->scissor_instance.w; - p_data->buffer_content[11] = this->scissor_instance.h; - - /** - * the point of re-using a simple shape stride makes performance a little better, - * due the index rendering, with only one triangle for rectangles. - **/ - - this->simple_shape = ( - static_cast(p_data->buffer_content[2]) != static_cast(ekg::gpu::allocator::concave) - && - static_cast(p_data->buffer_content[3]) != static_cast(ekg::gpu::allocator::concave) - ); - - if (this->simple_shape) { - p_data->begin_stride = this->simple_shape_index; - p_data->end_stride = 4; // simple shape contains 4 vertices. - this->end_stride_count = 0; - } else { - p_data->begin_stride = this->begin_stride_count; - p_data->end_stride = this->end_stride_count; - } - - /* flag re alloc buffers if factor changed */ - - if (!this->factor_changed) { - this->factor_changed = ( - this->previous_factor != p_data->factor - ); - } - - this->begin_stride_count += this->end_stride_count; - this->end_stride_count = 0; - this->data_instance_index++; -} - -void ekg::gpu::allocator::revoke() { - this->data_instance_index -= this->data_instance_index > 0; - - if (!this->high_priority_data_list.empty()) { - uint64_t high_priority_data_list_size {this->high_priority_data_list.size()}; - - /** - * So, I do not trust this code by the performanceless, - * usually I would use a refill, but if the refill necessary part - * is larger than the data list size, then it requires a re-allocation; - * and so I do not know how code it performanceness. - * - * Rina. - **/ - this->data_list.erase( - this->data_list.begin() + this->data_instance_index + 1, - this->data_list.end() - ); - - this->data_list.insert( - this->data_list.end(), - this->high_priority_data_list.begin(), - this->high_priority_data_list.end() - ); - - this->data_instance_index += high_priority_data_list_size; - this->high_priority_data_list.clear(); - this->high_priority_data_instance_index = 0; - } - - uint64_t geometry_resource_list_size {this->geometry_resource_index}; - bool should_re_alloc_buffers {this->previous_geometry_resource_list_size != geometry_resource_list_size}; - - if (this->data_instance_index < this->data_list.size()) { - this->data_list.erase( - this->data_list.begin() + this->data_instance_index + 1, - this->data_list.end() - ); - } - - if (should_re_alloc_buffers || this->factor_changed) { - ekg::p_core->p_gpu_api->re_alloc_geometry_resources( - this->geometry_resource_list.data(), - this->geometry_resource_list.size() - ); - } - - this->factor_changed = false; - - if (this->geometry_resource_list.size() < this->previous_geometry_resource_list_size) { - this->geometry_resource_list.erase( - this->geometry_resource_list.begin() + geometry_resource_list_size + 1, - this->geometry_resource_list.end() - ); - } - - this->previous_geometry_resource_list_size = geometry_resource_list_size; - ekg::gpu::allocator::current_rendering_data_count = this->data_list.size(); -} - -void ekg::gpu::allocator::on_update() { -} - -void ekg::gpu::allocator::draw() { - ekg::p_core->p_gpu_api->draw( - this->data_list - ); -} - -void ekg::gpu::allocator::init() { - ekg::log() << "Initializing GPU allocator"; -} - -void ekg::gpu::allocator::clear_current_data() { - if (!ekg::gpu::allocator::high_priority && this->data_instance_index >= this->data_list.size()) { - this->data_list.emplace_back(); - } - - /* allocator handle automatically the size of data */ - ekg::io::gpu_data_t &data {this->data_list.at(this->data_instance_index)}; - data.line_thickness = 0; - data.sampler_index = -1; - - this->previous_factor = data.factor; -} - -ekg::io::gpu_data_t &ekg::gpu::allocator::bind_current_data() { - this->clear_current_data(); - return this->data_list.at(this->data_instance_index); -} - -uint32_t ekg::gpu::allocator::get_current_data_id() { - return this->data_instance_index; -} - -ekg::io::gpu_data_t *ekg::gpu::allocator::get_data_by_id(int32_t id) { - if (id < 0 || static_cast(id) > this->data_instance_index) { - return nullptr; - } - - return &this->data_list[id]; -} - -void ekg::gpu::allocator::quit() { - ekg::log() << "Quitting GPU allocator"; -} - -bool ekg::gpu::allocator::sync_scissor( - ekg::rect_t &scissor, - ekg::rect_t &rect_child, - ekg::rect_t *p_parent_scissor -) { - scissor.x = rect_child.x; - scissor.y = rect_child.y; - scissor.w = rect_child.w; - scissor.h = rect_child.h; - - if (p_parent_scissor) { - bool only_if {}; - - only_if = scissor.x < p_parent_scissor->x; - scissor.w -= only_if * (p_parent_scissor->x - scissor.x); - scissor.x = (only_if * p_parent_scissor->x) + (scissor.x * !only_if); - - only_if = scissor.y < p_parent_scissor->y; - scissor.h -= only_if * (p_parent_scissor->y - scissor.y); - scissor.y = (only_if * p_parent_scissor->y) + (scissor.y * !only_if); - - only_if = scissor.x + scissor.w > p_parent_scissor->x + p_parent_scissor->w; - scissor.w -= only_if * ((scissor.x + scissor.w) - (p_parent_scissor->x + p_parent_scissor->w)); - - only_if = scissor.y + scissor.h > p_parent_scissor->y + p_parent_scissor->h; - scissor.h -= only_if * ((scissor.y + scissor.h) - (p_parent_scissor->y + p_parent_scissor->h)); - - this->scissor_instance.x = scissor.x; - this->scissor_instance.y = scissor.y; - this->scissor_instance.w = scissor.w; - this->scissor_instance.h = scissor.h; - - return ( - scissor.x < p_parent_scissor->x + p_parent_scissor->w - && - scissor.x + scissor.w > p_parent_scissor->x - && - scissor.y < p_parent_scissor->y + p_parent_scissor->h - && - scissor.y + scissor.h > p_parent_scissor->y - ); - } - - this->scissor_instance.x = scissor.x; - this->scissor_instance.y = scissor.y; - this->scissor_instance.w = scissor.w; - this->scissor_instance.h = scissor.h; - - return true; -} - -void ekg::gpu::allocator::unsafe_set_scissor_placement( - float x, - float y, - float w, - float h -) { - this->scissor_instance.x = x; - this->scissor_instance.y = y; - this->scissor_instance.w = w; - this->scissor_instance.h = h; -} - -void ekg::gpu::allocator::push_back_geometry( - float x, - float y, - float u, - float v -) { - this->end_stride_count++; - - if (this->geometry_resource_index >= this->geometry_resource_list.size()) { - this->geometry_resource_index += 4; - - this->geometry_resource_list.emplace_back(x); - this->geometry_resource_list.emplace_back(y); - - this->geometry_resource_list.emplace_back(u); - this->geometry_resource_list.emplace_back(v); - - return; - } - - this->geometry_resource_list[this->geometry_resource_index++] = x; - this->geometry_resource_list[this->geometry_resource_index++] = y; - - this->geometry_resource_list[this->geometry_resource_index++] = u; - this->geometry_resource_list[this->geometry_resource_index++] = v; -} diff --git a/src/os/ekg_wayland_server.cpp b/src/gpu/data.cpp similarity index 89% rename from src/os/ekg_wayland_server.cpp rename to src/gpu/data.cpp index 6e345aa2..75c2c6f1 100644 --- a/src/os/ekg_wayland_server.cpp +++ b/src/gpu/data.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,5 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/gpu/data.hpp" -#include "ekg/os/ekg_wayland_server.hpp" \ No newline at end of file +ekg::gpu::data_t ekg::gpu::data_t::not_found {}; diff --git a/src/os/ekg_opengl.cpp b/src/gpu/opengl/gl.cpp similarity index 60% rename from src/os/ekg_opengl.cpp rename to src/gpu/opengl/gl.cpp index 1f80623f..5df0a750 100644 --- a/src/os/ekg_opengl.cpp +++ b/src/gpu/opengl/gl.cpp @@ -1,17 +1,42 @@ -#include "ekg/os/ekg_opengl.hpp" -#include "ekg/gpu/opengl_pipeline_template.hpp" -#include "ekg/core/runtime.hpp" -#include "ekg/gpu/api.hpp" -#include "ekg/io/gpu.hpp" - +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include #include #include #include +#include "ekg/core/context.hpp" +#include "ekg/gpu/opengl/gl.hpp" +#include "ekg/gpu/opengl/shaders.hpp" +#include "ekg/core/runtime.hpp" +#include "ekg/io/log.hpp" +#include "ekg/core/pools.hpp" + ekg::opengl::opengl(std::string_view set_glsl_version) { if (set_glsl_version.empty()) { - ekg::log() << "[GPU][API] not viable glsl version, empty, must: 330 higher for core-profile or 300 higher for ES"; + ekg::log() + << "[GPU][API] not viable glsl version, empty, must: 330 higher for core-profile or 300 higher for ES"; return; } @@ -19,9 +44,9 @@ ekg::opengl::opengl(std::string_view set_glsl_version) { std::regex es_re {"es"}; if (std::regex_search(glsl_version, es_re)) { - this->gpu_api = ekg::gpu_api::opengles; + this->which_gpu_api = ekg::which_gpu_api::opengles; } else { - this->gpu_api = ekg::gpu_api::opengl; + this->which_gpu_api = ekg::which_gpu_api::opengl; } std::regex number_re {"\\d+"}; @@ -41,7 +66,7 @@ ekg::opengl::opengl(std::string_view set_glsl_version) { } } else { ekg::log() << "[GPU][API] not viable glsl version, unknown number, must: 330 higher for core-profile or 300 higher for ES"; - return; + return; } this->glsl_version = set_glsl_version; @@ -57,30 +82,29 @@ void ekg::opengl::init() { }; std::string vsh_src {}; - ekg::gpu::get_standard_vertex_shader( + ekg::gpu::glsl_opengl_pipeline_vsh( no_view_glsl_version, - this->gpu_api, + this->which_gpu_api, vsh_src ); std::string fsh_src {}; - ekg::gpu::get_standard_fragment_shader( + ekg::gpu::glsl_opengl_pipeline_fsh( no_view_glsl_version, - this->gpu_api, + this->which_gpu_api, fsh_src ); - if (!this->rendering_shader_fragment_source.empty()) { - fsh_src = this->rendering_shader_fragment_source; - } - ekg::log() << "Loading internal shaders..."; /* Create main shading program using two basic shaders (vertex & fragment). */ - this->create_pipeline_program(this->pipeline_program, { - {vsh_src, GL_VERTEX_SHADER}, - {fsh_src, GL_FRAGMENT_SHADER} - }); + this->create_pipeline_program( + this->pipeline_program, + { + {vsh_src, GL_VERTEX_SHADER}, + {fsh_src, GL_FRAGMENT_SHADER} + } + ); glGenVertexArrays(1, &this->vbo_array); glGenBuffers(1, &this->geometry_buffer); @@ -98,15 +122,33 @@ void ekg::opengl::init() { glBindBuffer(GL_ARRAY_BUFFER, this->geometry_buffer); glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr); + glVertexAttribPointer( + 0, 2, + GL_FLOAT, + GL_FALSE, + sizeof(float) * 4, + nullptr + ); glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *) (sizeof(float) * 2)); + glVertexAttribPointer( + 1, 2, + GL_FLOAT, + GL_FALSE, + sizeof(float) * 4, + (void *) (sizeof(float) * 2) + ); /* End of geometry resources buffer attributes. */ /* Start of simple shape indexing buffer bind to VAO. */ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->ebo_simple_shape); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(simple_shape_mesh_indices), simple_shape_mesh_indices, GL_STATIC_DRAW); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(simple_shape_mesh_indices), + simple_shape_mesh_indices, + GL_STATIC_DRAW + ); + glBindVertexArray(0); /* End of simple shape indexing buffer bind to VAO. */ @@ -132,26 +174,29 @@ void ekg::opengl::pre_re_alloc() { } void ekg::opengl::update_viewport(int32_t w, int32_t h) { - this->viewport.x = 0.0f; - this->viewport.y = 0.0f; - this->viewport.w = static_cast(w); - this->viewport.h = static_cast(h); + ekg::dpi.viewport.x = 0.0f; + ekg::dpi.viewport.y = 0.0f; + ekg::dpi.viewport.w = static_cast(w); + ekg::dpi.viewport.h = static_cast(h); ekg::ortho( - this->projection_matrix, + this->mat4x4_proj_matrix, 0, - this->viewport.w, - this->viewport.h, + ekg::dpi.viewport.w, + ekg::dpi.viewport.h, 0 ); glUseProgram(this->pipeline_program); - glUniformMatrix4fv(this->uniform_projection, GL_TRUE, 0, this->projection_matrix); - glUniform1f(this->uniform_viewport_height, this->viewport.h); + glUniformMatrix4fv(this->uniform_projection, GL_TRUE, 0, this->mat4x4_proj_matrix); + glUniform1f(this->uniform_viewport_height, ekg::dpi.viewport.h); glUseProgram(0); } -bool ekg::opengl::create_pipeline_program(uint32_t &program, const std::unordered_map &resources) { +bool ekg::opengl::create_pipeline_program( + uint32_t &program, + const std::unordered_map &resources +) { if (resources.empty()) { ekg::log() << "Error: Invalid shader, empty resources"; return true; @@ -206,12 +251,12 @@ bool ekg::opengl::create_pipeline_program(uint32_t &program, const std::unordere return false; } -void ekg::opengl::re_alloc_geometry_resources( +void ekg::opengl::pass_geometry_buffer_to_gpu( const float *p_data, uint64_t size ) { - // TODO: add capacity to gbuffers instead buffer data directly - // also use mapped buffers to get ptr directly. + // @TODO: add capacity mapped-gbuffer + // @TODO: add capacity to gbuffers instead buffer data directly glBindVertexArray(this->vbo_array); @@ -227,90 +272,106 @@ void ekg::opengl::re_alloc_geometry_resources( } ekg::flags_t ekg::opengl::allocate_sampler( - ekg::sampler_allocate_info_t *p_sampler_allocate_info, - ekg::sampler_t *p_sampler + ekg::sampler_allocate_info_t &sampler_allocate_info, + ekg::sampler_t &sampler ) { - if (p_sampler == nullptr) { + if (sampler == ekg::sampler_t::not_found) { return ekg::result::failed; } - if (!p_sampler->gl_id) { - glGenTextures(1, &p_sampler->gl_id); + if (!sampler.gl_id) { + glGenTextures(1, &sampler.gl_id); } glBindTexture( GL_TEXTURE_2D, - p_sampler->gl_id + sampler.gl_id ); - if (p_sampler_allocate_info->gl_unpack_alignment) { + if (sampler_allocate_info.gl_unpack_alignment) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } - p_sampler->w = p_sampler_allocate_info->w; - p_sampler->h = p_sampler_allocate_info->h; + sampler.w = sampler_allocate_info.w; + sampler.h = sampler_allocate_info.h; - if (p_sampler_allocate_info->gl_wrap_modes[0]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p_sampler_allocate_info->gl_wrap_modes[0]); + if (sampler_allocate_info.gl_wrap_modes[0]) { + glTexParameteri( + GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, + sampler_allocate_info.gl_wrap_modes[0] + ); } - if (p_sampler_allocate_info->gl_wrap_modes[1]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p_sampler_allocate_info->gl_wrap_modes[1]); + if (sampler_allocate_info.gl_wrap_modes[1]) { + glTexParameteri( + GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, + sampler_allocate_info.gl_wrap_modes[1] + ); } - if (p_sampler_allocate_info->gl_parameter_filter[0]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, p_sampler_allocate_info->gl_parameter_filter[0]); + if (sampler_allocate_info.gl_parameter_filter[0]) { + glTexParameteri( + GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + sampler_allocate_info.gl_parameter_filter[0] + ); } - if (p_sampler_allocate_info->gl_parameter_filter[1]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, p_sampler_allocate_info->gl_parameter_filter[1]); + if (sampler_allocate_info.gl_parameter_filter[1]) { + glTexParameteri( + GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + sampler_allocate_info.gl_parameter_filter[1] + ); } glTexImage2D( GL_TEXTURE_2D, 0, - p_sampler_allocate_info->gl_internal_format, - p_sampler_allocate_info->w, - p_sampler_allocate_info->h, + sampler_allocate_info.gl_internal_format, + sampler_allocate_info.w, + sampler_allocate_info.h, 0, - p_sampler_allocate_info->gl_format, - p_sampler_allocate_info->gl_type, - p_sampler_allocate_info->p_data + sampler_allocate_info.gl_format, + sampler_allocate_info.gl_type, + sampler_allocate_info.pv_data ); - if (p_sampler_allocate_info->gl_generate_mipmap) { + if (sampler_allocate_info.gl_generate_mipmap) { glGenerateMipmap(GL_TEXTURE_2D); } glBindTexture(GL_TEXTURE_2D, 0); - p_sampler->p_tag = p_sampler_allocate_info->p_tag; + sampler.p_tag = sampler_allocate_info.p_tag; return ekg::result::success; } ekg::flags_t ekg::opengl::fill_sampler( - ekg::sampler_fill_info_t *p_sampler_fill_info, - ekg::sampler_t *p_sampler + ekg::sampler_fill_info_t &sampler_fill_info, + ekg::sampler_t &sampler ) { - if (p_sampler == nullptr || !p_sampler->gl_id) { + if (sampler == ekg::sampler_t::not_found || !sampler.gl_id) { return ekg::result::failed; } glBindTexture( GL_TEXTURE_2D, - p_sampler->gl_id + sampler.gl_id ); glTexSubImage2D( GL_TEXTURE_2D, 0, - p_sampler_fill_info->offset[0], - p_sampler_fill_info->offset[1], - p_sampler_fill_info->w, - p_sampler_fill_info->h, - p_sampler_fill_info->gl_format, - p_sampler_fill_info->gl_type, - p_sampler_fill_info->p_data + sampler_fill_info.offset[0], + sampler_fill_info.offset[1], + sampler_fill_info.w, + sampler_fill_info.h, + sampler_fill_info.gl_format, + sampler_fill_info.gl_type, + sampler_fill_info.pv_data ); glBindTexture(GL_TEXTURE_2D, 0); @@ -318,16 +379,20 @@ ekg::flags_t ekg::opengl::fill_sampler( } ekg::flags_t ekg::opengl::gen_font_atlas_and_map_glyph( - ekg::sampler_t *p_sampler, + ekg::sampler_t &sampler, ekg::io::font_face_t *p_font_face_text, ekg::io::font_face_t *p_font_face_emoji, ekg::io::font_face_t *p_font_face_kanjis, ekg::rect_t &atlas_rect, std::vector &char_to_gen_sampler_list, - std::unordered_map &mapped_gpu_data_char_glyph, + std::unordered_map &mapped_gpu_data_char_glyph, float &non_swizzlable_range ) { - ekg::io::font_face_t *faces[ekg::io::supported_faces_size] { + if (sampler == ekg::sampler_t::not_found) { + return ekg::result::failed; + } + + ekg::io::font_face_t *faces[ekg::io::enum_font_face_type_size] { p_font_face_text, p_font_face_emoji, p_font_face_kanjis @@ -337,7 +402,7 @@ ekg::flags_t ekg::opengl::gen_font_atlas_and_map_glyph( float square {}; ekg::vec2_t highest_glyph_size {}; - for (size_t it {}; it < ekg::io::supported_faces_size; it++) { + for (size_t it {}; it < ekg::io::enum_font_face_type_size; it++) { ekg::io::font_face_t *&p_font_face { faces[it] }; @@ -364,7 +429,7 @@ ekg::flags_t ekg::opengl::gen_font_atlas_and_map_glyph( }; bool is_current_gpu_api_gl_es { - this->gpu_api == ekg::gpu_api::opengles + this->which_gpu_api == ekg::which_gpu_api::opengles }; std::vector r8_to_r8g8b8a8_swizzled_image {}; @@ -382,12 +447,12 @@ ekg::flags_t ekg::opengl::gen_font_atlas_and_map_glyph( ); } - if (!p_sampler->gl_id) { - glGenTextures(1, &p_sampler->gl_id); + if (!sampler.gl_id) { + glGenTextures(1, &sampler.gl_id); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, p_sampler->gl_id); + glBindTexture(GL_TEXTURE_2D, sampler.gl_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -435,7 +500,7 @@ ekg::flags_t ekg::opengl::gen_font_atlas_and_map_glyph( continue; } - ekg::io::glyph_char_t &char_data {mapped_gpu_data_char_glyph[char32]}; + ekg::io::glyph_t &char_data {mapped_gpu_data_char_glyph[char32]}; char_data.x = offset / static_cast(atlas_rect.w); p_current_image_buffer = ft_glyph_slot->bitmap.buffer; @@ -455,7 +520,7 @@ ekg::flags_t ekg::opengl::gen_font_atlas_and_map_glyph( if ( ekg::has( - ekg::image_src_r8_convert_to_r8g8b8a8( + ekg::sampler_src_r8_to_r8g8b8a8( highest_glyph_size, p_src_copy, r8_to_r8g8b8a8_swizzled_image @@ -496,27 +561,27 @@ ekg::flags_t ekg::opengl::gen_font_atlas_and_map_glyph( return ekg::result::success; } -ekg::flags_t ekg::opengl::bind_sampler(ekg::sampler_t *p_sampler) { +ekg::at_t &ekg::opengl::bind_sampler(ekg::sampler_t &sampler) { uint64_t size {this->bound_sampler_list.size()}; for (uint64_t it {}; it < this->bound_sampler_list.size(); it++) { - ekg::sampler_t *p_in_list_sampler {this->bound_sampler_list.at(it)}; - if (p_in_list_sampler->gl_id == p_sampler->gl_id) { - return it; + ekg::at_t &at {this->bound_sampler_list.at(it)}; + if (at == sampler.at) { + return sampler.at; } } if ( - (p_sampler->is_protected = p_sampler->gl_protected_active_index > -1) + (sampler.is_protected = sampler.gl_protected_active_index > -1) ) { - p_sampler->gl_protected_active_index = this->protected_texture_active_index++; + sampler.gl_protected_active_index = this->protected_texture_active_index++; } - this->bound_sampler_list.emplace_back() = p_sampler; - return size; + this->bound_sampler_list.emplace_back() = sampler; + return sampler.at; } -void ekg::opengl::draw( - std::vector &loaded_gpu_data_list +void ekg::opengl::pass_gpu_data_buffer_to_gpu( + std::vector &gpu_data_buffer ) { glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); @@ -531,56 +596,71 @@ void ekg::opengl::draw( * this reason the active index only increase * when THIS is a protected sampler. **/ - for (ekg::sampler_t *&p_sampler : this->bound_sampler_list) { - if (!p_sampler->is_protected) continue; + for (ekg::at_t &at : this->bound_sampler_list) { + ekg::sampler_t &sampler {ekg::query(at)}; + if (sampler == ekg::sampler_t::not_found || !sampler.is_protected) continue; glActiveTexture( - GL_TEXTURE0 + p_sampler->gl_protected_active_index + GL_TEXTURE0 + sampler.gl_protected_active_index ); - glBindTexture(GL_TEXTURE_2D, p_sampler->gl_id); + glBindTexture(GL_TEXTURE_2D, sampler.gl_id); } glActiveTexture(GL_TEXTURE0 + this->protected_texture_active_index); - int32_t previous_sampler_bound {INT32_MAX}; + ekg::at_t previous_sampler_bound {.unique_id = ekg::not_found}; bool sampler_going_on {}; - for (ekg::io::gpu_data_t &data : loaded_gpu_data_list) { - sampler_going_on = data.sampler_index > -1; + for (ekg::gpu::data_t &data : gpu_data_buffer) { + sampler_going_on = data.sampler_at != ekg::at_t::not_found; if ( sampler_going_on && - ( - previous_sampler_bound != data.sampler_index - || - !this->bound_sampler_list.at(data.sampler_index)->is_protected - ) + previous_sampler_bound != data.sampler_at + && + ekg::query(data.sampler_at) != ekg::sampler_t::not_found ) { - ekg::sampler_t *&p_sampler { - this->bound_sampler_list.at(data.sampler_index) - }; + ekg::sampler_t &sampler {ekg::query(data.sampler_at)}; + + if (sampler.is_protected) { + glUniform1i( + this->uniform_active_tex_slot, + sampler.gl_protected_active_index + ); - if (p_sampler->is_protected) { - glUniform1i(this->uniform_active_tex_slot, p_sampler->gl_protected_active_index); - glUniform1i(this->uniform_active_texture, EKG_ENABLE_TEXTURE_PROTECTED); + glUniform1i( + this->uniform_active_texture, + EKG_ENABLE_TEXTURE_PROTECTED + ); } else { - glBindTexture(GL_TEXTURE_2D, p_sampler->gl_id); - glUniform1i(this->uniform_active_tex_slot, this->protected_texture_active_index); - glUniform1i(this->uniform_active_texture, EKG_ENABLE_TEXTURE); + glBindTexture( + GL_TEXTURE_2D, + sampler.gl_id + ); + + glUniform1i( + this->uniform_active_tex_slot, + this->protected_texture_active_index + ); + + glUniform1i( + this->uniform_active_texture, + EKG_ENABLE_TEXTURE + ); } - previous_sampler_bound = data.sampler_index; - } else if (!sampler_going_on && previous_sampler_bound > -1) { + previous_sampler_bound = data.sampler_at; + } else if (!sampler_going_on && previous_sampler_bound != ekg::at_t::not_found) { glUniform1i(this->uniform_active_texture, EKG_DISABLE_TEXTURE); - previous_sampler_bound = -1; + previous_sampler_bound = ekg::at_t::not_found; } glUniform1i(this->uniform_line_thickness, data.line_thickness); - glUniform4fv(this->uniform_rect, GL_TRUE, data.buffer_content); - glUniform1fv(this->uniform_content, 8, &data.buffer_content[4]); + glUniform4fv(this->uniform_rect, GL_TRUE, data.buffer); + glUniform1fv(this->uniform_content, 8, &data.buffer[4]); switch (data.begin_stride) { case 0: { diff --git a/src/gpu/opengl_pipeline_template.cpp b/src/gpu/opengl/shaders.cpp similarity index 74% rename from src/gpu/opengl_pipeline_template.cpp rename to src/gpu/opengl/shaders.cpp index b5981e64..2d668508 100644 --- a/src/gpu/opengl_pipeline_template.cpp +++ b/src/gpu/opengl/shaders.cpp @@ -1,11 +1,34 @@ -#include "ekg/gpu/opengl_pipeline_template.hpp" - -void ekg::gpu::get_standard_vertex_shader( - std::string glsl_version, - ekg::gpu_api gpu_api, - std::string &output_kernel_source +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/gpu/opengl/shaders.hpp" + +void ekg::gpu::glsl_opengl_pipeline_vsh( + const std::string &glsl_version, + ekg::which_gpu_api which_gpu_api, + std::string &out_shader ) { - output_kernel_source = glsl_version + R"( + out_shader = glsl_version + R"( layout (location = 0) in vec2 aPos; layout (location = 1) in vec2 aTexCoord; @@ -33,12 +56,12 @@ void ekg::gpu::get_standard_vertex_shader( )"; } -void ekg::gpu::get_standard_fragment_shader( - std::string glsl_version, - ekg::gpu_api gpu_api, - std::string &output_kernel_source +void ekg::gpu::glsl_opengl_pipeline_fsh( + const std::string &glsl_version, + ekg::which_gpu_api which_gpu_api, + std::string &out_shader ) { - output_kernel_source = glsl_version + R"( + out_shader = glsl_version + R"( layout (location = 0) out vec4 aFragColor; uniform sampler2D uTextureSampler; diff --git a/src/gpu/sampler.cpp b/src/gpu/sampler.cpp new file mode 100644 index 00000000..43d7eadf --- /dev/null +++ b/src/gpu/sampler.cpp @@ -0,0 +1,70 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/gpu/sampler.hpp" + +ekg::sampler_t ekg::sampler_t::not_found {}; + +ekg::flags_t ekg::sampler_src_r8_to_r8g8b8a8( + const ekg::vec2_t &size, + const unsigned char *p_src, + std::vector &dst +) { + if ( + p_src == nullptr + || + size.x == 0 + || + size.y == 0 + || + dst.size() != (size.x * size.y) + ) { + return ekg::result::failed; + } + + size_t index {}; + + for (size_t it {}; it < size.x * size.y; it++) { + const unsigned char &char8_red_color { + p_src[it] + }; + + index = it * 4; + + if (index == dst.size()) { + break; + } + + /** + * may I be wrong? but the format is ARGB and not RGBA, + * I do not know. + **/ + + dst.at(index + 0) = char8_red_color; + dst.at(index + 1) = 255; + dst.at(index + 2) = 255; + dst.at(index + 3) = 255; + } + + return ekg::result::success; +} diff --git a/src/handler/callback.cpp b/src/handler/callback.cpp new file mode 100644 index 00000000..490318b6 --- /dev/null +++ b/src/handler/callback.cpp @@ -0,0 +1,28 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/handler/callback.hpp" + +ekg::callback_t ekg::callback_t::not_found { + .at = ekg::at_t::not_found +}; diff --git a/src/handler/callback/handler.cpp b/src/handler/callback/handler.cpp new file mode 100644 index 00000000..8ac12fc0 --- /dev/null +++ b/src/handler/callback/handler.cpp @@ -0,0 +1,98 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/handler/callback/handler.hpp" +#include "ekg/io/log.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/core/runtime.hpp" + +void ekg::handler::callback::init() { + ekg::log() << "Initialising callback-handler"; + + ekg::callback_t &swap {this->load()}; + swap.info.tag = "swap"; + swap.function = &ekg::core::swap; + + ekg::callback_t &reload {this->load()}; + reload.info.tag = "reload"; + reload.function = &ekg::core::reload; + + ekg::callback_t &docknize {this->load()}; + docknize.info.tag = "docknize"; + docknize.function = &ekg::core::docknize; + + ekg::callback_t &scalenize {this->load()}; + scalenize.info.tag = "scalenize"; + scalenize.function = &ekg::core::scalenize; +} + +ekg::callback_t &ekg::handler::callback::load() { + ekg::callback_t &task {ekg::make({})}; + this->loaded.emplace_back() = task.at; + return task; +} + +void ekg::handler::callback::dispatch(ekg::at_t &at) { + ekg::callback_t &callback {ekg::query(at)}; + if ( + callback != ekg::callback_t::not_found + && + !ekg::has(callback.at.flags, ekg::handler::status::dispatched) + ) { + callback.at.flags |= ekg::handler::status::dispatched; + this->queue.push(callback.at); + } +} + +void ekg::handler::callback::dispatch(uint64_t index) { + ekg::at_t &at {this->loaded.at(index)}; + ekg::callback_t &callback {ekg::query(at)}; + + if ( + callback != ekg::callback_t::not_found + && + !ekg::has(callback.at.flags, ekg::handler::status::dispatched) + ) { + callback.at.flags |= ekg::handler::status::dispatched; + this->queue.push(callback.at); + } +} + +void ekg::handler::callback::update() { + while (!this->queue.empty()) { + ekg::callback_t &callback {ekg::query(this->queue.front())}; + this->queue.pop(); + + if (callback == ekg::callback_t::not_found) { + continue; + } + + if (callback.lambda) { + callback.lambda(callback.info); + } + + if (callback.function) { + callback.function(callback.info); + } + } +} diff --git a/src/io/input.cpp b/src/handler/input.cpp similarity index 53% rename from src/io/input.cpp rename to src/handler/input.cpp index 3528f4bc..69b02f25 100644 --- a/src/io/input.cpp +++ b/src/handler/input.cpp @@ -1,20 +1,20 @@ -#include "ekg/io/input.hpp" +#include "ekg/handler/input.hpp" #include "ekg/core/runtime.hpp" -ekg::input_t &ekg::input() { - return ekg::p_core->service_input.input; +ekg::input_info_t &ekg::input() { + return ekg::p_core->handler_input.input; } bool ekg::fire(std::string_view tag) { - return ekg::p_core->service_input.get_input_bind_state(tag); + return ekg::p_core->handler_input.get_input_bind_state(tag); } bool ekg::input(std::string_view input) { - return ekg::p_core->service_input.get_input_state(input); + return ekg::p_core->handler_input.get_input_state(input); } void ekg::bind(std::string_view tag, std::string_view input) { - ekg::p_core->service_input.insert_input_bind(tag, input); + ekg::p_core->handler_input.insert_input_bind(tag, input); } void ekg::bind(std::string_view tag, std::vector inputs) { diff --git a/src/service/input.cpp b/src/handler/input/handler.cpp similarity index 79% rename from src/service/input.cpp rename to src/handler/input/handler.cpp index abb70c4f..656f618e 100644 --- a/src/service/input.cpp +++ b/src/handler/input/handler.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,16 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #include - -#include "ekg/service/input.hpp" #include -#include "ekg/core/context.hpp" + +#include "ekg/handler/input/handler.hpp" #include "ekg/core/runtime.hpp" +#include "ekg/core/context.hpp" +#include "ekg/io/log.hpp" -void ekg::service::input::init() { - ekg::log() << "Initialising input-service binding system-based"; +void ekg::handler::input::init() { + ekg::log() << "Initialising input-handler binding system-based"; /** * Forced null termination at end due the necessary optmization, @@ -104,7 +104,7 @@ void ekg::service::input::init() { this->special_keys[6][6] = '\0'; this->special_keys[6][7] = '\0'; - ekg::log() << "Registering default user-input bindings"; + ekg::log() << "Initialising default user-input bindings"; /** * https://github.com/vokegpu/ekg-docs/blob/master/model/input-binding-tag-style.md @@ -189,44 +189,47 @@ void ekg::service::input::init() { this->insert_input_bind("scrollbar-scroll-horizontal", "rshift+mouse-wheel"); } -void ekg::service::input::quit() { +void ekg::handler::input::quit() { ekg::log() << "Quitting input-service"; } -void ekg::service::input::on_event() { +void ekg::handler::input::poll_event() { this->input.was_pressed = false; this->input.was_released = false; this->input.has_motion = false; this->input.was_typed = false; float wheel_precise_interval {}; - ekg::io::serialized_input_event_t &serialized_input_event { - ekg::p_core->p_os_platform->serialized_input_event + ekg::io::event_t &platform_event { + ekg::p_core->p_platform_base->event }; - switch (serialized_input_event.type) { - case ekg::io::input_event_type::text_input: { + switch (platform_event.type) { + default: + break; + + case ekg::io::event_type::text_input: { this->input.was_pressed = true; this->input.was_typed = true; break; } - case ekg::io::input_event_type::key_down: { + case ekg::io::event_type::key_down: { this->input.was_pressed = true; this->input.was_typed = true; std::string key_name {}; std::string string_builder {}; - ekg::p_core->p_os_platform->get_key_name( - serialized_input_event.key, + ekg::p_core->p_platform_base->get_key_name( + platform_event.key, key_name ); - ekg::special_key_type special_key {ekg::special_key_type::unknown}; - ekg::p_core->p_os_platform->get_special_key(serialized_input_event.key, special_key); + ekg::special_key special_key {ekg::special_key::unknown}; + ekg::p_core->p_platform_base->get_special_key(platform_event.key, special_key); - if (special_key != ekg::special_key_type::unknown) { + if (special_key != ekg::special_key::unknown) { this->special_keys[static_cast(special_key)][0] = key_name[0]; string_builder += key_name; @@ -253,20 +256,20 @@ void ekg::service::input::on_event() { break; } - case ekg::io::input_event_type::key_up: { + case ekg::io::event_type::key_up: { this->input.was_released = true; std::string key_name {}; std::string string_builder {}; - ekg::p_core->p_os_platform->get_key_name( - serialized_input_event.key, + ekg::p_core->p_platform_base->get_key_name( + platform_event.key, key_name ); - ekg::special_key_type special_key {ekg::special_key_type::unknown}; - ekg::p_core->p_os_platform->get_special_key(serialized_input_event.key, special_key); + ekg::special_key special_key {ekg::special_key::unknown}; + ekg::p_core->p_platform_base->get_special_key(platform_event.key, special_key); - if (special_key != ekg::special_key_type::unknown) { + if (special_key != ekg::special_key::unknown) { this->special_keys[static_cast(special_key)][0] = '\0'; string_builder += key_name; @@ -295,7 +298,7 @@ void ekg::service::input::on_event() { break; } - case ekg::io::input_event_type::mouse_button_down: { + case ekg::io::event_type::mouse_button_down: { std::string string_builder {}; std::string key_name {"mouse"}; @@ -303,14 +306,14 @@ void ekg::service::input::on_event() { this->input_released_list.push_back(key_name); key_name = "mouse-"; - key_name += std::to_string(serialized_input_event.mouse_button); + key_name += std::to_string(platform_event.mouse_button); this->input.was_pressed = true; this->complete_with_units(string_builder, key_name); this->set_input_state(string_builder, true); this->input_released_list.push_back(string_builder); - bool double_click_factor {ekg::reach(&this->double_interact, 500)}; + bool double_click_factor {ekg::reach(this->double_interact, 500)}; if (!double_click_factor) { string_builder += "-double"; this->set_input_state(string_builder, true); @@ -320,13 +323,13 @@ void ekg::service::input::on_event() { } if (double_click_factor) { - ekg::reset(&this->double_interact); + ekg::reset(this->double_interact); } break; } - case ekg::io::input_event_type::mouse_button_up: { + case ekg::io::event_type::mouse_button_up: { std::string string_builder {}; std::string key_name {"mouse-up"}; @@ -335,7 +338,7 @@ void ekg::service::input::on_event() { this->input_released_list.push_back(key_name); key_name = "mouse-"; - key_name += std::to_string(serialized_input_event.mouse_button); + key_name += std::to_string(platform_event.mouse_button); this->complete_with_units(string_builder, key_name); string_builder += "-up"; @@ -345,24 +348,24 @@ void ekg::service::input::on_event() { break; } - case ekg::io::input_event_type::mouse_motion: { + case ekg::io::event_type::mouse_motion: { this->input.has_motion = true; - this->input.interact.x = static_cast(serialized_input_event.mouse_motion_x); - this->input.interact.y = static_cast(serialized_input_event.mouse_motion_y); + this->input.interact.x = static_cast(platform_event.mouse_motion_x); + this->input.interact.y = static_cast(platform_event.mouse_motion_y); break; } - case ekg::io::input_event_type::mouse_wheel: { + case ekg::io::event_type::mouse_wheel: { std::string string_builder {}; this->complete_with_units(string_builder, "mouse-wheel"); this->set_input_state(string_builder, true); this->input_released_list.push_back(string_builder); this->input.was_wheel = true; - this->set_input_state("mouse-wheel-up", serialized_input_event.mouse_wheel_y > 0); - this->set_input_state("mouse-wheel-down", serialized_input_event.mouse_wheel_y < 0); - this->set_input_state("mouse-wheel-right", serialized_input_event.mouse_wheel_x > 0); - this->set_input_state("mouse-wheel-left", serialized_input_event.mouse_wheel_x < 0); + this->set_input_state("mouse-wheel-up", platform_event.mouse_wheel_y > 0); + this->set_input_state("mouse-wheel-down", platform_event.mouse_wheel_y < 0); + this->set_input_state("mouse-wheel-right", platform_event.mouse_wheel_x > 0); + this->set_input_state("mouse-wheel-left", platform_event.mouse_wheel_x < 0); /** * I do not know how actually implement smooth scroll, @@ -373,41 +376,41 @@ void ekg::service::input::on_event() { **/ wheel_precise_interval = static_cast( - 1000 - ekg::clamp(ekg::interval(&this->last_time_wheel_was_fired), 0, 1000) + 1000 - ekg::clamp(ekg::interval(this->last_time_wheel_was_fired), 0, 1000) ); wheel_precise_interval = (wheel_precise_interval / 1000.0f); wheel_precise_interval = wheel_precise_interval + (static_cast(wheel_precise_interval > 0.99) * 0.5f); wheel_precise_interval = ekg::clamp_min(wheel_precise_interval, 0.2f); - this->input.interact.z = serialized_input_event.mouse_wheel_precise_x * wheel_precise_interval; - this->input.interact.w = serialized_input_event.mouse_wheel_precise_y * wheel_precise_interval; + this->input.interact.z = platform_event.mouse_wheel_precise_x * wheel_precise_interval; + this->input.interact.w = platform_event.mouse_wheel_precise_y * wheel_precise_interval; - ekg::reset(&this->last_time_wheel_was_fired); + ekg::reset(this->last_time_wheel_was_fired); break; } - case ekg::io::input_event_type::finger_down: { + case ekg::io::event_type::finger_down: { this->input.was_pressed = true; - ekg::reset(&this->input.timing_last_interact); - bool reach_double_interact {ekg::reach(&this->double_interact, 500)}; + ekg::reset(this->input.timing_last_interact); + bool reach_double_interact {ekg::reach(this->double_interact, 500)}; - this->input.interact.x = serialized_input_event.finger_x * static_cast(ekg::viewport.w); - this->input.interact.y = serialized_input_event.finger_y * static_cast(ekg::viewport.h); + this->input.interact.x = platform_event.finger_x * static_cast(ekg::dpi.viewport.w); + this->input.interact.y = platform_event.finger_y * static_cast(ekg::dpi.viewport.h); this->set_input_state("finger-click", true); this->set_input_state("finger-click-double", !reach_double_interact); if (reach_double_interact) { - ekg::reset(&this->double_interact); + ekg::reset(this->double_interact); } break; } - case ekg::io::input_event_type::finger_up: { + case ekg::io::event_type::finger_up: { this->input.was_released = true; - this->set_input_state("finger-hold", (this->finger_hold_event = ekg::reach(&this->input.timing_last_interact, 750))); + this->set_input_state("finger-hold", (this->finger_hold_event = ekg::reach(this->input.timing_last_interact, 750))); this->set_input_state("finger-click", false); this->set_input_state("finger-click-double", false); @@ -418,21 +421,21 @@ void ekg::service::input::on_event() { this->set_input_state("finger-swipe-up", false); this->set_input_state("finger-swipe-down", false); - this->input.interact.x = serialized_input_event.finger_x * static_cast(ekg::viewport.w); - this->input.interact.y = serialized_input_event.finger_y * static_cast(ekg::viewport.h); + this->input.interact.x = platform_event.finger_x * static_cast(ekg::dpi.viewport.w); + this->input.interact.y = platform_event.finger_y * static_cast(ekg::dpi.viewport.h); this->input.interact.z = 0.0f; this->input.interact.w = 0.0f; break; } - case ekg::io::input_event_type::finger_motion: { + case ekg::io::event_type::finger_motion: { this->input.has_motion = true; - this->input.interact.x = serialized_input_event.finger_x * static_cast(ekg::viewport.w); - this->input.interact.y = serialized_input_event.finger_y * static_cast(ekg::viewport.h); + this->input.interact.x = platform_event.finger_x * static_cast(ekg::dpi.viewport.w); + this->input.interact.y = platform_event.finger_y * static_cast(ekg::dpi.viewport.h); - this->input.interact.z = (serialized_input_event.finger_dx * (static_cast(ekg::viewport.w) / 9.0f)); - this->input.interact.w = (serialized_input_event.finger_dy * static_cast(ekg::viewport.h) / 9.0f); + this->input.interact.z = (platform_event.finger_dx * (static_cast(ekg::dpi.viewport.w) / 9.0f)); + this->input.interact.w = (platform_event.finger_dy * static_cast(ekg::dpi.viewport.h) / 9.0f); float swipe_factor = 0.01f; @@ -446,7 +449,7 @@ void ekg::service::input::on_event() { this->set_input_state("finger-swipe-down", this->input.interact.w < -swipe_factor); this->finger_swipe_event = true; - ekg::reset(&this->input.timing_last_interact); + ekg::reset(this->input.timing_last_interact); break; } } @@ -460,7 +463,7 @@ void ekg::service::input::on_event() { } } -void ekg::service::input::on_update() { +void ekg::handler::input::update() { #if defined(EKG_INPUT_DEBUG) ekg::log() << "scroll_speed: " << this->input.scroll_speed << "\n" @@ -472,7 +475,7 @@ void ekg::service::input::on_update() { << "was_typed: " << this->input.was_typed; #endif - ekg::reset_if_reach(&this->input.ui_timing, 1000); + ekg::reset_if_reach(this->input.ui_timing, 1000); ekg::timing_t::second = this->input.ui_timing.elapsed_ticks; if (this->input.was_wheel) { @@ -521,12 +524,12 @@ void ekg::service::input::on_update() { } } -void ekg::service::input::insert_input_bind( +void ekg::handler::input::insert_input_bind( std::string_view tag, std::string_view input ) { std::vector &bind_list {this->input_bindings_map[input.data()]}; - ekg::io::input_bind_t &input_bind {this->input_bind_map[tag.data()]}; + ekg::input_bind_t &input_bind {this->input_bind_map[tag.data()]}; bool *p_address {&input_bind.state}; bool must_bind {true}; @@ -544,12 +547,12 @@ void ekg::service::input::insert_input_bind( } } -void ekg::service::input::erase_input_bind( +void ekg::handler::input::erase_input_bind( std::string_view tag, std::string_view input ) { std::vector &bind_list {this->input_bindings_map[input.data()]}; - ekg::io::input_bind_t &input_bind {this->input_bind_map[tag.data()]}; + ekg::input_bind_t &input_bind {this->input_bind_map[tag.data()]}; bool *p_address {&input_bind.state}; bool was_erased {}; @@ -574,10 +577,10 @@ void ekg::service::input::erase_input_bind( } } -void ekg::service::input::erase_input_bind( +void ekg::handler::input::erase_input_bind( std::string_view tag ) { - ekg::io::input_bind_t &input_bind {this->input_bind_map[tag.data()]}; + ekg::input_bind_t &input_bind {this->input_bind_map[tag.data()]}; bool *p_address {&input_bind.state}; for (size_t it {}; it < input_bind.registry.size(); it++) { @@ -598,7 +601,7 @@ void ekg::service::input::erase_input_bind( this->input_bind_map.erase(tag.data()); } -void ekg::service::input::set_input_state( +void ekg::handler::input::set_input_state( std::string_view key, bool state ) { @@ -613,25 +616,25 @@ void ekg::service::input::set_input_state( } } -void ekg::service::input::complete_with_units( +void ekg::handler::input::complete_with_units( std::string &string_builder, std::string_view key_name ) { - string_builder += this->special_keys[static_cast(ekg::special_key_type::left_ctrl)]; - string_builder += this->special_keys[static_cast(ekg::special_key_type::right_ctrl)]; - string_builder += this->special_keys[static_cast(ekg::special_key_type::left_shift)]; - string_builder += this->special_keys[static_cast(ekg::special_key_type::right_shift)]; - string_builder += this->special_keys[static_cast(ekg::special_key_type::left_alt)]; - string_builder += this->special_keys[static_cast(ekg::special_key_type::right_alt)]; - string_builder += this->special_keys[static_cast(ekg::special_key_type::tab)]; + string_builder += this->special_keys[static_cast(ekg::special_key::left_ctrl)]; + string_builder += this->special_keys[static_cast(ekg::special_key::right_ctrl)]; + string_builder += this->special_keys[static_cast(ekg::special_key::left_shift)]; + string_builder += this->special_keys[static_cast(ekg::special_key::right_shift)]; + string_builder += this->special_keys[static_cast(ekg::special_key::left_alt)]; + string_builder += this->special_keys[static_cast(ekg::special_key::right_alt)]; + string_builder += this->special_keys[static_cast(ekg::special_key::tab)]; string_builder += key_name; } -void ekg::service::input::set_input_bind_state( +void ekg::handler::input::set_input_bind_state( std::string_view key, bool state ) { - ekg::io::input_bind_t &input_bind { + ekg::input_bind_t &input_bind { this->input_bind_map[key.data()] }; @@ -643,7 +646,7 @@ void ekg::service::input::set_input_bind_state( this->just_fired_input_bind.emplace_back(input_bind.p_address); } -bool ekg::service::input::contains_unit( +bool ekg::handler::input::contains_unit( std::string_view label ) { for (std::string &units : this->special_keys_unit_pressed) { @@ -655,13 +658,13 @@ bool ekg::service::input::contains_unit( return false; } -bool ekg::service::input::get_input_state( +bool ekg::handler::input::get_input_state( std::string_view input ) { return this->input_map[input.data()]; } -bool ekg::service::input::get_input_bind_state( +bool ekg::handler::input::get_input_bind_state( std::string_view tag ) { return this->input_bind_map[tag.data()].state; diff --git a/include/ekg/ui/menu/ui_menu.hpp b/src/handler/theme.cpp similarity index 70% rename from include/ekg/ui/menu/ui_menu.hpp rename to src/handler/theme.cpp index ddc68e41..b474841d 100644 --- a/include/ekg/ui/menu/ui_menu.hpp +++ b/src/handler/theme.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,17 +21,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/handler/theme.hpp" +#include "ekg/core/runtime.hpp" -#ifndef EKG_UI_MENU_H -#define EKG_UI_MENU_H +ekg::theme_t &ekg::theme( + std::string_view tag +) { + if (tag.empty()) { + return ekg::p_core->handler_theme.get_current_theme(); + } -#include "ekg/ui/abstract/ui_abstract.hpp" - -namespace ekg::ui { - class menu : public ekg::ui::abstract { - protected: - public: - }; + return ekg::p_core->handler_theme.registry(tag); } -#endif \ No newline at end of file +ekg::theme_t &ekg::set_current_theme( + std::string_view tag +) { + ekg::p_core->handler_theme.set_current_theme(tag); + return ekg::theme(tag); +} diff --git a/src/handler/theme/handler.cpp b/src/handler/theme/handler.cpp new file mode 100644 index 00000000..c3a34b8b --- /dev/null +++ b/src/handler/theme/handler.cpp @@ -0,0 +1,63 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/handler/theme/handler.hpp" +#include "ekg/io/log.hpp" + +void ekg::handler::theme::init() { + ekg::log() << "Initializing default themes"; + + ekg::theme_t light_pinky_theme { + .tag = "light-pinky", + .author = "Rina Wilk", + .description = "Pasted light-theme... moow", + }; + + light_pinky_theme.frame_color_scheme.background = {242, 242, 242, 255}; + light_pinky_theme.frame_color_scheme.highlight = {242, 242, 242, 0}; + light_pinky_theme.frame_color_scheme.outline = {190, 190, 190, 0}; + light_pinky_theme.frame_color_scheme.active = {242, 242, 242, 0}; + light_pinky_theme.frame_color_scheme.focused_background = {242, 242, 242, 0}; + light_pinky_theme.frame_color_scheme.focused_outline = {242, 242, 242, 0}; + light_pinky_theme.frame_color_scheme.warning_outline = {242, 242, 0, 100}; + light_pinky_theme.frame_color_scheme.actions_margin_pixel_thickness = 18; + + this->registry(light_pinky_theme.tag) = light_pinky_theme; + this->set_current_theme(light_pinky_theme.tag); +} + +void ekg::handler::theme::quit() { +} + +ekg::theme_t &ekg::handler::theme::registry(const std::string_view &tag) { + return this->themes[tag]; +} + +ekg::theme_t &ekg::handler::theme::set_current_theme(const std::string_view &tag) { + this->current_theme_tag = tag; + return this->themes[tag]; +} + +ekg::theme_t &ekg::handler::theme::get_current_theme() { + return this->themes[this->current_theme_tag]; +} diff --git a/src/io/algorithm.cpp b/src/io/algorithm.cpp deleted file mode 100644 index 34ca60af..00000000 --- a/src/io/algorithm.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "ekg/io/algorithm.hpp" -#include "ekg/io/log.hpp" - -#include - -ekg::flags_t ekg::add_child_to_parent( - ekg::properties_t *p_parent, - ekg::properties_t *p_child -) { - if (p_child == nullptr || p_parent == nullptr) { - ekg::log() << "Failed to add child to parent, `null`, `null`"; - return ekg::result::failed; - } - - if ( - p_child->p_parent != nullptr - && - p_child->p_parent->unique_id == p_parent->unique_id - ) { - return ekg::result::success; - } - - p_child->p_parent = p_parent; - p_child->p_abs_parent = p_parent->p_abs_parent; - p_parent->children.push_back(p_child); - - if (p_parent->is_docknizable) { - ekg::ui::abstract *p_child_widget { - static_cast(p_child->p_widget) - }; - - ekg::ui::abstract *p_parent_widget { - static_cast(p_parent->p_widget) - }; - - p_child_widget->p_parent_rect = &p_parent_widget->properties.rect; - p_child_widget->p_parent_scissor_rect = &p_parent_widget->scissor; - } - - return ekg::result::success; -} - -ekg::properties_t *ekg::find( - ekg::stack_t *p_stack, - std::string_view widget_tag -) { - if (p_stack == nullptr || widget_tag.empty()) { - ekg::log() << "Could not find any widget, may `` (empty) widget_tag, may `null` p_stack"; - return nullptr; - } - - for (ekg::ui::abstract *&p_widget : p_stack->children) { - if (p_widget != nullptr && p_widget->properties.tag == widget_tag) { - return &p_widget->properties; - } - } - - return nullptr; -} - -ekg::flags_t ekg::destroy( - ekg::stack_t *p_stack, - ekg::properties_t *p_properties -) { - if (p_stack == nullptr) { - ekg::log() << "Failed to destroy widget, `null` p_stack"; - return ekg::result::failed; - } - - if (p_properties == nullptr) { - ekg::log() << "Failed to destroy widget, `null` p_properties"; - return ekg::result::failed; - } - - uint64_t counter { - p_stack->counter++ - }; - - p_properties->is_alive = false; - - if (p_properties->p_parent->is_docknizable) { - ekg::ui::abstract *p_widget { - static_cast(p_properties->p_widget) - }; - - p_widget->p_parent_rect = &p_widget->_blank_parent_rect; - p_widget->p_scroll_vec = &p_widget->_blank_scroll_vec; - } - - for (ekg::properties_t *&p_child : p_properties->children) { - ekg::destroy(p_stack, p_child); - } - - if (counter == 0) { - p_stack->counter = 0; - - if (p_properties->p_parent != nullptr) { - std::vector &parent_of_parent_children { - p_properties->p_parent->children - }; - - parent_of_parent_children.erase( - std::remove_if( - parent_of_parent_children.begin(), - parent_of_parent_children.end(), - [p_properties](ekg::properties_t *&p_parent) { - return p_parent->unique_id == p_properties->unique_id; - } - ) - ); - } - } - - return ekg::result::success; -} - -ekg::flags_t ekg::find_and_destroy( - ekg::stack_t *p_stack, - std::string_view widget_tag -) { - if (p_stack == nullptr) { - ekg::log() << "Failed to destroy widget, `null` stack"; - return ekg::result::failed; - } - - return ekg::destroy(p_stack, ekg::find(p_stack, widget_tag)); -} - -ekg::flags_t ekg::io::push_back_widget_tree_recursively( - ekg::io::target_collector_t *p_target_collector, - ekg::ui::abstract *p_widget -) { - if (p_widget == nullptr || p_target_collector == nullptr) { - ekg::log() << "Failed to push back widget tree, may `null` p_widget, may `null` p_target_collector"; - return ekg::result::failed; - } - - if ( - p_target_collector->unique_id - == - p_widget->properties.unique_id - ) { - p_target_collector->unique_id = p_widget->properties.unique_id; - p_target_collector->was_target_found = true; - } - - p_target_collector->storage.push_back(p_widget); - - for (ekg::properties_t *&p_properties : p_widget->properties.children) { - if ( - p_properties == nullptr - || - p_properties->p_widget == nullptr - ) { - continue; - } - - ekg::io::push_back_widget_tree_recursively( - p_target_collector, - static_cast(p_properties->p_widget) - ); - } - - return ekg::result::success; -} diff --git a/src/io/design.cpp b/src/io/design.cpp deleted file mode 100644 index 1ea0c574..00000000 --- a/src/io/design.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "ekg/io/design.hpp" -#include "ekg/core/runtime.hpp" - -std::map &ekg::themes() { - return ekg::p_core->service_theme.get_theme_map(); -} - -ekg::theme_t &ekg::theme(std::string_view name) { - if (name.empty()) { - return ekg::p_core->service_theme.get_current_theme(); - } - - return ekg::themes()[name]; -} - -void ekg::theme(ekg::theme_t theme) { - return ekg::p_core->service_theme.add(theme); -} - -ekg::flags_t ekg::set_current_theme(std::string_view name) { - return ekg::p_core->service_theme.set_current_theme(name); -} diff --git a/src/io/event.cpp b/src/io/event.cpp new file mode 100644 index 00000000..98b49a50 --- /dev/null +++ b/src/io/event.cpp @@ -0,0 +1,85 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/io/event.hpp" +#include "ekg/core/runtime.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/core/context.hpp" + +void ekg::io::dispatch( + ekg::io::operation op, + ekg::at_t &property_at +) { + ekg::property_t &property { + ekg::query(property_at) + }; + + if ( + op != ekg::io::operation::swap + && + op != ekg::io::operation::scalenize + && + property == ekg::property_t::not_found + ) { + return; + } + + switch (op) { + case ekg::io::operation::swap: + if (property != ekg::property_t::not_found) { + ekg::gui.bind.swap_at = property_at; + } + + ekg::p_core->handler_callback.dispatch( + static_cast(op) + ); + break; + case ekg::io::operation::reload: + if (property.operation.should_docknize) return; + property.operation.should_reload = true; + ekg::p_core->reload.push_back(property_at); + ekg::p_core->handler_callback.dispatch( + static_cast(op) + ); + break; + case ekg::io::operation::docknize: + if (property.operation.should_docknize) return; + property.operation.should_docknize = true; + ekg::p_core->docknize.push_back(property_at); + ekg::p_core->handler_callback.dispatch( + static_cast(op) + ); + break; + case ekg::io::operation::scalenize: + ekg::p_core->handler_callback.dispatch( + static_cast(op) + ); + break; + case ekg::io::operation::high_frequency: + property.widget.is_high_frequency = true; // make sure this tick is true + if (property.operation.should_enable_high_frequency) return; + property.operation.should_enable_high_frequency = true; + ekg::p_core->high_frequency.push_back(property_at); + break; + } +} diff --git a/src/io/typography.cpp b/src/io/font.cpp similarity index 56% rename from src/io/typography.cpp rename to src/io/font.cpp index adbc21cd..9995d12f 100644 --- a/src/io/typography.cpp +++ b/src/io/font.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,44 +22,40 @@ * SOFTWARE. */ -#include "ekg/io/typography.hpp" -#include "ekg/core/context.hpp" +#include "ekg/io/font.hpp" +#include "ekg/core/runtime.hpp" #include "ekg/io/log.hpp" -ekg::flags_t ekg::io::refresh_font_face( - ekg::io::font_face_t *p_font_face +ekg::flags_t ekg::io::font( + ekg::io::font_face_t &font_face ) { - if (p_font_face == nullptr) { - return ekg::result::failed; - } - - if (p_font_face->was_face_changed) { - if (p_font_face->was_loaded) { - FT_Done_Face(p_font_face->ft_face); - p_font_face->was_loaded = false; + if (font_face.was_face_changed) { + if (font_face.was_loaded) { + FT_Done_Face(font_face.ft_face); + font_face.was_loaded = false; } - p_font_face->was_loaded = FT_New_Face( - ekg::freetype_library, - p_font_face->path.data(), + font_face.was_loaded = FT_New_Face( + ekg::p_core->ft_library, + font_face.path.data(), 0, - &p_font_face->ft_face + &font_face.ft_face ); - if (p_font_face->was_loaded) { - ekg::log() << "Could not load font " << p_font_face->path; + if (font_face.was_loaded) { + ekg::log() << "Could not load font " << font_face.path; return ekg::result::failed; } - ekg::log() << "Font '" << p_font_face->path << "' loaded!"; + ekg::log() << "Font '" << font_face.path << "' loaded!"; - p_font_face->was_loaded = true; - p_font_face->was_face_changed = false; + font_face.was_loaded = true; + font_face.was_face_changed = false; } - if (p_font_face->was_loaded && p_font_face->was_size_changed) { - FT_Set_Pixel_Sizes(p_font_face->ft_face, 0, p_font_face->size); - p_font_face->was_size_changed = false; + if (font_face.was_loaded && font_face.was_size_changed) { + FT_Set_Pixel_Sizes(font_face.ft_face, 0, font_face.size); + font_face.was_size_changed = false; } return ekg::result::success; diff --git a/src/io/gpu.cpp b/src/io/gpu.cpp deleted file mode 100644 index 6d8a289d..00000000 --- a/src/io/gpu.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "ekg/core/runtime.hpp" - -ekg::flags_t ekg::gpu_allocate_sampler( - ekg::sampler_allocate_info_t *p_sampler_allocate_info, - ekg::sampler_t *p_sampler -) { - return ekg::p_core->p_gpu_api->allocate_sampler( - p_sampler_allocate_info, - p_sampler - ); -} - -ekg::flags_t ekg::gpu_fill_sampler( - ekg::sampler_fill_info_t *p_sampler_fill_info, - ekg::sampler_t *p_sampler -) { - return ekg::p_core->p_gpu_api->fill_sampler( - p_sampler_fill_info, - p_sampler - ); -} - -ekg::flags_t ekg::image_src_r8_convert_to_r8g8b8a8( - ekg::vec2_t size, - const unsigned char *p_src, - std::vector &dst -) { - if (size.x == 0 || size.y == 0 || p_src == nullptr || dst.empty()) { - return ekg::result::failed; - } - - size_t index {}; - for (size_t it {}; it < (size.x * size.y); it++) { - const unsigned char &char8_red_color { - p_src[it] - }; - - index = it * 4; - - if (index == dst.size()) { - break; - } - - /** - * may I be wrong? but the format is ARGB and not RGBA, - * I do not know. - **/ - - dst.at(index + 0) = char8_red_color; - dst.at(index + 1) = 255; - dst.at(index + 2) = 255; - dst.at(index + 3) = 255; - } - - return ekg::result::success; -} diff --git a/src/io/log.cpp b/src/io/log.cpp deleted file mode 100644 index 932220bd..00000000 --- a/src/io/log.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "ekg/io/log.hpp" - -std::ostringstream ekg::log::buffer {}; -bool ekg::log::buffered {}; -bool ekg::log::tracked {}; - -int64_t ekg::timing_t::second {}; -int64_t ekg::timing_t::ticks {}; - -bool ekg::reach(ekg::timing_t *p_timing, int64_t ms) { - p_timing->ticks_going_on = ekg::timing_t::ticks; - p_timing->current_ticks = p_timing->ticks_going_on - p_timing->elapsed_ticks; - return p_timing->current_ticks > ms; -} - -bool ekg::reset(ekg::timing_t *p_timing) { - p_timing->elapsed_ticks = p_timing->ticks_going_on; - return true; -} - -bool ekg::reset_if_reach(ekg::timing_t *p_timing, int64_t ms) { - p_timing->ticks_going_on = ekg::timing_t::ticks; - p_timing->current_ticks = p_timing->ticks_going_on - p_timing->elapsed_ticks; - - if (p_timing->current_ticks > ms) { - p_timing->elapsed_ticks = p_timing->ticks_going_on; - return true; - } - - return false; -} - -bool ekg::extend(ekg::timing_t *p_timing, int64_t ms) { - p_timing->elapsed_ticks = p_timing->ticks_going_on - ms; - return true; -} - -int64_t ekg::interval(ekg::timing_t *p_timing) { - p_timing->ticks_going_on = ekg::timing_t::ticks; - return ( - p_timing->current_ticks = p_timing->ticks_going_on - p_timing->elapsed_ticks - ); -} diff --git a/src/io/memory.cpp b/src/io/memory.cpp new file mode 100644 index 00000000..edfcf153 --- /dev/null +++ b/src/io/memory.cpp @@ -0,0 +1,82 @@ +/** + * MIT License + + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/io/memory.hpp" +#include "ekg/core/context.hpp" +#include "ekg/core/pools.hpp" + +ekg::at_t ekg::at_t::not_found {}; + +ekg::signed_address_info_t ekg::sign {}; + +void ekg::map(void *pv_address) { + if (pv_address == nullptr) { + ekg::sign.current = ekg::not_found; + return; + } + + ekg::stack_t ¤t_stack { + ekg::query(ekg::gui.bind.stack_at) + }; + + if (current_stack == ekg::stack_t::not_found) { + ekg::sign.current = ekg::not_found; + return; + } + size_t size {ekg::sign.list.size()}; + for (size_t it {}; it < size; it++) { + ekg::mapped_address_sign_info_t &info {ekg::sign.list.at(it)}; + if (info.pv_address == pv_address) { + ekg::sign.current = it; + return; + } + } + + ekg::sign.current = ekg::sign.list.size(); + ekg::sign.list.push_back({.ats = {}, .pv_address = pv_address}); +} + +void ekg::unmap(void *pv_address) { + if (pv_address) { + return; + } + + size_t size {ekg::sign.list.size()}; + for (size_t it {}; it < size; it++) { + ekg::mapped_address_sign_info_t &info {ekg::sign.list.at(it)}; + if (info.pv_address == pv_address) { + for (ekg::at_t &at : info.ats) { + ekg_abstract_todo( + at.flags, + at, + ekg::ui::unmap(descriptor); + ); + } + + ekg::sign.list.erase( + ekg::sign.list.begin() + it + ); + break; + } + } +} diff --git a/src/io/safety.cpp b/src/io/safety.cpp deleted file mode 100644 index 253d42ea..00000000 --- a/src/io/safety.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "ekg/io/safety.hpp" - -void ekg::pop(ekg::properties_t *p_properties) { - ekg::p_core->set_current_parent_properties( - p_properties - ); -} diff --git a/src/service/handler.cpp b/src/io/timing.cpp similarity index 51% rename from src/service/handler.cpp rename to src/io/timing.cpp index 12c1fc9c..d7170a58 100644 --- a/src/service/handler.cpp +++ b/src/io/timing.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,45 +21,42 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/io/timing.hpp" -#include "ekg/service/handler.hpp" -#include "ekg/io/log.hpp" +int64_t ekg::timing_t::second {}; +int64_t ekg::timing_t::ticks {}; -void ekg::service::handler::init() { - ekg::log() << "Initialising handler-service task system-based"; +bool ekg::reach(ekg::timing_t &timing, int64_t ms) { + timing.ticks_going_on = ekg::timing_t::ticks; + timing.current_ticks = timing.ticks_going_on - timing.elapsed_ticks; + return timing.current_ticks > ms; } -void ekg::service::handler::quit() { - ekg::log() << "Quitting handler-service"; +bool ekg::reset(ekg::timing_t &timing) { + timing.elapsed_ticks = timing.ticks_going_on; + return true; } -ekg::task_t *&ekg::service::handler::allocate() { - return this->pre_allocated_task_list.emplace_back(); -} - -void ekg::service::handler::dispatch(ekg::task_t *p_task) { - if (!p_task->was_dispatched) { - p_task->was_dispatched = true; - this->task_queue.push(p_task); +bool ekg::reset_if_reach(ekg::timing_t &timing, int64_t ms) { + timing.ticks_going_on = ekg::timing_t::ticks; + timing.current_ticks = timing.ticks_going_on - timing.elapsed_ticks; + + if (timing.current_ticks > ms) { + timing.elapsed_ticks = timing.ticks_going_on; + return true; } -} -void ekg::service::handler::dispatch_pre_allocated_task(uint64_t index) { - ekg::task_t *&p_task { - this->pre_allocated_task_list.at(index) - }; + return false; +} - if (!p_task->was_dispatched) { - this->task_queue.push(p_task); - p_task->was_dispatched = true; - } +bool ekg::extend(ekg::timing_t &timing, int64_t ms) { + timing.elapsed_ticks = timing.ticks_going_on - ms; + return true; } -void ekg::service::handler::on_update() { - while (!this->task_queue.empty()) { - ekg::task_t *p_task {this->task_queue.front()}; - p_task->function(p_task->info); - p_task->was_dispatched = false; - this->task_queue.pop(); - } +int64_t ekg::interval(ekg::timing_t &timing) { + timing.ticks_going_on = ekg::timing_t::ticks; + return ( + timing.current_ticks = timing.ticks_going_on - timing.elapsed_ticks + ); } diff --git a/src/io/text.cpp b/src/io/utf.cpp similarity index 79% rename from src/io/text.cpp rename to src/io/utf.cpp index 2b25dd98..ad51b8eb 100644 --- a/src/io/text.cpp +++ b/src/io/utf.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,17 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/io/utf.hpp" #include -#include -#include +#include #include -#include "ekg/io/text.hpp" -#include "ekg/math/geometry.hpp" -#include "ekg/io/log.hpp" - -uint64_t ekg::utf_check_sequence( +uint64_t ekg::utf8_check_sequence( uint8_t &char8, char32_t &char32, std::string &utf_string, @@ -44,24 +40,22 @@ uint64_t ekg::utf_check_sequence( return 0; } else if ((char8 & 0xE0) == 0xC0) { utf_string = string.substr(index, 2); - char32 = ekg::utf_string_to_char32(utf_string); + char32 = ekg::utf8_to_utf32(utf_string); return 1; } else if ((char8 & 0xF0) == 0xE0) { utf_string = string.substr(index, 3); - char32 = ekg::utf_string_to_char32(utf_string); + char32 = ekg::utf8_to_utf32(utf_string); return 2; } else if ((char8 & 0xF8) == 0xF0) { utf_string = string.substr(index, 4); - char32 = ekg::utf_string_to_char32(utf_string); + char32 = ekg::utf8_to_utf32(utf_string); return 3; } return 0; } -std::string ekg::utf_char32_to_string( - char32_t char32 -) { +std::string ekg::utf32_to_string(char32_t char32) { std::string result {}; if (char32 < 0x80) { @@ -87,9 +81,7 @@ std::string ekg::utf_char32_to_string( return result; } -char32_t ekg::utf_string_to_char32( - std::string_view string -) { +char32_t ekg::utf8_to_utf32(std::string_view string) { char32_t char32 {}; uint64_t it {}; @@ -114,9 +106,7 @@ char32_t ekg::utf_string_to_char32( return char32; } -uint64_t ekg::utf_length( - std::string_view utf_string -) { +uint64_t ekg::utf8_length(std::string_view utf_string) { if (utf_string.empty()) { return 0; } @@ -145,11 +135,7 @@ uint64_t ekg::utf_length( return string_size; } -std::string ekg::utf_substr( - std::string_view string, - uint64_t offset, - uint64_t size -) { +std::string ekg::utf8_substr(std::string_view string, uint64_t offset, uint64_t size) { if (string.empty() || size == 0) { return ""; } @@ -205,10 +191,7 @@ std::string ekg::utf_substr( return ""; } -void ekg::utf_decode( - std::string_view string, - std::vector &utf8_read -) { +void ekg::utf8_split_new_line(std::string_view string, std::vector &utf8_read) { if (string.empty()) { return; } @@ -232,9 +215,7 @@ void ekg::utf_decode( start_index = index + 1; } - if ( - !string.empty() - && + if (!string.empty() && !( string = string.substr(start_index, string.find('\0', start_index)) ).empty() @@ -244,36 +225,8 @@ void ekg::utf_decode( } } -std::string ekg::string_float64_precision( - double n, - int32_t precision -) { - std::string to_string_result {std::to_string(n)}; - return to_string_result.substr( - 0, - ekg::clamp_max( - static_cast(to_string_result.find('.') + precision + (1 * precision)), - static_cast(to_string_result.size()) - ) - ); -} - -std::string ekg::string_float_precision( - float n, - int32_t precision -) { - std::string to_string_result {std::to_string(n)}; - return to_string_result.substr( - 0, - ekg::clamp_max( - static_cast(to_string_result.find('.') + precision + (1 * precision)), - static_cast(to_string_result.size()) - ) - ); -} - -bool ekg::split( - std::vector &splitted_string_list, +bool ekg::utf8_split( + std::vector &splitted, const std::string &string, char find_char ) { @@ -281,8 +234,9 @@ bool ekg::split( std::string find_string {}; bool found_flag {}; + while (std::getline(ss, find_string, find_char)) { - splitted_string_list.push_back(find_string); + splitted.push_back(find_string); found_flag = true; } diff --git a/src/layout/dimension.cpp b/src/layout/dimension.cpp deleted file mode 100644 index 49253f8b..00000000 --- a/src/layout/dimension.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "ekg/layout/dimension.hpp" -#include "ekg/core/runtime.hpp" - -float ekg::layout::estimate_docknizable_height( - ekg::ui::abstract *p_parent_widget -) { - if ( - p_parent_widget == nullptr - || - !p_parent_widget->properties.is_docknizable - ) { - return 0.0f; - } - - ekg::theme_t ¤t_global_theme {ekg::p_core->service_theme.get_current_theme()}; - ekg::ui::abstract *p_widgets {}; - ekg::flags_t flags {}; - - float total_height {}; - float height {}; - - for (ekg::properties_t *&p_properties : p_parent_widget->properties.children) { - if ( - p_properties == nullptr - || - p_properties->p_widget == nullptr - ) { - continue; - } - - p_widgets = static_cast(p_properties->p_widget); - if (p_widgets->p_descriptor_rect == nullptr) { - continue; - } - - p_widgets->on_reload(); - - flags = p_widgets->properties.dock; - height = p_widgets->p_descriptor_rect->h; - - if ( - p_widgets->properties.is_docknizable - && - p_widgets->properties.children.empty() - ) { - height = ekg::layout::estimate_docknizable_height(p_widgets); - } - - total_height += ( - height - * - ( - ekg::fequalsf(total_height, 0.0f) - || - ekg::has(flags, ekg::dock::next) - ) - + - current_global_theme.layout_offset - ); - } - - total_height += ( - current_global_theme.layout_offset // top - + - current_global_theme.layout_offset // top - + - current_global_theme.layout_offset // bottom - + - current_global_theme.layout_offset // bottom - ); - - return total_height; -} diff --git a/src/layout/docknize.cpp b/src/layout/docknize.cpp index 5e772401..f5cc29b4 100644 --- a/src/layout/docknize.cpp +++ b/src/layout/docknize.cpp @@ -1,6 +1,31 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include "ekg/layout/docknize.hpp" #include "ekg/core/runtime.hpp" #include "ekg/layout/extentnize.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/math/floating_point.hpp" void ekg::layout::mask::preset( ekg::vec3_t offset, @@ -11,24 +36,26 @@ void ekg::layout::mask::preset( this->offset = offset; this->respective_all = initial_respective_size; - ekg::layout::extent_t::v_rect_descriptor = {}; - ekg::layout::extent_t::h_rect_descriptor = {}; + ekg::layout::extent_t::v_mask = {}; + ekg::layout::extent_t::h_mask = {}; } void ekg::layout::mask::insert( - ekg::rect_descriptor_t rect_descriptor + const ekg::layout::mask::component_t &component ) { - if (ekg::has(rect_descriptor.flags, ekg::dock::none)) { - rect_descriptor.p_rect->w = 0.0f; - rect_descriptor.p_rect->h = 0.0f; + if (ekg::has(component.dock, ekg::dock::none)) { + component.p_rect->w = 0.0f; + component.p_rect->h = 0.0f; return; } - this->rect_descriptor_list.push_back(rect_descriptor); + this->components.push_back(component); } void ekg::layout::mask::docknize() { + size_t size {}; int32_t count {}; + float dimensional_extent {}; float rect_height {}; float rect_width {}; @@ -69,7 +96,7 @@ void ekg::layout::mask::docknize() { switch (this->axis) { case ekg::axis::horizontal: - if (this->rect_descriptor_list.empty()) { + if (this->components.empty()) { this->mask.w = this->respective_all; this->mask.h = this->offset.z; return; @@ -90,26 +117,27 @@ void ekg::layout::mask::docknize() { center_right_corner.x = dimension_width / 2.0f; center_right_corner.w = this->offset.x; - for (size_t it {}; it < this->rect_descriptor_list.size(); it++) { - ekg::rect_descriptor_t &rect_descriptor {this->rect_descriptor_list.at(it)}; - if (rect_descriptor.p_rect == nullptr) { + size = this->components.size(); + for (size_t it {}; it < size; it++) { + ekg::layout::mask::component_t &component {this->components.at(it)}; + if (component.p_rect == nullptr) { continue; } - is_left = ekg::has(rect_descriptor.flags, ekg::dock::left); - is_right = ekg::has(rect_descriptor.flags, ekg::dock::right); - is_bottom = ekg::has(rect_descriptor.flags, ekg::dock::bottom); - is_top = ekg::has(rect_descriptor.flags, ekg::dock::top); - is_not_concat = !ekg::has(rect_descriptor.flags, ekg::dock::concat); + is_left = ekg::has(component.dock, ekg::dock::left); + is_right = ekg::has(component.dock, ekg::dock::right); + is_bottom = ekg::has(component.dock, ekg::dock::bottom); + is_top = ekg::has(component.dock, ekg::dock::top); + is_not_concat = !ekg::has(component.dock, ekg::dock::concat); is_bind_dimension_not_zero = (dimension_bind > 0.0f); - rect_width = rect_descriptor.p_rect->w; - rect_height = rect_descriptor.p_rect->h; + rect_width = component.p_rect->w; + rect_height = component.p_rect->h; - if (ekg::has(rect_descriptor.flags, ekg::dock::fill)) { + if (ekg::has(component.dock, ekg::dock::fill)) { count = 0; - ekg::layout::extentnize_rect_descriptor( - this->rect_descriptor_list, + ekg::layout::extentnize_mask( + this->components, this->offset, ekg::dock::fill, ekg::dock::none, @@ -130,11 +158,11 @@ void ekg::layout::mask::docknize() { } if (is_left) { - rect_descriptor.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + left_corner.w; - rect_descriptor.p_rect->w = rect_width; + component.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + left_corner.w; + component.p_rect->w = rect_width; dimension_bind += ( - ((this->offset.x * is_bind_dimension_not_zero) + ((rect_descriptor.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) + ((this->offset.x * is_bind_dimension_not_zero) + ((component.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) * is_not_concat ); @@ -142,11 +170,11 @@ void ekg::layout::mask::docknize() { left_corner.w += dimension_bind; this->mask.w += dimension_bind; } else if (is_right) { - rect_descriptor.p_rect->w = rect_width; - rect_descriptor.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + dimension_width - right_corner.w - rect_descriptor.p_rect->w; + component.p_rect->w = rect_width; + component.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + dimension_width - right_corner.w - component.p_rect->w; dimension_bind += ( - ((this->offset.x * is_bind_dimension_not_zero) + ((rect_descriptor.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) + ((this->offset.x * is_bind_dimension_not_zero) + ((component.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) * is_not_concat ); @@ -154,11 +182,11 @@ void ekg::layout::mask::docknize() { right_corner.w += dimension_bind; this->mask.w += dimension_bind; } else if (is_left) { - rect_descriptor.p_rect->w = rect_width; - rect_descriptor.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + center_left_corner.x - center_left_corner.w - rect_descriptor.p_rect->w; + component.p_rect->w = rect_width; + component.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + center_left_corner.x - center_left_corner.w - component.p_rect->w; dimension_bind += ( - ((this->offset.x * is_bind_dimension_not_zero) + ((rect_descriptor.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) + ((this->offset.x * is_bind_dimension_not_zero) + ((component.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) * is_not_concat ); @@ -166,34 +194,34 @@ void ekg::layout::mask::docknize() { center_left_corner.w += dimension_bind; this->mask.w += dimension_bind; } else if (is_right) { - rect_descriptor.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + center_right_corner.x + center_right_corner.w; - rect_descriptor.p_rect->w = rect_width; + component.p_rect->x = (is_bind_dimension_not_zero * this->offset.x) + center_right_corner.x + center_right_corner.w; + component.p_rect->w = rect_width; dimension_bind += ( - ((this->offset.x * is_bind_dimension_not_zero) + ((rect_descriptor.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) + ((this->offset.x * is_bind_dimension_not_zero) + ((component.p_rect->w + this->offset.x) * !is_bind_dimension_not_zero)) * is_not_concat ); center_right_corner.w += dimension_bind; this->mask.w += dimension_bind; - } else if (rect_descriptor.flags == ekg::dock::center) { - rect_descriptor.p_rect->w = rect_width; - rect_descriptor.p_rect->x = (dimension_width / 2.0f) - (rect_descriptor.p_rect->w / 2.0f); + } else if (component.dock == ekg::dock::center) { + component.p_rect->w = rect_width; + component.p_rect->x = (dimension_width / 2.0f) - (component.p_rect->w / 2.0f); } if (is_top | is_bottom) { - rect_descriptor.p_rect->y = ( + component.p_rect->y = ( is_top ? (this->offset.y) : (dimension_height - rect_height - this->offset.y) ); } else { - rect_descriptor.p_rect->y = ( + component.p_rect->y = ( (dimension_height / 2.0f) - (rect_height / 2.0f) ); } - if (!is_not_concat && rect_descriptor.p_rect->w > dimension_bind) { - dimension_bind = rect_descriptor.p_rect->w; + if (!is_not_concat && component.p_rect->w > dimension_bind) { + dimension_bind = component.p_rect->w; } else if (is_not_concat) { dimension_bind = 0.0f; } @@ -207,7 +235,7 @@ void ekg::layout::mask::docknize() { ); break; case ekg::axis::vertical: - if (this->rect_descriptor_list.empty()) { + if (this->components.empty()) { this->mask.w = this->offset.z; this->mask.h = this->respective_all; return; @@ -218,7 +246,7 @@ void ekg::layout::mask::docknize() { break; } - this->rect_descriptor_list.clear(); + this->components.clear(); } ekg::rect_t &ekg::layout::mask::get_rect() { @@ -226,40 +254,34 @@ ekg::rect_t &ekg::layout::mask::get_rect() { } void ekg::layout::docknize_widget( - ekg::ui::abstract *p_widget_parent + ekg::property_t &parent_property ) { if ( - p_widget_parent->p_descriptor_rect == nullptr - || - p_widget_parent == nullptr + parent_property == ekg::property_t::not_found || - !p_widget_parent->properties.is_docknizable + !parent_property.widget.is_children_docknizable ) { return; } - bool is_group {p_widget_parent->properties.type == ekg::type::frame}; - ekg::rect_t &abs_parent_rect {p_widget_parent->get_abs_rect()}; - - if (!is_group || abs_parent_rect.w == 0 || abs_parent_rect.h == 0) { + if ( + parent_property.widget.is_targeting_absolute_parent + && + parent_property.abs_parent_at != ekg::at_t::not_found + ) { + parent_property.widget.is_targeting_absolute_parent = false; + ekg::layout::docknize_widget(ekg::query(parent_property.abs_parent_at)); return; } - if (p_widget_parent->states.is_targeting_absolute_parent) { - p_widget_parent->states.is_targeting_absolute_parent = false; - - if (p_widget_parent->properties.p_abs_parent) { - ekg::layout::docknize_widget( - static_cast( - p_widget_parent->properties.p_abs_parent->p_widget - ) - ); - return; - } + if (parent_property.widget.rect.w == 0.0f || parent_property.widget.rect.h == 0.0f) { + return; } - ekg::rect_t container_rect {abs_parent_rect}; - ekg::theme_t ¤t_global_theme {ekg::theme()}; + parent_property.widget.is_targeting_absolute_parent = false; + + ekg::rect_t container_rect {parent_property.widget.rect}; + ekg::theme_t ¤t_global_theme {ekg::p_core->handler_theme.get_current_theme()}; float margin {static_cast(current_global_theme.layout_margin_thickness) * 2.0f}; container_rect.w -= margin; @@ -269,30 +291,29 @@ void ekg::layout::docknize_widget( * Pixel correction to make margin aligned and symmetric. **/ container_rect.w -= ( - static_cast(abs_parent_rect.w - container_rect.w) + static_cast(parent_property.widget.rect.w - container_rect.w) - current_global_theme.layout_margin_thickness ); if ( - p_widget_parent->states.is_scrolling.z + parent_property.scroll.is_scrolling.x || - p_widget_parent->states.is_scrolling.w + parent_property.scroll.is_scrolling.y ) { float nearest_scroll_bar_thickness { - static_cast(p_widget_parent->states.nearest_scroll_bar_thickness) + static_cast(parent_property.scroll.nearest_scroll_bar_thickness) }; - container_rect.w -= nearest_scroll_bar_thickness * static_cast(p_widget_parent->states.is_scrolling.w); - container_rect.h -= nearest_scroll_bar_thickness * static_cast(p_widget_parent->states.is_scrolling.z); + container_rect.w -= nearest_scroll_bar_thickness * static_cast(parent_property.scroll.is_scrolling.x); + container_rect.h -= nearest_scroll_bar_thickness * static_cast(parent_property.scroll.is_scrolling.y); } - ekg::ui::abstract *p_widgets {}; ekg::flags_t flags {}; float dimensional_extent {}; float extent {}; - int32_t it {}; + int32_t it {}; int32_t count {}; ekg::rect_t parent_offset { @@ -343,29 +364,31 @@ void ekg::layout::docknize_widget( float projected_pixel_perfect_width {pixel_perfect_projection.x + pixel_perfect_projection.w}; float unsolved_pixel_position {}; - for (ekg::properties_t *&p_properties : p_widget_parent->properties.children) { - if (p_properties == nullptr || p_properties->p_widget == nullptr) { - continue; - } + ekg::rect_t rect {}; - p_widgets = static_cast(p_properties->p_widget); - if (p_widgets->p_descriptor_rect == nullptr) { + for (ekg::at_t &at : parent_property.children) { + ekg::property_t &property {ekg::query(at)}; + if (property == ekg::property_t::not_found) { continue; } - if (p_widget_parent->properties.must_refresh_size) { - p_widgets->properties.must_refresh_size = true; + if (property.widget.should_refresh_size) { + property.widget.should_refresh_size = true; } - // @TODO Prevent useless scrolling reload. - p_widgets->on_reload(); + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, + ekg::ui::reload(property, descriptor); + flags = descriptor.dock; + rect = descriptor.rect; + ); - if (p_widgets->properties.type == ekg::type::scrollbar) { + if (property.at.flags == ekg::type::scrollbar) { it++; continue; } - flags = p_properties->dock; is_right = ekg::has(flags, ekg::dock::right); is_left = ekg::has(flags, ekg::dock::left) || !is_right; is_bottom = ekg::has(flags, ekg::dock::bottom); @@ -378,7 +401,7 @@ void ekg::layout::docknize_widget( if (is_fill) { count = it; ekg::layout::extentnize_widget( - p_widget_parent, + parent_property, ekg::dock::fill, ekg::dock::next | (is_top ? ekg::dock::bottom : ekg::dock::top), ekg::axis::horizontal, @@ -393,7 +416,7 @@ void ekg::layout::docknize_widget( current_global_theme.layout_offset, count ), - p_widgets->min_size.x + property.widget.min_size.x ); if (is_bottom) { @@ -401,14 +424,12 @@ void ekg::layout::docknize_widget( align = (align - pixel_perfect_projection.w) * (align > pixel_perfect_projection.w) * (extent > 0.0f); align > 0.0f && (align = (align / count)); } else { - if (p_properties->tag == "bt-2") ekg::log() << dimensional_extent << " meow " << pixel_perfect_projection.w; align = ((dimensional_extent + current_global_theme.layout_offset) * count) + (extent); - if (p_properties->tag == "bt-2") ekg::log() << align << " meow " << pixel_perfect_projection.w; align = (pixel_perfect_projection.w - align) * (align < pixel_perfect_projection.w) * (extent > 0.0f); align > 0.0f && (align = -(align / count)); } - p_widgets->p_descriptor_rect->w = dimensional_extent - align; + rect.w = dimensional_extent - align; should_reload_widget = true; should_estimate_extent = false; @@ -417,7 +438,7 @@ void ekg::layout::docknize_widget( switch (flags & ekg::dock::bottom) { case ekg::dock::bottom: if (ekg::fequalsf(corner_bottom_right.y, 0.0f)) { - highest_bottom = p_widgets->p_descriptor_rect->h; + highest_bottom = rect.h; corner_bottom_right.y += highest_bottom + current_global_theme.layout_offset; corner_bottom_left.y = corner_bottom_right.y; } @@ -432,8 +453,8 @@ void ekg::layout::docknize_widget( } if (is_left) { - p_widgets->p_descriptor_rect->x = corner_bottom_left.x; - p_widgets->p_descriptor_rect->y = ( + rect.x = corner_bottom_left.x; + rect.y = ( ekg::clamp_min( ekg::layout::transform_to_pixel_perfect_position( corner_top_right.y, @@ -445,7 +466,7 @@ void ekg::layout::docknize_widget( ) ); - corner_bottom_left.x += p_widgets->p_descriptor_rect->w + current_global_theme.layout_offset; + corner_bottom_left.x += rect.w + current_global_theme.layout_offset; } if (is_next && is_right) { @@ -458,15 +479,15 @@ void ekg::layout::docknize_widget( } if (is_right) { - corner_bottom_right.x += p_widgets->p_descriptor_rect->w; + corner_bottom_right.x += rect.w; - p_widgets->p_descriptor_rect->x = projected_pixel_perfect_width - corner_bottom_right.x; - p_widgets->p_descriptor_rect->y = container_rect.h - corner_bottom_right.y; + rect.x = projected_pixel_perfect_width - corner_bottom_right.x; + rect.y = container_rect.h - corner_bottom_right.y; corner_bottom_right.x += current_global_theme.layout_offset; } - highest_bottom = ekg::clamp_min(highest_bottom, p_widgets->p_descriptor_rect->h); + highest_bottom = ekg::clamp_min(highest_bottom, rect.h); break; default: if (is_next && is_left) { @@ -478,12 +499,10 @@ void ekg::layout::docknize_widget( } if (is_left) { - p_widgets->p_descriptor_rect->x = corner_top_left.x; - p_widgets->p_descriptor_rect->y = corner_top_left.y; - - if (p_properties->tag == "bt-2") ekg::log() << corner_top_left.x + p_widgets->p_descriptor_rect->w << " xoxo "; + rect.x = corner_top_left.x; + rect.y = corner_top_left.y; - corner_top_left.x += p_widgets->p_descriptor_rect->w + current_global_theme.layout_offset; + corner_top_left.x += rect.w + current_global_theme.layout_offset; } if (is_next && is_right) { @@ -495,8 +514,8 @@ void ekg::layout::docknize_widget( } if (is_right) { - corner_top_right.x += p_widgets->p_descriptor_rect->w; - p_widgets->p_descriptor_rect->x = ( + corner_top_right.x += rect.w; + rect.x = ( ekg::layout::transform_to_pixel_perfect_position( corner_top_left.x, corner_top_right.x, @@ -506,17 +525,17 @@ void ekg::layout::docknize_widget( ); corner_top_right.x += current_global_theme.layout_offset; - p_widgets->p_descriptor_rect->y = corner_top_right.y; + rect.y = corner_top_right.y; } - highest_top = ekg::clamp_min(highest_top, p_widgets->p_descriptor_rect->h); + highest_top = ekg::clamp_min(highest_top, rect.h); break; } if (should_estimate_extent) { count = it; //ekg::layout::extentnize_widget( - // p_widget_parent, + // parent_property, // ekg::dock::fill, // ekg::dock::next | (is_top ? ekg::dock::bottom : ekg::dock::top), // ekg::axis::horizontal, @@ -525,22 +544,96 @@ void ekg::layout::docknize_widget( //); } - max_previous_height = p_widgets->p_descriptor_rect->h > max_previous_height ? p_widgets->p_descriptor_rect->h : max_previous_height; - if (should_reload_widget) { - p_widgets->on_reload(); - should_reload_widget = false; - } + max_previous_height = rect.h > max_previous_height ? rect.h : max_previous_height; + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, + + descriptor.rect = rect; + + if (should_reload_widget) { + ekg::ui::reload(property, descriptor); + should_reload_widget = false; + } + ); h_extent_backup = ekg::layout::extent_t::h_widget; - if (p_properties->is_docknizable && !p_properties->children.empty()) { - ekg::layout::docknize_widget(p_widgets); + if (property.widget.is_children_docknizable && !property.children.empty()) { + ekg::layout::docknize_widget(property); } ekg::layout::extent_t::h_widget = h_extent_backup; it++; } - p_widget_parent->properties.must_refresh_size = false; + parent_property.widget.should_refresh_size = false; // TODO: may is necessary to re-docknize the parent widget if previous scroll is disabled but now enabled } + +float ekg::layout::get_widget_height_by_children( + ekg::property_t &parent_property +) { + if ( + parent_property == ekg::property_t::not_found + || + !parent_property.widget.is_children_docknizable + ) { + return 0.0f; + } + + ekg::theme_t ¤t_global_theme {ekg::p_core->handler_theme.get_current_theme()}; + ekg::flags_t flags {}; + + float total_height {}; + float height {}; + + for (ekg::at_t &at : parent_property.children) { + ekg::property_t &property {ekg::query(at)}; + if ( + property == ekg::property_t::not_found + ) { + continue; + } + + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, + flags = descriptor.dock; + ); + + height = property.widget.rect.h; + + if ( + property.widget.is_children_docknizable + && + !property.children.empty() + ) { + height = ekg::layout::get_widget_height_by_children(property); + } + + total_height += ( + height + * + ( + ekg::fequalsf(total_height, 0.0f) + || + ekg::has(flags, ekg::dock::next) + ) + + + current_global_theme.layout_offset + ); + } + + total_height += ( + current_global_theme.layout_offset // top + + + current_global_theme.layout_offset // top + + + current_global_theme.layout_offset // bottom + + + current_global_theme.layout_offset // bottom + ); + + return total_height; +} diff --git a/src/layout/extentnize.cpp b/src/layout/extentnize.cpp index 0237ab47..062f461a 100644 --- a/src/layout/extentnize.cpp +++ b/src/layout/extentnize.cpp @@ -1,13 +1,38 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include "ekg/layout/extentnize.hpp" #include "ekg/core/runtime.hpp" +#include "ekg/io/descriptor.hpp" +#include "ekg/core/pools.hpp" ekg::layout::extent_t ekg::layout::extent_t::v_widget {}; ekg::layout::extent_t ekg::layout::extent_t::h_widget {}; -ekg::layout::extent_t ekg::layout::extent_t::v_rect_descriptor {}; -ekg::layout::extent_t ekg::layout::extent_t::h_rect_descriptor {}; +ekg::layout::extent_t ekg::layout::extent_t::v_mask {}; +ekg::layout::extent_t ekg::layout::extent_t::h_mask {}; -void ekg::layout::extentnize_rect_descriptor( - std::vector &rect_descriptor_list, +void ekg::layout::extentnize_mask( + std::vector &components, ekg::vec3_t offset, ekg::flags_t flag_ok, ekg::flags_t flag_stop, @@ -21,19 +46,19 @@ void ekg::layout::extentnize_rect_descriptor( int32_t it {in_out_count}; if ( - it > ekg::layout::extent_t::h_rect_descriptor.begin_index + it > ekg::layout::extent_t::h_mask.begin_index && - it < ekg::layout::extent_t::h_rect_descriptor.end_index + it < ekg::layout::extent_t::h_mask.end_index ) { - in_out_count = ekg::layout::extent_t::h_rect_descriptor.count; - extent = ekg::layout::extent_t::h_rect_descriptor.extent; + in_out_count = ekg::layout::extent_t::h_mask.count; + extent = ekg::layout::extent_t::h_mask.extent; return; } - ekg::layout::extent_t::h_rect_descriptor.begin_index = static_cast(it); + ekg::layout::extent_t::h_mask.begin_index = static_cast(it); - int32_t size {static_cast(rect_descriptor_list.size())}; - int32_t latest_index {static_cast(size - (!rect_descriptor_list.empty()))}; + int32_t size {static_cast(components.size())}; + int32_t latest_index {static_cast(size - (!components.empty()))}; int32_t should_skip_next {}; int32_t flag_ok_count {}; @@ -43,21 +68,21 @@ void ekg::layout::extentnize_rect_descriptor( extent += offset.x; for (it = it; it < size; it++) { - ekg::rect_descriptor_t &rect_descriptor {rect_descriptor_list.at(it)}; - if (rect_descriptor.p_rect == nullptr) { + ekg::layout::mask::component_t &component {components.at(it)}; + if (component.p_rect == nullptr) { continue; } is_last_index = it == latest_index; if ( - (ekg::has(rect_descriptor.flags, flag_stop) && it != in_out_count) + (ekg::has(component.dock, flag_stop) && it != in_out_count) || is_last_index ) { extent -= offset.x; flag_ok_count += ( - (is_ok_flag = (!ekg::has(rect_descriptor.flags, flag_stop) && (ekg::has(rect_descriptor.flags, flag_ok)) && is_last_index)) + (is_ok_flag = (!ekg::has(component.dock, flag_stop) && (ekg::has(component.dock, flag_ok)) && is_last_index)) ); /** @@ -67,31 +92,31 @@ void ekg::layout::extentnize_rect_descriptor( * :blush: **/ extent += ( - (rect_descriptor.p_rect->w + offset.x) + (component.p_rect->w + offset.x) * - (is_last_index && (!ekg::has(rect_descriptor.flags, flag_ok) && should_skip_next == 0)) + (is_last_index && (!ekg::has(component.dock, flag_ok) && should_skip_next == 0)) ); - ekg::layout::extent_t::h_rect_descriptor.end_index = it + is_last_index; - ekg::layout::extent_t::h_rect_descriptor.extent = extent; - ekg::layout::extent_t::h_rect_descriptor.count = flag_ok_count + (flag_ok_count == 0); + ekg::layout::extent_t::h_mask.end_index = it + is_last_index; + ekg::layout::extent_t::h_mask.extent = extent; + ekg::layout::extent_t::h_mask.count = flag_ok_count + (flag_ok_count == 0); break; } - should_skip_next += ekg::has(rect_descriptor.flags, ekg::dock::concat); + should_skip_next += ekg::has(component.dock, ekg::dock::concat); if (should_skip_next > 0) { should_skip_next = (should_skip_next + 1) * (should_skip_next < 2); - flag_ok_count += ekg::has(rect_descriptor.flags, flag_ok); + flag_ok_count += ekg::has(component.dock, flag_ok); continue; } - if (ekg::has(rect_descriptor.flags, flag_ok)) { + if (ekg::has(component.dock, flag_ok)) { flag_ok_count++; continue; } - extent += rect_descriptor.p_rect->w + offset.x; + extent += component.p_rect->w + offset.x; } in_out_count = flag_ok_count + (flag_ok_count == 0); @@ -105,7 +130,7 @@ void ekg::layout::extentnize_rect_descriptor( } void ekg::layout::extentnize_widget( - ekg::ui::abstract *p_widget, + ekg::property_t &parent_property, ekg::flags_t flag_ok, ekg::flags_t flag_stop, ekg::flags_t flag_axis, @@ -113,9 +138,6 @@ void ekg::layout::extentnize_widget( int32_t &in_out_count ) { extent = 0.0f; - if (p_widget == nullptr) { - return; - } int32_t begin_index {in_out_count}; switch (flag_axis & ekg::axis::horizontal) { @@ -134,35 +156,41 @@ void ekg::layout::extentnize_widget( } ekg::layout::extent_t::h_widget.begin_index = static_cast(it); - ekg::ui::abstract *p_widgets {}; - ekg::theme_t ¤t_global_theme {ekg::p_core->service_theme.get_current_theme()}; + ekg::theme_t ¤t_global_theme {ekg::p_core->handler_theme.get_current_theme()}; - int32_t size {static_cast(p_widget->properties.children.size())}; - int32_t latest_index {size - (!p_widget->properties.children.empty())}; + int32_t size {static_cast(parent_property.children.size())}; + int32_t latest_index {size - (!parent_property.children.empty())}; int32_t flag_ok_count {}; + ekg::flags_t dock {}; bool is_scrollbar {}; bool is_last_index {}; bool is_last_index_but {}; bool is_ok {}; bool is_stop {}; - for (it = it; it < size; it++) { - ekg::properties_t *&p_properties {p_widget->properties.children.at(it)}; - if (p_properties == nullptr) { - continue; - } + ekg::rect_t rect {}; + for (; it < size; it++) { + ekg::property_t &property { + ekg::query(parent_property.children.at(it)) + }; - p_widgets = static_cast(p_properties->p_widget); - if (p_widgets->p_descriptor_rect == nullptr) { + if (property == ekg::property_t::not_found) { continue; } - is_scrollbar = p_properties->type == ekg::type::scrollbar; + is_scrollbar = property.descriptor_at.flags == ekg::type::scrollbar; is_last_index = it == latest_index; - is_ok = ekg::has(p_properties->dock, flag_ok); - is_stop = ekg::has(p_properties->dock, flag_stop); + ekg_abstract_todo( + property.descriptor_at.flags, + property.descriptor_at, + dock = descriptor.dock; + rect = descriptor.rect; + ); + + is_ok = ekg::has(dock, flag_ok); + is_stop = ekg::has(dock, flag_stop); if ( (is_stop && it != in_out_count) @@ -202,7 +230,7 @@ void ekg::layout::extentnize_widget( * :blush: **/ extent += ( - p_widgets->p_descriptor_rect->w + rect.w * is_last_index_but ); @@ -244,7 +272,7 @@ void ekg::layout::extentnize_widget( continue; } - extent += p_widgets->p_descriptor_rect->w + current_global_theme.layout_offset; + extent += rect.w + current_global_theme.layout_offset; } in_out_count = flag_ok_count + (flag_ok_count == 0); diff --git a/src/layout/scale.cpp b/src/layout/scale.cpp deleted file mode 100644 index 4c4ee702..00000000 --- a/src/layout/scale.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "ekg/core/runtime.hpp" -#include "ekg/core/context.hpp" -#include "ekg/layout/scale.hpp" - -void ekg::layout::scale_calculate() { - ekg::vec2_t display_size {ekg::dpi.scale.w, ekg::dpi.scale.h}; - ekg::vec2_t viewport {ekg::viewport.w, ekg::viewport.h}; - - if (ekg::dpi.auto_scale) { - ekg::p_core->p_os_platform->update_display_size(); - - display_size.x = ekg::p_core->p_os_platform->display_size.w; - display_size.y = ekg::p_core->p_os_platform->display_size.h; - - ekg::dpi.scale.w = 1920.0f; - ekg::dpi.scale.h = 1080.0f; - - viewport = display_size; - } - - /** - * The scale is step-based, each step change the scale, e.g: - * scale percent interval = 25 - * scale percent = 100 (scale GUI resolution == window size) - * - * scale percent in: - * 0 == 0 - * 25 == 1 - * 50 == 2 - * 75 == 3 - * 100 == 4 - * - * Then it is divided by 4 (4 is the maximum value) - * e.g: 2/4 = 0.5f --> 3/4 = 0.75f - **/ - - float base_scale { - ekg::dpi.scale.w * ekg::dpi.scale.h - }; - - float display_scale { - display_size.x * display_size.y - }; - - float display_factor { - display_scale / base_scale - }; - - float display_scale_percent { - display_factor * 100.0f - }; - - float factor { - ( - (viewport.x * viewport.y) - / - base_scale - ) * 100.0f - }; - - factor = ( - roundf(factor / ekg::dpi.scale_interval) - / - (display_scale_percent / ekg::dpi.scale_interval) - ); - - ekg::dpi.factor_scale = ekg::clamp(factor, 0.5f, 2.0f); -} diff --git a/src/layout/scalenize.cpp b/src/layout/scalenize.cpp new file mode 100644 index 00000000..e2aa7695 --- /dev/null +++ b/src/layout/scalenize.cpp @@ -0,0 +1,92 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/core/runtime.hpp" +#include "ekg/core/context.hpp" +#include "ekg/layout/scalenize.hpp" +#include "ekg/math/floating_point.hpp" + +void ekg::layout::scalenize() { + ekg::vec2_t display_size {ekg::dpi.scale.w, ekg::dpi.scale.h}; + ekg::vec2_t viewport {ekg::dpi.viewport.w, ekg::dpi.viewport.h}; + + if (ekg::dpi.auto_scale) { + ekg::p_core->p_platform_base->update_display_size(); + + display_size.x = ekg::p_core->p_platform_base->display_size.w; + display_size.y = ekg::p_core->p_platform_base->display_size.h; + + ekg::dpi.scale.w = 1920.0f; + ekg::dpi.scale.h = 1080.0f; + + viewport = display_size; + } + + /** + * The scale is step-based, each step change the scale, e.g: + * scale percent interval = 25 + * scale percent = 100 (scale GUI resolution == window size) + * + * scale percent in: + * 0 == 0 + * 25 == 1 + * 50 == 2 + * 75 == 3 + * 100 == 4 + * + * Then it is divided by 4 (4 is the maximum value) + * e.g: 2/4 = 0.5f --> 3/4 = 0.75f + **/ + + float base_scale { + ekg::dpi.scale.w * ekg::dpi.scale.h + }; + + float display_scale { + display_size.x * display_size.y + }; + + float display_factor { + display_scale / base_scale + }; + + float display_scale_percent { + display_factor * 100.0f + }; + + float factor { + ( + (viewport.x * viewport.y) + / + base_scale + ) * 100.0f + }; + + factor = ( + roundf(factor / ekg::dpi.scale_interval) + / + (display_scale_percent / ekg::dpi.scale_interval) + ); + + ekg::dpi.factor_scale = ekg::clamp(factor, 0.5f, 2.0f); +} diff --git a/src/math/geometry.cpp b/src/math/geometry.cpp index 61d664ec..3ca06a25 100644 --- a/src/math/geometry.cpp +++ b/src/math/geometry.cpp @@ -1,3 +1,26 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include "ekg/math/geometry.hpp" void ekg::ortho( diff --git a/src/os/ekg_glfw.cpp b/src/os/ekg_glfw.cpp deleted file mode 100644 index 919fbb66..00000000 --- a/src/os/ekg_glfw.cpp +++ /dev/null @@ -1,427 +0,0 @@ -#include "ekg/os/ekg_glfw.hpp" -#include "ekg/core/context.hpp" -#include "ekg/core/runtime.hpp" - -ekg::glfw::glfw( - GLFWwindow *p_glfw_win, - ekg::flags_t modes -) { - this->p_glfw_win = p_glfw_win; - this->modes = modes; - this->update_display_size(); - - int32_t w {}, h {}; - - /** - * Sounds an unnecessary resize, but it is invoke a complete swapchain redo, - * to fix the orthographic matrix neededs calc. - */ - - glfwGetWindowSize(this->p_glfw_win, &w, &h); - glfwSetWindowSize(this->p_glfw_win, w--, h--); - glfwSetWindowSize(this->p_glfw_win, w++, h++); -} - -void ekg::glfw::init() { - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::arrow)] = glfwCreateStandardCursor(GLFW_CURSOR_NORMAL); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::ibeam)] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::wait)] = glfwCreateStandardCursor(GLFW_CURSOR_NORMAL); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::crosshair)] = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::wait_arrow)] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_nwse)] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_nesw)] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_we)] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_ns)] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_all)] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::no)] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::hand)] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); -} - -void ekg::glfw::quit() { - -} - -void ekg::glfw::update_display_size() { - const GLFWvidmode *p_glfw_vidmode {glfwGetVideoMode(glfwGetPrimaryMonitor())}; - - this->display_size.x = p_glfw_vidmode->width; - this->display_size.y = p_glfw_vidmode->height; -} - -void ekg::glfw::update() { - ekg::timing_t::ticks = static_cast(glfwGetTime() * 1000.0f); - - glfwSetCursor( - this->p_glfw_win, - this->loaded_system_cursor_list[static_cast(system_cursor)] - ); -} - -void ekg::glfw::get_key_name(ekg::io::input_key_t &key, std::string &name) { - switch (key.key) { - case GLFW_KEY_LEFT_CONTROL: - name = "lctrl"; - break; - case GLFW_KEY_RIGHT_CONTROL: - name = "rctrl"; - break; - case GLFW_KEY_LEFT_SHIFT: - name = "lshift"; - break; - case GLFW_KEY_RIGHT_SHIFT: - name = "rshift"; - break; - case GLFW_KEY_LEFT_ALT: - name = "alt"; - break; - case GLFW_KEY_RIGHT_ALT: - name = "altgr"; - break; - case GLFW_KEY_TAB: - name = "tab"; - break; - case GLFW_KEY_ESCAPE: - name = "ESCAPE"; - break; - case GLFW_KEY_ENTER: - name = "return"; - break; - case GLFW_KEY_BACKSPACE: - name = "BACKSPACE"; - break; - case GLFW_KEY_INSERT: - name = "INSERT"; - break; - case GLFW_KEY_DELETE: - name = "DELETE"; - break; - case GLFW_KEY_RIGHT: - name = "RIGHT"; - break; - case GLFW_KEY_LEFT: - name = "LEFT"; - break; - case GLFW_KEY_DOWN: - name = "DOWN"; - break; - case GLFW_KEY_UP: - name = "UP"; - break; - case GLFW_KEY_PAGE_UP: - name = "PAGE_UP"; - break; - case GLFW_KEY_PAGE_DOWN: - name = "PAGE_DOWN"; - break; - case GLFW_KEY_HOME: - name = "HOME"; - break; - case GLFW_KEY_END: - name = "END"; - break; - case GLFW_KEY_CAPS_LOCK: - name = "CAPS_LOCK"; - break; - case GLFW_KEY_SCROLL_LOCK: - name = "SCROLL_LOCK"; - break; - case GLFW_KEY_NUM_LOCK: - name = "NUM_LOCK"; - break; - case GLFW_KEY_PRINT_SCREEN: - name = "PRINT_SCREEN"; - break; - case GLFW_KEY_PAUSE: - name = "PAUSE"; - break; - case GLFW_KEY_F1: - name = "F1"; - break; - case GLFW_KEY_F2: - name = "F2"; - break; - case GLFW_KEY_F3: - name = "F3"; - break; - case GLFW_KEY_F4: - name = "F4"; - break; - case GLFW_KEY_F5: - name = "F5"; - break; - case GLFW_KEY_F6: - name = "F6"; - break; - case GLFW_KEY_F7: - name = "F7"; - break; - case GLFW_KEY_F8: - name = "F8"; - break; - case GLFW_KEY_F9: - name = "F9"; - break; - case GLFW_KEY_F10: - name = "F10"; - break; - case GLFW_KEY_F11: - name = "F11"; - break; - case GLFW_KEY_F12: - name = "F12"; - break; - case GLFW_KEY_F13: - name = "F13"; - break; - case GLFW_KEY_F14: - name = "F14"; - break; - case GLFW_KEY_F15: - name = "F15"; - break; - case GLFW_KEY_F16: - name = "F16"; - break; - case GLFW_KEY_F17: - name = "F17"; - break; - case GLFW_KEY_F18: - name = "F18"; - break; - case GLFW_KEY_F19: - name = "F19"; - break; - case GLFW_KEY_F20: - name = "F20"; - break; - case GLFW_KEY_F21: - name = "F21"; - break; - case GLFW_KEY_F22: - name = "F22"; - break; - case GLFW_KEY_F23: - name = "F23"; - break; - case GLFW_KEY_F24: - name = "F24"; - break; - case GLFW_KEY_F25: - name = "F25"; - break; - case GLFW_KEY_KP_0: - name = "KP_0"; - break; - case GLFW_KEY_KP_1: - name = "KP_1"; - break; - case GLFW_KEY_KP_2: - name = "KP_2"; - break; - case GLFW_KEY_KP_3: - name = "KP_3"; - break; - case GLFW_KEY_KP_4: - name = "KP_4"; - break; - case GLFW_KEY_KP_5: - name = "KP_5"; - break; - case GLFW_KEY_KP_6: - name = "KP_6"; - break; - case GLFW_KEY_KP_7: - name = "KP_7"; - break; - case GLFW_KEY_KP_8: - name = "KP_8"; - break; - case GLFW_KEY_KP_9: - name = "KP_9"; - break; - case GLFW_KEY_KP_DECIMAL: - name = "KP_DECIMAL"; - break; - case GLFW_KEY_KP_DIVIDE: - name = "KP_DIVIDE"; - break; - case GLFW_KEY_KP_MULTIPLY: - name = "KP_MULTIPLY"; - break; - case GLFW_KEY_KP_SUBTRACT: - name = "KP_SUBTRACT"; - break; - case GLFW_KEY_KP_ADD: - name = "KP_ADD"; - break; - case GLFW_KEY_KP_ENTER: - name = "return"; - break; - case GLFW_KEY_KP_EQUAL: - name = "KP_EQUAL"; - break; - case GLFW_KEY_LEFT_SUPER: - name = "LEFT_SUPER"; - break; - case GLFW_KEY_RIGHT_SUPER: - name = "RIGHT_SUPER"; - break; - case GLFW_KEY_MENU: - name = "MENU"; - break; - default: - const char *p_key_name {glfwGetKeyName(key.key, key.scancode)}; - if (p_key_name) { - name = glfwGetKeyName(key.key, key.scancode); - } - break; - } -} - -void ekg::glfw::get_special_key(ekg::io::input_key_t &key, ekg::special_key_type &special_key) { - switch (key.key) { - case GLFW_KEY_LEFT_CONTROL: - special_key = ekg::special_key_type::left_ctrl; - break; - case GLFW_KEY_RIGHT_CONTROL: - special_key = ekg::special_key_type::right_ctrl; - break; - case GLFW_KEY_LEFT_SHIFT: - special_key = ekg::special_key_type::left_shift; - break; - case GLFW_KEY_RIGHT_SHIFT: - special_key = ekg::special_key_type::right_shift; - break; - case GLFW_KEY_LEFT_ALT: - special_key = ekg::special_key_type::left_alt; - break; - case GLFW_KEY_RIGHT_ALT: - special_key = ekg::special_key_type::right_alt; - break; - case GLFW_KEY_TAB: - special_key = ekg::special_key_type::tab; - break; - default: - special_key = ekg::special_key_type::unknown; - break; - } -} - -const char *ekg::glfw::get_clipboard_text() { - return glfwGetClipboardString(this->p_glfw_win); -} - -void ekg::glfw::set_clipboard_text(const char *p_text) { - glfwSetClipboardString(this->p_glfw_win, p_text); - -} - -bool ekg::glfw::has_clipboard_text() { - return glfwGetClipboardString(this->p_glfw_win) != NULL; -} - -void ekg::glfw_window_size_callback(int32_t w, int32_t h) { - if ( - ekg::has( - ekg::p_core->p_os_platform->modes, - ekg::internal_behavior::no_auto_set_viewport_when_resize - ) - ) { - return; - } - - ekg::viewport.w = static_cast(w); - ekg::viewport.h = static_cast(h); - - ekg::p_core->p_gpu_api->update_viewport(ekg::viewport.w, ekg::viewport.h); - ekg::io::dispatch(ekg::io::operation::scale_update); -} - -void ekg::glfw_scroll_callback(double dx, double dy) { - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_wheel; - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_x = static_cast(dx); - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_y = static_cast(dy); - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_precise_x = dx; - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_precise_y = dy; - - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::arrow; - ekg::p_core->poll_events(); -} - -void ekg::glfw_char_callback(uint32_t codepoint) { - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::text_input; - - // idk it seems pretty much a workaround, predictable crash if codepoint - // is larger than 127 (overflow) - const char c [1] {static_cast(codepoint)}; - ekg::p_core->p_os_platform->serialized_input_event.text_input = (c); - - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::arrow; - ekg::p_core->poll_events(); -} - -void ekg::glfw_key_callback(int32_t key, int32_t scancode, int32_t action, int32_t mods) { - switch (action) { - case GLFW_PRESS: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::key_down; - ekg::p_core->p_os_platform->serialized_input_event.key.key = key; - ekg::p_core->p_os_platform->serialized_input_event.key.scancode = scancode; - break; - - case GLFW_REPEAT: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::key_down; - ekg::p_core->p_os_platform->serialized_input_event.key.key = key; - ekg::p_core->p_os_platform->serialized_input_event.key.scancode = scancode; - break; - - case GLFW_RELEASE: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::key_up; - ekg::p_core->p_os_platform->serialized_input_event.key.key = key; - ekg::p_core->p_os_platform->serialized_input_event.key.scancode = scancode; - break; - } - - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::arrow; - ekg::p_core->poll_events(); -} - -void ekg::glfw_mouse_button_callback(int32_t button, int32_t action, int32_t mods) { - - /** - * The mouse button number on GLFW is different from SDL2, - * but EKG is mainly SDL-based; then the `mouse_button` - * will convert to SDL code sequences: - * - * (converted) (glfw original button code) - * 1 0 - * 2 2 - * 3 1 - * 4 3 - * 5 4 - * ... - **/ - - switch (action) { - case GLFW_PRESS: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_button_down; - ekg::p_core->p_os_platform->serialized_input_event.mouse_button = (1 + (button == 1) + button - (1 * (button == 2))); - break; - - case GLFW_RELEASE: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_button_up; - ekg::p_core->p_os_platform->serialized_input_event.mouse_button = (1 + (button == 1) + button - (1 * (button == 2))); - break; - } - - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::arrow; - ekg::p_core->poll_events(); -} - -void ekg::glfw_cursor_pos_callback(double x, double y) { - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_motion; - ekg::p_core->p_os_platform->serialized_input_event.mouse_motion_x = static_cast(x); - ekg::p_core->p_os_platform->serialized_input_event.mouse_motion_y = static_cast(y); - - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::arrow; - ekg::p_core->poll_events(); -} diff --git a/src/os/ekg_sdl.cpp b/src/os/ekg_sdl.cpp deleted file mode 100644 index 4f0180ba..00000000 --- a/src/os/ekg_sdl.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -#include "ekg/core/runtime.hpp" -#include "ekg/core/context.hpp" -#include "ekg/os/ekg_sdl.hpp" - -ekg::sdl::sdl( - SDL_Window *p_sdl_win, - ekg::flags_t modes -) { - this->p_sdl_win = p_sdl_win; - this->modes = modes; - this->update_display_size(); - - int32_t w {}, h {}; - - /** - * Seems like an unnecessary resize, but it is invoke a complete swapchain redo, - * to fix the orthographic matrix neededs calc. - */ - - SDL_GetWindowSize(this->p_sdl_win, &w, &h); - SDL_SetWindowSize(this->p_sdl_win, w--, h--); - SDL_SetWindowSize(this->p_sdl_win, w++, h++); -} - -void ekg::sdl::set_clipboard_text(const char *p_text) { - SDL_SetClipboardText(p_text); -} - -bool ekg::sdl::has_clipboard_text() { - return SDL_HasClipboardText(); -} - -const char *ekg::sdl::get_clipboard_text() { - return SDL_GetClipboardText(); -} - -void ekg::sdl::init() { - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::arrow)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::ibeam)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::wait)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::crosshair)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::wait_arrow)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_nwse)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_nesw)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_we)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_ns)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::size_all)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::no)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); - this->loaded_system_cursor_list[static_cast(ekg::system_cursor_type::hand)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); - - this->system_cursor = ekg::system_cursor_type::arrow; - this->update(); - - ekg::vec2_t window_size {}; - SDL_GetWindowSize(this->p_sdl_win, &window_size.x, &window_size.y); - - ekg::viewport.w = static_cast(window_size.x); - ekg::viewport.h = static_cast(window_size.y); - - SDL_version sdl_version {}; - SDL_GetVersion(&sdl_version); - - ekg::log() << "SDL version: " - << static_cast(sdl_version.major) - << '.' - << static_cast(sdl_version.minor) - << '.' - << static_cast(sdl_version.patch); -} - -void ekg::sdl::quit() { - -} - -void ekg::sdl::update() { - ekg::timing_t::ticks = SDL_GetTicks64(); - - SDL_SetCursor( - this->loaded_system_cursor_list[static_cast(this->system_cursor)] - ); -} - -void ekg::sdl::update_display_size() { - SDL_DisplayMode sdl_display_mode {}; - SDL_GetDisplayMode(0, 0, &sdl_display_mode); - - this->display_size.w = static_cast(sdl_display_mode.w); - this->display_size.h = static_cast(sdl_display_mode.h); -} - -void ekg::sdl::get_key_name(ekg::io::input_key_t &key, std::string &name) { - switch (key.key) { - case SDLK_LCTRL: - name = "lctrl"; - break; - case SDLK_RCTRL: - name = "rctrl"; - break; - case SDLK_LSHIFT: - name = "lshift"; - break; - case SDLK_RSHIFT: - name = "rshift"; - break; - case SDLK_LALT: - name = "alt"; - break; - case SDLK_RALT: - name = "altgr"; - break; - case SDLK_TAB: - name = "tab"; - break; - default: - name = SDL_GetKeyName(key.key); - break; - } -} - -void ekg::sdl::get_special_key(ekg::io::input_key_t &key, ekg::special_key_type &special_key) { - switch (key.key) { - case SDLK_LCTRL: - special_key = ekg::special_key_type::left_ctrl; - break; - case SDLK_RCTRL: - special_key = ekg::special_key_type::right_ctrl; - break; - case SDLK_LSHIFT: - special_key = ekg::special_key_type::left_shift; - break; - case SDLK_RSHIFT: - special_key = ekg::special_key_type::right_shift; - break; - case SDLK_LALT: - special_key = ekg::special_key_type::left_alt; - break; - case SDLK_RALT: - special_key = ekg::special_key_type::right_alt; - break; - case SDLK_TAB: - special_key = ekg::special_key_type::tab; - break; - default: - special_key = ekg::special_key_type::unknown; - break; - } -} - -void ekg::sdl_poll_event(SDL_Event &sdl_event) { - bool must_poll_events {}; - - switch (sdl_event.type) { - default: - break; - case SDL_WINDOWEVENT: - if ( - ekg::has( - ekg::p_core->p_os_platform->modes, - ekg::internal_behavior::no_auto_set_viewport_when_resize - ) - ) { - break; - } - - switch (sdl_event.window.event) { - case SDL_WINDOWEVENT_SIZE_CHANGED: - ekg::viewport.w = sdl_event.window.data1; - ekg::viewport.h = sdl_event.window.data2; - - ekg::p_core->p_gpu_api->update_viewport(ekg::viewport.w, ekg::viewport.h); - ekg::io::dispatch(ekg::io::operation::scale_update); - - break; - } - break; - case SDL_KEYDOWN: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::key_down; - ekg::p_core->p_os_platform->serialized_input_event.key.key = static_cast(sdl_event.key.keysym.sym); - must_poll_events = true; - break; - case SDL_KEYUP: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::key_up; - ekg::p_core->p_os_platform->serialized_input_event.key.key = static_cast(sdl_event.key.keysym.sym); - must_poll_events = true; - break; - case SDL_TEXTINPUT: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::text_input; - ekg::p_core->p_os_platform->serialized_input_event.text_input = sdl_event.text.text; - must_poll_events = true; - break; - case SDL_MOUSEBUTTONUP: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_button_up; - ekg::p_core->p_os_platform->serialized_input_event.mouse_button = sdl_event.button.button; - must_poll_events = true; - break; - case SDL_MOUSEBUTTONDOWN: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_button_down; - ekg::p_core->p_os_platform->serialized_input_event.mouse_button = sdl_event.button.button; - must_poll_events = true; - break; - case SDL_MOUSEWHEEL: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_wheel; - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_x = sdl_event.wheel.x; - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_y = sdl_event.wheel.y; - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_precise_x = sdl_event.wheel.preciseX; - ekg::p_core->p_os_platform->serialized_input_event.mouse_wheel_precise_y = sdl_event.wheel.preciseY; - must_poll_events = true; - break; - case SDL_MOUSEMOTION: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::mouse_motion; - ekg::p_core->p_os_platform->serialized_input_event.mouse_motion_x = sdl_event.motion.x; - ekg::p_core->p_os_platform->serialized_input_event.mouse_motion_y = sdl_event.motion.y; - must_poll_events = true; - break; - case SDL_FINGERUP: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::finger_up; - ekg::p_core->p_os_platform->serialized_input_event.finger_x = sdl_event.tfinger.x; - ekg::p_core->p_os_platform->serialized_input_event.finger_y = sdl_event.tfinger.y; - must_poll_events = true; - break; - case SDL_FINGERDOWN: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::finger_down; - ekg::p_core->p_os_platform->serialized_input_event.finger_x = sdl_event.tfinger.x; - ekg::p_core->p_os_platform->serialized_input_event.finger_y = sdl_event.tfinger.y; - must_poll_events = true; - break; - case SDL_FINGERMOTION: - ekg::p_core->p_os_platform->serialized_input_event.type = ekg::io::input_event_type::finger_motion; - ekg::p_core->p_os_platform->serialized_input_event.finger_x = sdl_event.tfinger.x; - ekg::p_core->p_os_platform->serialized_input_event.finger_y = sdl_event.tfinger.y; - ekg::p_core->p_os_platform->serialized_input_event.finger_dx = sdl_event.tfinger.dx; - ekg::p_core->p_os_platform->serialized_input_event.finger_dy = sdl_event.tfinger.dy; - must_poll_events = true; - break; - } - - if (must_poll_events) { - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::arrow; - ekg::p_core->poll_events(); - must_poll_events = false; - } -} diff --git a/src/platform/sdl/sdl2.cpp b/src/platform/sdl/sdl2.cpp new file mode 100644 index 00000000..be4ad831 --- /dev/null +++ b/src/platform/sdl/sdl2.cpp @@ -0,0 +1,265 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/platform/sdl/sdl2.hpp" +#include "ekg/core/runtime.hpp" +#include "ekg/core/context.hpp" +#include "ekg/io/log.hpp" + +ekg::sdl2::sdl2( + SDL_Window *p_sdl_win, + ekg::flags_t modes +) { + this->p_sdl_win = p_sdl_win; + this->modes = modes; + this->update_display_size(); + + int32_t w {}, h {}; + + /** + * Seems like an unnecessary resize, but it is invoke a complete swapchain redo, + * to fix the orthographic matrix neededs calc. + */ + + SDL_GetWindowSize(this->p_sdl_win, &w, &h); + SDL_SetWindowSize(this->p_sdl_win, w--, h--); + SDL_SetWindowSize(this->p_sdl_win, w++, h++); +} + +void ekg::sdl2::set_clipboard_text(const char *p_text) { + SDL_SetClipboardText(p_text); +} + +bool ekg::sdl2::has_clipboard_text() { + return SDL_HasClipboardText(); +} + +const char *ekg::sdl2::get_clipboard_text() { + return SDL_GetClipboardText(); +} + +void ekg::sdl2::init() { + this->loaded_system_cursors[static_cast(ekg::system_cursor::arrow)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + this->loaded_system_cursors[static_cast(ekg::system_cursor::ibeam)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); + this->loaded_system_cursors[static_cast(ekg::system_cursor::wait)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); + this->loaded_system_cursors[static_cast(ekg::system_cursor::crosshair)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR); + this->loaded_system_cursors[static_cast(ekg::system_cursor::wait_arrow)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW); + this->loaded_system_cursors[static_cast(ekg::system_cursor::size_nwse)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); + this->loaded_system_cursors[static_cast(ekg::system_cursor::size_nesw)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); + this->loaded_system_cursors[static_cast(ekg::system_cursor::size_we)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); + this->loaded_system_cursors[static_cast(ekg::system_cursor::size_ns)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); + this->loaded_system_cursors[static_cast(ekg::system_cursor::size_all)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); + this->loaded_system_cursors[static_cast(ekg::system_cursor::no)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); + this->loaded_system_cursors[static_cast(ekg::system_cursor::hand)] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + + this->system_cursor = ekg::system_cursor::arrow; + this->update(); + + ekg::vec2_t window_size {}; + SDL_GetWindowSize(this->p_sdl_win, &window_size.x, &window_size.y); + + ekg::dpi.viewport.w = static_cast(window_size.x); + ekg::dpi.viewport.h = static_cast(window_size.y); + + SDL_version sdl_version {}; + SDL_GetVersion(&sdl_version); + + ekg::log() << "SDL version: " + << static_cast(sdl_version.major) + << '.' + << static_cast(sdl_version.minor) + << '.' + << static_cast(sdl_version.patch); +} + +void ekg::sdl2::quit() { + +} + +void ekg::sdl2::update() { + ekg::timing_t::ticks = SDL_GetTicks64(); + + SDL_SetCursor( + this->loaded_system_cursors[static_cast(this->system_cursor)] + ); +} + +void ekg::sdl2::update_display_size() { + SDL_DisplayMode sdl_display_mode {}; + SDL_GetDisplayMode(0, 0, &sdl_display_mode); + + this->display_size.w = static_cast(sdl_display_mode.w); + this->display_size.h = static_cast(sdl_display_mode.h); +} + +void ekg::sdl2::get_key_name(ekg::input_key_t &key, std::string &name) { + switch (key.key) { + case SDLK_LCTRL: + name = "lctrl"; + break; + case SDLK_RCTRL: + name = "rctrl"; + break; + case SDLK_LSHIFT: + name = "lshift"; + break; + case SDLK_RSHIFT: + name = "rshift"; + break; + case SDLK_LALT: + name = "alt"; + break; + case SDLK_RALT: + name = "altgr"; + break; + case SDLK_TAB: + name = "tab"; + break; + default: + name = SDL_GetKeyName(key.key); + break; + } +} + +void ekg::sdl2::get_special_key(ekg::input_key_t &key, ekg::special_key &special_key) { + switch (key.key) { + case SDLK_LCTRL: + special_key = ekg::special_key::left_ctrl; + break; + case SDLK_RCTRL: + special_key = ekg::special_key::right_ctrl; + break; + case SDLK_LSHIFT: + special_key = ekg::special_key::left_shift; + break; + case SDLK_RSHIFT: + special_key = ekg::special_key::right_shift; + break; + case SDLK_LALT: + special_key = ekg::special_key::left_alt; + break; + case SDLK_RALT: + special_key = ekg::special_key::right_alt; + break; + case SDLK_TAB: + special_key = ekg::special_key::tab; + break; + default: + special_key = ekg::special_key::unknown; + break; + } +} + +void ekg::sdl2_poll_event(SDL_Event &sdl_event) { + bool must_poll_events {}; + + switch (sdl_event.type) { + default: + break; + case SDL_WINDOWEVENT: + if ( + ekg::has( + ekg::p_core->p_platform_base->modes, + ekg::behavior::no_auto_set_viewport_when_resize + ) + ) { + break; + } + + switch (sdl_event.window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + ekg::dpi.viewport.w = sdl_event.window.data1; + ekg::dpi.viewport.h = sdl_event.window.data2; + + ekg::p_core->p_gpu_api->update_viewport(ekg::dpi.viewport.w, ekg::dpi.viewport.h); + ekg::io::dispatch(ekg::io::operation::scalenize); + + break; + } + break; + case SDL_KEYDOWN: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::key_down; + ekg::p_core->p_platform_base->event.key.key = static_cast(sdl_event.key.keysym.sym); + must_poll_events = true; + break; + case SDL_KEYUP: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::key_up; + ekg::p_core->p_platform_base->event.key.key = static_cast(sdl_event.key.keysym.sym); + must_poll_events = true; + break; + case SDL_TEXTINPUT: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::text_input; + ekg::p_core->p_platform_base->event.text_input = sdl_event.text.text; + must_poll_events = true; + break; + case SDL_MOUSEBUTTONUP: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::mouse_button_up; + ekg::p_core->p_platform_base->event.mouse_button = sdl_event.button.button; + must_poll_events = true; + break; + case SDL_MOUSEBUTTONDOWN: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::mouse_button_down; + ekg::p_core->p_platform_base->event.mouse_button = sdl_event.button.button; + must_poll_events = true; + break; + case SDL_MOUSEWHEEL: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::mouse_wheel; + ekg::p_core->p_platform_base->event.mouse_wheel_x = sdl_event.wheel.x; + ekg::p_core->p_platform_base->event.mouse_wheel_y = sdl_event.wheel.y; + ekg::p_core->p_platform_base->event.mouse_wheel_precise_x = sdl_event.wheel.preciseX; + ekg::p_core->p_platform_base->event.mouse_wheel_precise_y = sdl_event.wheel.preciseY; + must_poll_events = true; + break; + case SDL_MOUSEMOTION: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::mouse_motion; + ekg::p_core->p_platform_base->event.mouse_motion_x = sdl_event.motion.x; + ekg::p_core->p_platform_base->event.mouse_motion_y = sdl_event.motion.y; + must_poll_events = true; + break; + case SDL_FINGERUP: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::finger_up; + ekg::p_core->p_platform_base->event.finger_x = sdl_event.tfinger.x; + ekg::p_core->p_platform_base->event.finger_y = sdl_event.tfinger.y; + must_poll_events = true; + break; + case SDL_FINGERDOWN: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::finger_down; + ekg::p_core->p_platform_base->event.finger_x = sdl_event.tfinger.x; + ekg::p_core->p_platform_base->event.finger_y = sdl_event.tfinger.y; + must_poll_events = true; + break; + case SDL_FINGERMOTION: + ekg::p_core->p_platform_base->event.type = ekg::io::event_type::finger_motion; + ekg::p_core->p_platform_base->event.finger_x = sdl_event.tfinger.x; + ekg::p_core->p_platform_base->event.finger_y = sdl_event.tfinger.y; + ekg::p_core->p_platform_base->event.finger_dx = sdl_event.tfinger.dx; + ekg::p_core->p_platform_base->event.finger_dy = sdl_event.tfinger.dy; + must_poll_events = true; + break; + } + + if (must_poll_events) { + ekg::p_core->p_platform_base->system_cursor = ekg::system_cursor::arrow; + ekg::core::poll_event(); + } +} diff --git a/src/service/theme.cpp b/src/service/theme.cpp deleted file mode 100644 index 03c83c62..00000000 --- a/src/service/theme.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"); to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/service/theme.hpp" -#include "ekg/io/log.hpp" - -std::map &ekg::service::theme::get_theme_map() { - return this->theme_map; -} - -void ekg::service::theme::add(ekg::theme_t theme) { - this->theme_map[theme.name] = theme; -} - -ekg::flags_t ekg::service::theme::set_current_theme(std::string_view name) { - if (this->current_theme.name == name) { - return ekg::result::success; - } - - if (!this->theme_map.count(name)) { - ekg::log() << "Could not to find theme named '" << name << "'!"; - return ekg::result::could_not_find; - } - - this->current_theme = this->theme_map[name]; - return ekg::result::success; -} - -ekg::theme_t &ekg::service::theme::get_current_theme() { - return this->current_theme; -} - -void ekg::service::theme::init() { - ekg::log() << "Initialising theme-service theme-scheme based!"; - - ekg::theme_t dark_theme { - .name = "dark", - .author = "Rina Wilk", - .description = "Pasted dark-theme... mwm", - }; - - dark_theme.frame.background = ekg::color(43, 43, 43, 255); - dark_theme.frame.outline = ekg::color(190, 190, 190, 0); - dark_theme.frame.outline = ekg::color(30, 40, 60, 100); - dark_theme.frame.actions_margin_pixel_thickness = 18; - dark_theme.button.background = ekg::color(85, 85, 85, 50); - dark_theme.button.text = ekg::color(202, 202, 202, 255); - dark_theme.button.outline = ekg::color(202, 207, 222, 0); - dark_theme.button.active = ekg::color(44, 166, 255, 100); - dark_theme.button.highlight = ekg::color(44, 166, 255, 50); - dark_theme.checkbox.background = ekg::color(85, 85, 85, 0); - dark_theme.checkbox.text = ekg::color(202, 202, 202, 255); - dark_theme.checkbox.outline = ekg::color(202, 207, 222, 0); - dark_theme.checkbox.active = ekg::color(44, 166, 255, 200); - dark_theme.checkbox.highlight = ekg::color(44, 166, 255, 50); - dark_theme.checkbox.box_outline = ekg::color(202, 207, 222, 50); - dark_theme.checkbox.box_active = dark_theme.checkbox.active; - dark_theme.checkbox.box_highlight = dark_theme.checkbox.highlight; - dark_theme.checkbox.box_background = dark_theme.checkbox.background; - dark_theme.slider.background = ekg::color(85, 85, 85, 50); - dark_theme.slider.bar_background = ekg::color(85, 85, 85, 50); - dark_theme.slider.text = ekg::color(202, 202, 202, 255); - dark_theme.slider.outline = ekg::color(202, 207, 222, 0); - dark_theme.slider.active = ekg::color(44, 166, 255, 200); - dark_theme.slider.highlight = ekg::color(44, 166, 255, 50); - dark_theme.slider.background = ekg::color(44, 166, 255, 200); - dark_theme.slider.bar_outline = ekg::color(202, 207, 222, 0); - dark_theme.slider.bar_thickness = 100; - dark_theme.slider.target_thickness = 0; - dark_theme.label.text = ekg::color(202, 202, 202, 255); - dark_theme.popup.background = ekg::color(43, 43, 43, 255); - dark_theme.popup.text = ekg::color(202, 202, 202, 255); - dark_theme.popup.outline = ekg::color(43, 43, 43, 0); - dark_theme.popup.highlight = ekg::color(44, 166, 255, 50); - dark_theme.popup.separator = ekg::color(141, 141, 141, 50); - dark_theme.popup.drop_animation_delay = 120; - dark_theme.textbox.background = ekg::color(242, 242, 242, 255); - dark_theme.textbox.text = ekg::color(141, 141, 141, 255); - dark_theme.textbox.outline = ekg::color(141, 141, 141, 50); - dark_theme.textbox.cursor = ekg::color(202, 202, 202, 255); - dark_theme.textbox.select = ekg::color(44, 166, 255, 50); - dark_theme.scrollbar.background = ekg::color(85, 85, 85, 255); - dark_theme.scrollbar.outline = ekg::color(202, 207, 222, 150); - dark_theme.scrollbar.background = ekg::color(44, 166, 255, 200); - dark_theme.scrollbar.highlight = ekg::color(44, 166, 255, 50); - dark_theme.scrollbar.pixel_thickness = 4; - dark_theme.scrollbar.min_bar_size = 30.0f; - dark_theme.listbox.header_background = ekg::color(85, 85, 85, 255); - dark_theme.listbox.item_highlight_outline = ekg::color(141, 141, 141, 0); - dark_theme.listbox.header_highlight = ekg::color(44, 166, 255, 50); - dark_theme.listbox.header_outline = ekg::color(141, 141, 141, 100); - dark_theme.listbox.header_string = ekg::color(202, 202, 202, 255); - dark_theme.listbox.item_outline = ekg::color(141, 141, 141, 0); - dark_theme.listbox.item_background = ekg::color(85, 85, 85, 0); - dark_theme.listbox.item_string = ekg::color(202, 202, 202, 255); - dark_theme.listbox.item_highlight = ekg::color(44, 166, 255, 50); - dark_theme.listbox.item_focused_outline = ekg::color(141, 141, 141, 0); - dark_theme.listbox.item_focused = ekg::color(44, 166, 255, 100); - dark_theme.listbox.line_separator = ekg::color(141, 141, 141, 100); - dark_theme.listbox.outline = ekg::color(141, 141, 141, 100); - dark_theme.listbox.background = ekg::color(85, 85, 85, 50); - dark_theme.listbox.drag_outline = ekg::color(141, 141, 141, 100); - dark_theme.listbox.drag_background = ekg::color(85, 85, 85, 50); - - this->add(dark_theme); - - ekg::theme_t light_theme { - .name = "light", - .author = "Rina Wilk", - .description = "Pasted light-theme... moow", - }; - - light_theme.frame.background = ekg::color(242, 242, 242, 255); - light_theme.frame.outline = ekg::color(190, 190, 190, 0); - light_theme.frame.outline = ekg::color(202, 207, 222, 150); - light_theme.frame.actions_margin_pixel_thickness = 18; - light_theme.button.text = ekg::color(141, 141, 141, 255); - light_theme.button.background = ekg::color(204, 204, 204, 50); - light_theme.button.active = ekg::color(44, 166, 255, 100); - light_theme.button.outline = ekg::color(202, 207, 222, 0); - light_theme.button.highlight = ekg::color(44, 166, 255, 50); - light_theme.checkbox.text = ekg::color(141, 141, 141, 255); - light_theme.checkbox.background = ekg::color(204, 204, 204, 0); - light_theme.checkbox.active = ekg::color(44, 166, 255, 200); - light_theme.checkbox.outline = ekg::color(202, 207, 222, 0); - light_theme.checkbox.highlight = ekg::color(44, 166, 255, 50); - light_theme.checkbox.box_outline = light_theme.checkbox.outline; - light_theme.checkbox.box_active = ekg::color(202, 207, 222, 50); - light_theme.checkbox.box_highlight = light_theme.checkbox.highlight; - light_theme.checkbox.box_background = light_theme.checkbox.background; - light_theme.slider.text = ekg::color(141, 141, 141, 255); - light_theme.slider.background = ekg::color(204, 204, 204, 50); - light_theme.slider.bar_background = ekg::color(204, 204, 204, 50); - light_theme.slider.active = ekg::color(44, 166, 255, 200); - light_theme.slider.outline = ekg::color(202, 207, 222, 0); - light_theme.slider.highlight = ekg::color(44, 166, 255, 50); - light_theme.slider.background = ekg::color(44, 166, 255, 200); - light_theme.slider.bar_outline = ekg::color(44, 166, 255, 200); - light_theme.slider.bar_thickness = 16; - light_theme.slider.target_thickness = 0; - light_theme.label.text = ekg::color(141, 141, 141, 255); - light_theme.popup.text = ekg::color(141, 141, 141, 255); - light_theme.popup.background = ekg::color(242, 242, 242, 255); - light_theme.popup.outline = ekg::color(30, 40, 60, 0); - light_theme.popup.highlight = ekg::color(206, 225, 239, 255); - light_theme.popup.separator = ekg::color(202, 207, 222, 150); - light_theme.popup.drop_animation_delay = 120, - light_theme.textbox.text = ekg::color(141, 141, 141, 255); - light_theme.textbox.background = ekg::color(242, 242, 242, 255); - light_theme.textbox.outline = ekg::color(202, 207, 222, 150); - light_theme.textbox.select = ekg::color(44, 166, 255, 50); - light_theme.textbox.cursor = ekg::color(141, 141, 141, 255); - light_theme.scrollbar.background = ekg::color(202, 202, 202, 255); - light_theme.scrollbar.outline = ekg::color(202, 207, 222, 150); - light_theme.scrollbar.background = ekg::color(44, 166, 255, 200); - light_theme.scrollbar.highlight = ekg::color(44, 166, 255, 50); - light_theme.scrollbar.pixel_thickness = 4; - light_theme.scrollbar.min_bar_size = 30.0f; - light_theme.listbox.header_background = ekg::color(204, 204, 204, 255); - light_theme.listbox.header_highlight = ekg::color(44, 166, 255, 50); - light_theme.listbox.header_outline = ekg::color(202, 207, 222, 50); - light_theme.listbox.header_string = ekg::color(141, 141, 141, 255); - light_theme.listbox.item_background = ekg::color(204, 204, 204, 0); - light_theme.listbox.item_string = ekg::color(141, 141, 141, 255); - light_theme.listbox.item_outline = ekg::color(202, 207, 222, 50); - light_theme.listbox.item_highlight_outline = ekg::color(202, 207, 222, 0); - light_theme.listbox.item_focused_outline = ekg::color(202, 207, 222, 0); - light_theme.listbox.item_focused = ekg::color(44, 166, 255, 100); - light_theme.listbox.item_highlight = ekg::color(44, 166, 255, 50); - light_theme.listbox.line_separator = ekg::color(202, 207, 222, 100); - light_theme.listbox.background = ekg::color(204, 204, 204, 50); - light_theme.listbox.drag_background = ekg::color(204, 204, 204, 50); - light_theme.listbox.drag_outline = ekg::color(202, 207, 222, 100); - light_theme.listbox.outline = ekg::color(202, 207, 222, 100); - - this->add(light_theme); - - ekg::theme_t light_pinky_theme { - .name = "light-pinky", - .author = "Rina Wilk", - .description = "Pasted light-theme... moow", - }; - - light_pinky_theme.frame.background = ekg::color(242, 242, 242, 255); - light_pinky_theme.frame.outline = ekg::color(190, 190, 190, 0); - light_pinky_theme.frame.outline = ekg::color(202, 207, 222, 150); - light_pinky_theme.frame.actions_margin_pixel_thickness = 18; - light_pinky_theme.button.text = ekg::color(141, 141, 141, 255); - light_pinky_theme.button.background = ekg::color(204, 204, 204, 50); - light_pinky_theme.button.active = ekg::color(245, 169, 184, 100); - light_pinky_theme.button.outline = ekg::color(202, 207, 222, 0); - light_pinky_theme.button.highlight = ekg::color(245, 169, 184, 50); - light_pinky_theme.checkbox.text = ekg::color(141, 141, 141, 255); - light_pinky_theme.checkbox.background = ekg::color(204, 204, 204, 0); - light_pinky_theme.checkbox.active = ekg::color(245, 169, 184, 200); - light_pinky_theme.checkbox.outline = ekg::color(202, 207, 222, 0); - light_pinky_theme.checkbox.highlight = ekg::color(245, 169, 184, 50); - light_pinky_theme.checkbox.box_outline = light_pinky_theme.checkbox.outline; - light_pinky_theme.checkbox.box_active = ekg::color(202, 207, 222, 50); - light_pinky_theme.checkbox.box_highlight = light_pinky_theme.checkbox.highlight; - light_pinky_theme.checkbox.box_background = light_pinky_theme.checkbox.background; - light_pinky_theme.slider.text = ekg::color(141, 141, 141, 255); - light_pinky_theme.slider.background = ekg::color(204, 204, 204, 50); - light_pinky_theme.slider.bar_background = ekg::color(204, 204, 204, 50); - light_pinky_theme.slider.active = ekg::color(245, 169, 184, 200); - light_pinky_theme.slider.outline = ekg::color(202, 207, 222, 0); - light_pinky_theme.slider.highlight = ekg::color(245, 169, 184, 50); - light_pinky_theme.slider.background = ekg::color(245, 169, 184, 200); - light_pinky_theme.slider.bar_outline = ekg::color(245, 169, 184, 200); - light_pinky_theme.slider.bar_thickness = 16; - light_pinky_theme.slider.target_thickness = 0; - light_pinky_theme.label.text = ekg::color(141, 141, 141, 255); - light_pinky_theme.popup.text = ekg::color(141, 141, 141, 255); - light_pinky_theme.popup.background = ekg::color(242, 242, 242, 255); - light_pinky_theme.popup.outline = ekg::color(30, 40, 60, 0); - light_pinky_theme.popup.highlight = ekg::color(206, 225, 239, 255); - light_pinky_theme.popup.separator = ekg::color(202, 207, 222, 150); - light_pinky_theme.popup.drop_animation_delay = 120, - light_pinky_theme.textbox.text = ekg::color(141, 141, 141, 255); - light_pinky_theme.textbox.background = ekg::color(242, 242, 242, 255); - light_pinky_theme.textbox.outline = ekg::color(202, 207, 222, 150); - light_pinky_theme.textbox.select = ekg::color(245, 169, 184, 50); - light_pinky_theme.textbox.cursor = ekg::color(141, 141, 141, 255); - light_pinky_theme.scrollbar.background = ekg::color(202, 202, 202, 255); - light_pinky_theme.scrollbar.outline = ekg::color(202, 207, 222, 150); - light_pinky_theme.scrollbar.background = ekg::color(245, 169, 184, 200); - light_pinky_theme.scrollbar.highlight = ekg::color(245, 169, 184, 50); - light_pinky_theme.scrollbar.pixel_thickness = 4; - light_pinky_theme.scrollbar.min_bar_size = 30.0f; - light_pinky_theme.listbox.header_background = ekg::color(204, 204, 204, 255); - light_pinky_theme.listbox.header_highlight = ekg::color(245, 169, 184, 50); - light_pinky_theme.listbox.header_outline = ekg::color(202, 207, 222, 50); - light_pinky_theme.listbox.header_string = ekg::color(141, 141, 141, 255); - light_pinky_theme.listbox.item_background = ekg::color(204, 204, 204, 0); - light_pinky_theme.listbox.item_string = ekg::color(141, 141, 141, 255); - light_pinky_theme.listbox.item_outline = ekg::color(202, 207, 222, 50); - light_pinky_theme.listbox.item_highlight_outline = ekg::color(202, 207, 222, 0); - light_pinky_theme.listbox.item_focused_outline = ekg::color(202, 207, 222, 0); - light_pinky_theme.listbox.item_focused = ekg::color(245, 169, 184, 100); - light_pinky_theme.listbox.item_highlight = ekg::color(245, 169, 184, 50); - light_pinky_theme.listbox.line_separator = ekg::color(202, 207, 222, 100); - light_pinky_theme.listbox.background = ekg::color(204, 204, 204, 50); - light_pinky_theme.listbox.drag_background = ekg::color(204, 204, 204, 50); - light_pinky_theme.listbox.drag_outline = ekg::color(202, 207, 222, 100); - light_pinky_theme.listbox.outline = ekg::color(202, 207, 222, 100); - - this->add(light_pinky_theme); - - ekg::theme_t dark_pinky_theme { - .name = "dark-pinky", - .author = "Rina Wilk", - .description = "Pasted dark-theme... mooo mwm", - }; - - dark_pinky_theme.frame.background = ekg::color(43, 43, 43, 255); - dark_pinky_theme.frame.outline = ekg::color(190, 190, 190, 0); - dark_pinky_theme.frame.outline = ekg::color(30, 40, 60, 100); - dark_pinky_theme.frame.actions_margin_pixel_thickness = 18; - dark_pinky_theme.button.background = ekg::color(85, 85, 85, 50); - dark_pinky_theme.button.text = ekg::color(202, 202, 202, 255); - dark_pinky_theme.button.outline = ekg::color(202, 207, 222, 0); - dark_pinky_theme.button.active = ekg::color(245, 169, 184, 100); - dark_pinky_theme.button.highlight = ekg::color(245, 169, 184, 50); - dark_pinky_theme.checkbox.background = ekg::color(85, 85, 85, 0); - dark_pinky_theme.checkbox.text = ekg::color(202, 202, 202, 255); - dark_pinky_theme.checkbox.outline = ekg::color(202, 207, 222, 0); - dark_pinky_theme.checkbox.active = ekg::color(245, 169, 184, 200); - dark_pinky_theme.checkbox.highlight = ekg::color(245, 169, 184, 50); - dark_pinky_theme.checkbox.box_outline = ekg::color(202, 207, 222, 50); - dark_pinky_theme.checkbox.box_active = dark_pinky_theme.checkbox.active; - dark_pinky_theme.checkbox.box_highlight = dark_pinky_theme.checkbox.highlight; - dark_pinky_theme.checkbox.box_background = dark_pinky_theme.checkbox.background; - dark_pinky_theme.slider.background = ekg::color(85, 85, 85, 50); - dark_pinky_theme.slider.bar_background = ekg::color(85, 85, 85, 50); - dark_pinky_theme.slider.text = ekg::color(202, 202, 202, 255); - dark_pinky_theme.slider.outline = ekg::color(202, 207, 222, 0); - dark_pinky_theme.slider.active = ekg::color(245, 169, 184, 200); - dark_pinky_theme.slider.highlight = ekg::color(245, 169, 184, 50); - dark_pinky_theme.slider.background = ekg::color(245, 169, 184, 200); - dark_pinky_theme.slider.bar_outline = ekg::color(202, 207, 222, 0); - dark_pinky_theme.slider.bar_thickness = 100; - dark_pinky_theme.slider.target_thickness = 0; - dark_pinky_theme.label.text = ekg::color(202, 202, 202, 255); - dark_pinky_theme.popup.background = ekg::color(43, 43, 43, 255); - dark_pinky_theme.popup.text = ekg::color(202, 202, 202, 255); - dark_pinky_theme.popup.outline = ekg::color(43, 43, 43, 0); - dark_pinky_theme.popup.highlight = ekg::color(245, 169, 184, 50); - dark_pinky_theme.popup.separator = ekg::color(141, 141, 141, 50); - dark_pinky_theme.popup.drop_animation_delay = 120; - dark_pinky_theme.textbox.background = ekg::color(242, 242, 242, 255); - dark_pinky_theme.textbox.text = ekg::color(141, 141, 141, 255); - dark_pinky_theme.textbox.outline = ekg::color(141, 141, 141, 50); - dark_pinky_theme.textbox.cursor = ekg::color(202, 202, 202, 255); - dark_pinky_theme.textbox.select = ekg::color(245, 169, 184, 50); - dark_pinky_theme.scrollbar.background = ekg::color(85, 85, 85, 255); - dark_pinky_theme.scrollbar.outline = ekg::color(202, 207, 222, 150); - dark_pinky_theme.scrollbar.background = ekg::color(245, 169, 184, 200); - dark_pinky_theme.scrollbar.highlight = ekg::color(245, 169, 184, 50); - dark_pinky_theme.scrollbar.pixel_thickness = 4; - dark_pinky_theme.scrollbar.min_bar_size = 30.0f; - dark_pinky_theme.listbox.header_background = ekg::color(85, 85, 85, 255); - dark_pinky_theme.listbox.item_highlight_outline = ekg::color(141, 141, 141, 0); - dark_pinky_theme.listbox.header_highlight = ekg::color(245, 169, 184, 50); - dark_pinky_theme.listbox.header_outline = ekg::color(141, 141, 141, 100); - dark_pinky_theme.listbox.header_string = ekg::color(202, 202, 202, 255); - dark_pinky_theme.listbox.item_outline = ekg::color(141, 141, 141, 0); - dark_pinky_theme.listbox.item_background = ekg::color(85, 85, 85, 0); - dark_pinky_theme.listbox.item_string = ekg::color(202, 202, 202, 255); - dark_pinky_theme.listbox.item_highlight = ekg::color(245, 169, 184, 50); - dark_pinky_theme.listbox.item_focused_outline = ekg::color(141, 141, 141, 0); - dark_pinky_theme.listbox.item_focused = ekg::color(245, 169, 184, 100); - dark_pinky_theme.listbox.line_separator = ekg::color(141, 141, 141, 100); - dark_pinky_theme.listbox.outline = ekg::color(141, 141, 141, 100); - dark_pinky_theme.listbox.background = ekg::color(85, 85, 85, 50); - dark_pinky_theme.listbox.drag_outline = ekg::color(141, 141, 141, 100); - dark_pinky_theme.listbox.drag_background = ekg::color(85, 85, 85, 50); - - this->add(dark_pinky_theme); - this->set_current_theme("dark-pinky"); -} - -void ekg::service::theme::quit() { - ekg::log() << "Quitting theme-service"; -} diff --git a/src/ui/abstract.cpp b/src/ui/abstract.cpp index ae15abe6..0f6188db 100644 --- a/src/ui/abstract.cpp +++ b/src/ui/abstract.cpp @@ -1,86 +1,83 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include "ekg/ui/abstract.hpp" +#include "ekg/core/pools.hpp" #include "ekg/core/runtime.hpp" -ekg::rect_t &ekg::ui::abstract::get_abs_rect() { +ekg::rect_t &ekg::ui::get_abs_rect( + ekg::property_t &property, + ekg::rect_t &descriptor_rect +) { + ekg::property_t &parent { + ekg::query(property.parent_at) + }; + return ( - this->properties.rect = ( - *this->p_descriptor_rect + *this->p_parent_rect + *this->p_scroll_vec + property.widget.rect = ( + descriptor_rect + parent.widget.rect + parent.scroll.position ) ); } -void ekg::ui::abstract::on_create() { - if (this->p_parent_rect == nullptr) { - this->p_parent_rect = &this->_blank_parent_rect; - } - - if (this->p_scroll_vec == nullptr) { - this->p_scroll_vec = &this->_blank_scroll_vec; - } - - if (this->p_descriptor_rect == nullptr) { - this->p_descriptor_rect = &this->_blank_descriptor_rect; - } -} - -void ekg::ui::abstract::on_destroy() { - -} - -void ekg::ui::abstract::on_reload() { -} - -void ekg::ui::abstract::on_event(ekg::io::stage stage) { - ekg::input_t &input { - ekg::p_core->service_input.input - }; - - switch (stage) { - case ekg::io::stage::pre: { - if (input.was_pressed || input.was_released || input.has_motion || input.was_wheel) { - ekg::rect_t &rect {this->get_abs_rect()}; - ekg::vec2_t interact {static_cast>(input.interact)}; - - this->states.is_hovering = ( - ekg::rect_collide_vec2(rect, interact) - && - ( - this->properties.level == ekg::level::top - || - this->properties.p_parent == nullptr - || - ekg::rect_collide_vec2(this->scissor, interact) - ) - ); - } - - break; - } - - case ekg::io::stage::post: { - this->states.is_hovering = false; - - #if defined(__ANDROID__) - this->states.is_highlight = ( - !( - !this->states.is_hovering - && - input.was_released - ) - && - this->states.is_highlight +void ekg::ui::pre_event( + ekg::property_t &property, + ekg::rect_t &descriptor_rect, + bool is_top_level +) { + ekg::input_info_t &input {ekg::p_core->handler_input.input}; + if ( + input.was_pressed || input.was_released || + input.has_motion || input.was_wheel + ) { + ekg::rect_t &abs {ekg::ui::get_abs_rect(property, descriptor_rect)}; + ekg::vec2_t interact {static_cast>(input.interact)}; + + property.widget.is_hovering = ( + ekg::rect_collide_vec2(abs, interact) + && + ( + is_top_level + || + property.parent_at == ekg::at_t::not_found + || + ekg::rect_collide_vec2(property.widget.rect_scissor, interact) ) - #endif - - break; - } + ); } } -void ekg::ui::abstract::on_update() { -} - -void ekg::ui::abstract::on_draw() { - +void ekg::ui::post_event( + ekg::property_t &property +) { + property.widget.is_hovering = false; + + #if defined(__ANDROID__) + property.widget.is_highlight = ( + !(!property.widget.is_hovering && ekg::p_core->handler_input.input.was_released) + && + property.widget.is_highlight + ); + #endif } diff --git a/src/ui/abstract/ui_abstract.cpp b/src/ui/abstract/ui_abstract.cpp deleted file mode 100644 index 2ca34dff..00000000 --- a/src/ui/abstract/ui_abstract.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/abstract/ui_abstract.hpp" -#include "ekg/core/runtime.hpp" - -ekg::ui::abstract::abstract() { - -} - -ekg::ui::abstract::~abstract() { -} - -ekg::ui::abstract *ekg::ui::abstract::add_child(int32_t to_insert_id) { - bool contains {}; - for (int32_t &ids: this->child_id_list) { - if ((contains = ids == to_insert_id)) { - break; - } - } - - ekg::ui::abstract_widget *p_child_widget {}; - ekg::ui::abstract_widget *p_parent_widget {}; - - /** - * Each widget must be correctly connected between: - * Absolute parent, parent, & child. - * - * The absolute parent is the first widget container, normally the frame widget, - * any widget must be linked with the absolute parent widget address, making fast - * the access during any-internal operations. - * - * Parent is that widget, which will insert a child widget on own children ID list; - * if the parent widget does not contains an absolute parent widget, then it turns an - * absolute parent widget. - * - * The child widget is the inserted one. - * - * --- - * - * The point of having triple ptr(s) make the possibility of handling all entire - * GUI without overhead calls to find widgets. - **/ - - if (contains == false && ( - (p_child_widget = ekg::core->get_fast_widget_by_id(to_insert_id)) != nullptr - && - (p_parent_widget = ekg::core->get_fast_widget_by_id(this->id)) != nullptr - )) { - - p_child_widget->p_data->set_parent_id(this->id); - p_child_widget->p_parent = &this->rect_widget; - p_child_widget->p_parent_scissor = &p_parent_widget->scissor; - - if (p_parent_widget->p_abs_parent_widget != nullptr) { - p_child_widget->p_abs_parent_widget = p_parent_widget->p_abs_parent_widget; - } else { - p_child_widget->p_abs_parent_widget = p_parent_widget; - } - - this->child_id_list.push_back(to_insert_id); - } - - return this; -} - -std::vector &ekg::ui::abstract::get_child_id_list() { - return this->child_id_list; -} - -ekg::ui::abstract *ekg::ui::abstract::remove_child(int32_t id) { - ekg::ui::abstract_widget *p_widget {}; - - uint64_t it {}; - bool contains {}; - - for (it = 0; it < this->child_id_list.size(); it++) { - if ((contains = this->child_id_list.at(it) == id)) { - break; - } - } - - if (contains == false && (p_widget = ekg::core->get_fast_widget_by_id(id)) != nullptr) { - this->child_id_list.erase(this->child_id_list.begin() + it); - p_widget->p_data->set_parent_id(0); - p_widget->p_parent = &p_widget->empty_parent; - } - - return this; -} - -ekg::ui::abstract *ekg::ui::abstract::unsafe_set_id(int32_t id) { - this->id = id; - return this; -} - -int32_t ekg::ui::abstract::get_id() { - return this->id; -} - -ekg::ui::abstract *ekg::ui::abstract::set_parent_id(int32_t id) { - this->parent_id = id; - return this; -} - -int32_t ekg::ui::abstract::get_parent_id() { - return this->parent_id; -} - -ekg::ui::abstract *ekg::ui::abstract::set_alive(bool state) { - this->alive = state; - return this; -} - -bool ekg::ui::abstract::is_alive() { - return this->alive; -} - -ekg::ui::abstract *ekg::ui::abstract::set_visible(bool state) { - this->visible = state; - return this; -} - -bool ekg::ui::abstract::is_visible() { - return this->visible; -} - -void ekg::ui::abstract::destroy() { - this->set_alive(false); - this->unsafe_destroy_childs(); - ekg::refresh(this->id); -} - -void ekg::ui::abstract::unsafe_destroy_childs() { - for (int32_t &ids: this->child_id_list) { - ekg::ui::abstract_widget *p_widget {ekg::core->get_fast_widget_by_id(ids)}; - if (p_widget != nullptr && p_widget->p_data != nullptr) { - p_widget->p_data->destroy(); - } - } -} - -ekg::ui::abstract *ekg::ui::abstract::set_state(const ekg::state &enum_state) { - this->state = enum_state; - return this; -} - -ekg::state ekg::ui::abstract::get_state() { - return this->state; -} - -ekg::ui::abstract *ekg::ui::abstract::set_level(const ekg::level &_level) { - this->level = _level; - return this; -} - -ekg::level ekg::ui::abstract::get_level() { - return this->level; -} - -ekg::ui::abstract *ekg::ui::abstract::unsafe_set_type(ekg::type enum_type) { - this->type = enum_type; - return this; -} - -ekg::ui::abstract *ekg::ui::abstract::unsafe_sync_ui(ekg::flags flags) { - this->sync_flags |= flags; - return this; -} - -ekg::type ekg::ui::abstract::get_type() { - return this->type; -} - -ekg::flags ekg::ui::abstract::get_place_dock() { - return this->dock_flags; -} - -ekg::rect &ekg::ui::abstract::widget() { - return this->rect_widget; -} - -ekg::rect &ekg::ui::abstract::ui() { - return this->sync_ui; -} - -ekg::ui::abstract *ekg::ui::abstract::set_tag(std::string_view string) { - this->tag = string; - return this; -} - -std::string_view ekg::ui::abstract::get_tag() { - return this->tag; -} - -ekg::flags &ekg::ui::abstract::get_sync() { - return this->sync_flags; -} - -ekg::ui::abstract *ekg::ui::abstract::set_task(ekg::task *p_task, ekg::action action) { - this->action_register[static_cast(action)] = p_task; - return this; -} - -ekg::task *ekg::ui::abstract::get_task(ekg::action action) { - return this->action_register[static_cast(action)]; -} - -ekg::ui::abstract *ekg::ui::abstract::set_layer(ekg::gpu::sampler_t *p_sampler, ekg::layer layer) { - this->layer_surfaces[static_cast(layer)] = p_sampler; - return this; -} - -ekg::gpu::sampler_t *ekg::ui::abstract::get_layer(ekg::layer layer) { - return this->layer_surfaces[static_cast(layer)]; -} - -ekg::ui::abstract *ekg::ui::abstract::unsafe_set_scaled_height_layout(int32_t scaled_height_int) { - this->scaled_height = scaled_height_int; - return this; -} - -bool ekg::ui::abstract::has_parent() const { - return this->parent_id != 0; -} - -bool ekg::ui::abstract::has_children() { - return !this->child_id_list.empty(); -} - -ekg::ui::abstract::operator int32_t() { - return this->id; -} - -ekg::ui::abstract *ekg::ui::abstract::set_auto_initial_dimension(bool is_auto) { - this->auto_initial_dimension = is_auto; - return this; -} - -bool ekg::ui::abstract::is_auto_initial_dimension() { - return this->auto_initial_dimension; -} diff --git a/src/ui/abstract/ui_abstract_widget.cpp b/src/ui/abstract/ui_abstract_widget.cpp deleted file mode 100644 index f060b7c4..00000000 --- a/src/ui/abstract/ui_abstract_widget.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/abstract/ui_abstract_widget.hpp" -#include "ekg/util/gui.hpp" -#include "ekg/draw/draw.hpp" -#include "ekg/core/memory.hpp" - -ekg::ui::abstract_widget::abstract_widget() { - this->p_parent = &this->empty_parent; - this->p_scroll = &this->empty_scroll; -} - -ekg::ui::abstract_widget::~abstract_widget() { - this->on_destroy(); -} - -void ekg::ui::abstract_widget::on_create() { - this->is_just_created = true; -} - -void ekg::ui::abstract_widget::on_destroy() { - if (EKG_MEMORY_MUST_FREE_TASKS_AUTOMATICALLY) { - for (uint64_t it {}; it < EKG_MEMORY_ACTIONS_SIZE; it++) { - ekg::task *p_task { - this->p_data->get_task((ekg::action)it) - }; - - if (p_task->unsafe_is_allocated) { - delete p_task; - } - } - } -} - -void ekg::ui::abstract_widget::on_reload() { - ekg::dispatch(ekg::env::redraw); -} - -void ekg::ui::abstract_widget::on_pre_event(ekg::os::io_event_serial &io_event_serial) { - if (ekg::input::pressed() || ekg::input::released() || ekg::input::motion() || ekg::input::wheel()) { - ekg::vec4 &interact {ekg::input::interact()}; - ekg::rect &rect {this->get_abs_rect()}; - - this->flag.hovered = ( - ekg::rect_collide_vec(rect, interact) && ( - this->p_data->get_level() == ekg::level::top_level || - this->p_data->get_parent_id() == 0 || - ekg::rect_collide_vec(this->scissor, interact)) - ); - } -} - -void ekg::ui::abstract_widget::on_event(ekg::os::io_event_serial &io_event_serial) { - -} - -void ekg::ui::abstract_widget::on_post_event(ekg::os::io_event_serial &io_event_serial) { - this->flag.hovered = false; - - #if defined(__ANDROID__) - this->flag.highlight = ( - !(!this->flag.hovered && - (ekg::input::released())) && - this->flag.highlight - ) - #endif -} - -void ekg::ui::abstract_widget::on_update() { - -} - -void ekg::ui::abstract_widget::on_draw_refresh() { - -} - -ekg::rect ekg::ui::abstract_widget::get_static_rect() { - return this->dimension + *this->p_parent; -} - -ekg::rect &ekg::ui::abstract_widget::get_abs_rect() { - ekg::rect &rect { - (this->p_data->widget() = (this->dimension + *this->p_parent + *this->p_scroll)) - }; - - /** - * ~~As pointed here: ekg/layout/docknize -> ekg::layout::docknize -> fill property~~ - * - * ~~The pixel imperfect is easy fixed with an int32_t cast.~~ - * ~~I do not care anymore about incorrect quads aligned, - * ~~too stupid.~~ - * - * I do not know what I am talking about. - * - Rina. - **/ - - rect.x = static_cast(rect.x); - rect.y = static_cast(rect.y); - rect.w = static_cast(rect.w); - rect.h = static_cast(rect.h); - - return rect; -} \ No newline at end of file diff --git a/src/ui/button/button.cpp b/src/ui/button/button.cpp new file mode 100644 index 00000000..5cef3e67 --- /dev/null +++ b/src/ui/button/button.cpp @@ -0,0 +1,28 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/ui/button/button.hpp" + +ekg::button_t ekg::button_t::not_found { + .at = ekg::at_t::not_found +}; diff --git a/src/ui/button/button_widget.cpp b/src/ui/button/button_widget.cpp deleted file mode 100644 index f15fa7a8..00000000 --- a/src/ui/button/button_widget.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "ekg/ui/button/button_widget.hpp" -#include "ekg/draw/font_renderer.hpp" -#include "ekg/draw/shape.hpp" -#include "ekg/core/runtime.hpp" - -void ekg::ui::button::on_reload() { - this->get_abs_rect(); - - ekg::draw::font_renderer &text_fr { - ekg::draw::get_font_renderer(this->descriptor.text_font_size) - }; - - this->text_rect.w = text_fr.get_text_width(static_cast(this->descriptor.text)); - this->text_rect.h = text_fr.get_text_height(); - - ekg::axis pick_axis { - ekg::axis::horizontal - }; - - ekg::aligned_t aligned_dimension {}; - ekg::align_rect_dimension( - pick_axis, - this->text_rect, - ekg::viewport.minimum_possible_size, - aligned_dimension - ); - - this->descriptor.rect.scaled_height = ekg::clamp_min(this->descriptor.rect.scaled_height, ekg::one_pixel); - - if (this->properties.must_refresh_size) { - this->descriptor.rect.w = ekg::clamp_min(aligned_dimension.w, this->descriptor.rect.w); - this->properties.must_refresh_size = false; - } - - this->descriptor.rect.h = ekg::clamp_min(aligned_dimension.h * this->descriptor.rect.scaled_height, this->descriptor.rect.h); - - ekg::layout::mask &mask { - ekg::p_core->layout_mask - }; - - mask.preset( - { - aligned_dimension.offset, - aligned_dimension.offset, - aligned_dimension.h - }, - pick_axis, - this->descriptor.rect.w - ); - - mask.insert({&this->text_rect, this->descriptor.text_dock}); - mask.docknize(); -} - -void ekg::ui::button::on_event(ekg::io::stage stage) { - switch (stage) { - case ekg::io::stage::process: { - ekg::input_t &input {ekg::p_core->service_input.input}; - - if ( - input.has_motion - || - input.was_pressed - || - input.was_released - ) { - ekg::io::trigger( - ( - input.has_motion - && - this->states.is_hovering - && - (ekg::timing_t::second > ekg::tweaks.task_latency) - ), - ekg::action::hover, - this->descriptor.actions, - this->properties - ); - - ekg::io::set( - this->states.is_highlighting, - this->states.is_hovering - ); - } - - if ( - input.was_pressed - && - !this->states.is_active - && - this->states.is_hovering - && - ekg::fire("button-active") - ) { - ekg::io::trigger( - true, - ekg::action::press, - this->descriptor.actions, - this->properties - ); - - ekg::io::set( - this->states.is_active, - true - ); - } else if ( - input.was_released - && - this->states.is_active - ) { - ekg::io::trigger( - this->states.is_hovering, - ekg::action::active, - this->descriptor.actions, - this->properties - ); - - this->descriptor.value.set_value( - this->states.is_hovering - ); - - ekg::io::set( - this->states.is_active, - false - ); - } - - break; - } - - default: { - ekg::ui::abstract::on_event(stage); - break; - } - } -} - -void ekg::ui::button::on_draw() { - ekg::rect_t &rect { - this->get_abs_rect() - }; - - EKG_ASSERT_SCISSOR( - this->scissor, - rect, - this->p_parent_scissor_rect - ); - - EKG_ASSERT_VALUE(this->descriptor.value); - EKG_ASSERT_VALUE(this->descriptor.text); - - ekg::draw::rect( - rect, - this->descriptor.theme.background, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::background] - ); - - if (this->states.is_highlighting) { - ekg::draw::rect( - rect, - this->descriptor.theme.highlight, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::highlight] - ); - } - - if (this->states.is_active) { - ekg::draw::rect( - rect, - this->descriptor.theme.active, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::active] - ); - - ekg::draw::rect( - rect, - this->descriptor.theme.active_outline, - ekg::draw_mode::outline - ); - } - - ekg::draw::get_font_renderer(this->descriptor.text_font_size) - .blit( - static_cast(this->descriptor.text), - rect.x + this->text_rect.x, - rect.y + this->text_rect.y, - this->descriptor.theme.text - ); - - ekg::draw::rect( - rect, - this->descriptor.theme.outline, - ekg::draw_mode::outline - ); -} diff --git a/src/ui/button/widget.cpp b/src/ui/button/widget.cpp new file mode 100644 index 00000000..3f5050f3 --- /dev/null +++ b/src/ui/button/widget.cpp @@ -0,0 +1,154 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/ui/button/widget.hpp" +#include "ekg/layout/docknize.hpp" +#include "ekg/io/log.hpp" +#include "ekg/ui/abstract.hpp" +#include "ekg/draw/shape/shape.hpp" +#include "ekg/draw/typography/font.hpp" +#include "ekg/core/context.hpp" + +void ekg::ui::reload( + ekg::property_t &property, + ekg::button_t &button +) { + ekg_assert_low_level( + property == ekg::property_t::not_found, + ekg::log() << "warn: invalid property on reload", + return + ); + + ekg_assert_low_level( + button == ekg::button_t::not_found, + ekg::log() << "warn: invalid button on reload", + return + ); + + ekg::ui::get_abs_rect( + property, + button.rect + ); + + ekg::axis pick_axis { + ekg::axis::horizontal + }; + + ekg::aligned_t aligned_dimension {}; + for (ekg::button_t::check_t &check : button.checks) { + ekg::draw::font &draw_font { + ekg::draw::get_font_renderer(check.font_size) + }; + + check.widget.rect_text.w = draw_font.get_text_width(check.text.get()); + check.widget.rect_text.h = draw_font.get_text_height(); + + aligned_dimension = {}; + ekg::align_rect_dimension( + pick_axis, + check.widget.rect_text, + ekg::dpi.min_sizes, + aligned_dimension + ); + } + + button.rect.h = aligned_dimension.h * button.rect.scaled_height; + + ekg::layout::mask mask {}; + mask.preset( + { + aligned_dimension.offset, + aligned_dimension.offset, + button.rect.h + }, + pick_axis, + button.rect.w + ); + + for (ekg::button_t::check_t &check : button.checks) { + ekg::draw::font &draw_font { + ekg::draw::get_font_renderer(check.font_size) + }; + + if (check.is_check_box) { + check.widget.rect_box.w = check.widget.rect_text.h; + check.widget.rect_box.h = check.widget.rect_text.h; + + mask.insert( + { + .p_rect = &check.widget.rect_box, + .dock = check.dock + } + ); + } + + mask.insert( + { + .p_rect = &check.widget.rect_text, + .dock = check.dock + } + ); + } + + mask.docknize(); + + if (property.widget.should_refresh_size) { + button.rect.w = ekg::max(ekg::dpi.min_sizes, mask.get_rect().w); + property.widget.should_refresh_size = false; + } +} + +void ekg::ui::event( + ekg::property_t &property, + ekg::button_t &button, + const ekg::io::stage &stage +) { + +} + +void ekg::ui::high_frequency( + ekg::property_t &property, + ekg::button_t &button +) { + +} + +void ekg::ui::pass( + ekg::property_t &property, + ekg::button_t &button +) { + +} + +void ekg::ui::buffering( + ekg::property_t &property, + ekg::button_t &button +) { + +} + +void ekg::ui::unmap( + ekg::button_t &button +) { + +} diff --git a/src/ui/checkbox/checkbox_widget.cpp b/src/ui/checkbox/checkbox_widget.cpp deleted file mode 100644 index 62561848..00000000 --- a/src/ui/checkbox/checkbox_widget.cpp +++ /dev/null @@ -1,249 +0,0 @@ -#include "ekg/ui/checkbox/checkbox_widget.hpp" -#include "ekg/draw/font_renderer.hpp" -#include "ekg/draw/shape.hpp" -#include "ekg/core/runtime.hpp" - -void ekg::ui::checkbox::on_reload() { - this->get_abs_rect(); - - ekg::draw::font_renderer &text_fr { - ekg::draw::get_font_renderer(this->descriptor.text_font_size) - }; - - this->text_rect.w = text_fr.get_text_width(static_cast(this->descriptor.text)); - this->text_rect.h = text_fr.get_text_height(); - - /* checkbox box square size is the same size as font renderer text height */ - - this->box_rect.w = this->text_rect.h; - this->box_rect.h = this->text_rect.h; - - ekg::axis pick_axis { - ekg::axis::horizontal - }; - - ekg::aligned_t aligned_dimension {}; - ekg::align_rect_dimension( - pick_axis, - this->text_rect, - ekg::viewport.minimum_possible_size, - aligned_dimension - ); - - this->descriptor.rect.scaled_height = ekg::clamp_min(this->descriptor.rect.scaled_height, ekg::one_pixel); - - if (this->properties.must_refresh_size) { - this->descriptor.rect.w = ekg::clamp_min(aligned_dimension.w, this->descriptor.rect.w); - this->properties.must_refresh_size = false; - } - - this->descriptor.rect.h = ekg::clamp_min(aligned_dimension.h * this->descriptor.rect.scaled_height, this->descriptor.rect.h); - - ekg::layout::mask &mask { - ekg::p_core->layout_mask - }; - - mask.preset( - { - aligned_dimension.offset, - aligned_dimension.offset, - aligned_dimension.h - }, - pick_axis, - this->descriptor.rect.w - ); - - mask.insert({&this->box_rect, this->descriptor.box_dock}); - mask.insert({&this->text_rect, this->descriptor.text_dock}); - mask.docknize(); -} - -void ekg::ui::checkbox::on_event(ekg::io::stage stage) { - switch (stage) { - case ekg::io::stage::process: { - ekg::input_t &input {ekg::p_core->service_input.input}; - - if ( - input.has_motion - || - input.was_pressed - || - input.was_released - ) { - ekg::io::set( - this->states.is_highlighting, - this->states.is_hovering - ); - - ekg::io::set( - this->states.is[ekg::checkbox_t::box], - ( - this->states.is_hovering - && - ekg::rect_collide_vec2( - (this->box_rect + this->get_abs_rect()), - static_cast>(input.interact) - ) - ) - ); - - ekg::io::trigger( - ( - input.has_motion - && - this->states.is_hovering - && - (ekg::timing_t::second > ekg::tweaks.task_latency) - ), - ekg::action::motion, - this->descriptor.actions, - this->properties - ); - } - - if ( - input.was_pressed - && - !this->states.is_active - && - this->states.is_hovering - && - ekg::fire("checkbox-active") - ) { - ekg::io::trigger( - true, - ekg::action::press, - this->descriptor.actions, - this->properties - ); - - ekg::io::set(this->states.is_active, true); - } else if (input.was_released && this->states.is_active) { - ekg::io::trigger( - this->states.is_hovering, - ekg::action::release, - this->descriptor.actions, - this->properties - ); - - ekg::io::trigger( - ( - this->states.is_hovering - && - (this->descriptor.value.set_value(!this->descriptor.value.get_value()) || true) - ), - ekg::action::active, - this->descriptor.actions, - this->properties - ); - - ekg::io::set( - this->states.is_active, - false - ); - } - - break; - } - - default: - ekg::ui::abstract::on_event(stage); - break; - } -} - -void ekg::ui::checkbox::on_draw() { - ekg::rect_t &rect {this->get_abs_rect()}; - - EKG_ASSERT_SCISSOR( - this->scissor, - rect, - this->p_parent_scissor_rect - ); - - EKG_ASSERT_VALUE(this->descriptor.value); - EKG_ASSERT_VALUE(this->descriptor.text); - - bool is_value_true { - this->descriptor.value.get_value() - }; - - ekg::rect_t box {this->box_rect + rect}; - - ekg::draw::rect( - rect, - this->descriptor.theme.background, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::background] - ); - - if (this->states.is_highlighting && !this->states.is[ekg::checkbox_t::box]) { - ekg::draw::rect( - rect, - this->descriptor.theme.highlight, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::highlight] - ); - } - - if (this->states.is[ekg::checkbox_t::box]) { - ekg::draw::rect( - box, - this->descriptor.theme.box_highlight, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::checkbox_t::box][ekg::layer::highlight] - ); - } - - ekg::draw::rect( - box, - this->descriptor.theme.box_background, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::checkbox_t::box][ekg::layer::background] - ); - - if (this->states.is_active && this->states.is[ekg::checkbox_t::box]) { - ekg::draw::rect( - box, - this->descriptor.theme.box_active, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::checkbox_t::box][ekg::layer::active] - ); - } else if (this->states.is_active) { - ekg::draw::rect( - rect, - this->descriptor.theme.active, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::active] - ); - } - - if (is_value_true) { - ekg::draw::rect( - box, - this->descriptor.theme.box_active, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::checkbox_t::box][ekg::layer::active] - ); - } - - ekg::draw::rect( - box, - this->descriptor.theme.box_outline, - ekg::draw_mode::outline - ); - - ekg::draw::get_font_renderer(this->descriptor.text_font_size) - .blit( - static_cast(this->descriptor.text), - rect.x + this->text_rect.x, - rect.y + this->text_rect.y, - this->descriptor.theme.text - ); - - ekg::draw::rect( - rect, - this->descriptor.theme.outline, - ekg::draw_mode::outline - ); -} diff --git a/src/os/ekg_vulkan.cpp b/src/ui/frame/frame.cpp similarity index 89% rename from src/os/ekg_vulkan.cpp rename to src/ui/frame/frame.cpp index b2b6fb6c..0d18b16a 100644 --- a/src/os/ekg_vulkan.cpp +++ b/src/ui/frame/frame.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,5 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/ui/frame/frame.hpp" -#include "ekg/os/ekg_vulkan.hpp" \ No newline at end of file +ekg::frame_t ekg::frame_t::not_found {}; diff --git a/src/ui/frame/frame_widget.cpp b/src/ui/frame/frame_widget.cpp deleted file mode 100644 index 038db14c..00000000 --- a/src/ui/frame/frame_widget.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include "ekg/ui/frame/frame_widget.hpp" -#include "ekg/core/runtime.hpp" -#include "ekg/core/context.hpp" -#include "ekg/draw/shape.hpp" - -void ekg::ui::frame::on_create() { - ekg::ui::abstract::on_create(); -} - -void ekg::ui::frame::on_destroy() { - -} - -void ekg::ui::frame::on_reload() { - -} - -void ekg::ui::frame::on_event(ekg::io::stage stage) { - switch (stage) { - case ekg::io::stage::process: { - ekg::rect_t &rect {this->get_abs_rect()}; - ekg::input_t &input {ekg::p_core->service_input.input}; - ekg::flags_t resize_over_dock {}; - - ekg::io::trigger( - ( - input.has_motion - && - this->states.is_hovering - && - (ekg::timing_t::second > ekg::tweaks.task_latency) - ), - ekg::action::motion, - this->descriptor.actions, - this->properties - ); - - if ( - input.was_pressed - && - ( - (this->descriptor.drag_dock != ekg::dock::none && ekg::fire("frame-drag")) - || - (this->descriptor.resize_dock != ekg::dock::none && ekg::fire("frame-resize")) - ) - ) { - - ekg::vec2_t margin { - static_cast(this->descriptor.theme.actions_margin_pixel_thickness), - static_cast(this->descriptor.theme.actions_margin_pixel_thickness) - }; - - ekg::scale_docker_by_margin( - this->docker_drag, - margin, - rect - ); - - const float margin_factor {4.0f}; - ekg::scale_docker_by_margin( - this->docker_resize, - margin / margin_factor, - rect - ); - - this->target_dock_drag = ekg::vec2_collide_docker_if( - static_cast>(input.interact), - this->docker_drag, - this->descriptor.drag_dock - ); - - this->target_dock_resize = ekg::vec2_collide_docker_if( - static_cast>(input.interact), - this->docker_resize, - this->descriptor.resize_dock - ); - - resize_over_dock = this->target_dock_resize; - - this->rect_delta.x = input.interact.x - rect.x; - this->rect_delta.y = input.interact.y - rect.y; - this->rect_delta.w = rect.w; - this->rect_delta.h = rect.h; - this->rect_cache = rect; - - this->states.is_active = this->target_dock_drag != ekg::dock::none || this->target_dock_resize != ekg::dock::none; - this->states.is_absolute = this->states.is_active; - - ekg::io::trigger( - true, - ekg::action::press, - this->descriptor.actions, - this->properties - ); - } else if (input.has_motion && this->states.is_active) { - ekg::rect_t new_rect {rect}; - ekg::vec2_t interact {static_cast>(input.interact)}; - - resize_over_dock = this->target_dock_resize; - - if (this->target_dock_drag != ekg::dock::none && this->target_dock_resize == ekg::dock::none) { - ekg::io::trigger( - ekg::timing_t::second > ekg::tweaks.task_latency, - ekg::action::drag, - this->descriptor.actions, - this->properties - ); - - new_rect.x = interact.x - this->rect_delta.x; - new_rect.y = interact.y - this->rect_delta.y; - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::size_all; - } - - if (this->target_dock_resize != ekg::dock::none) { - ekg::io::trigger( - ekg::timing_t::second > ekg::tweaks.task_latency, - ekg::action::resize, - this->descriptor.actions, - this->properties - ); - - if (ekg::has(this->target_dock_resize, ekg::dock::left)) { - interact.x = ekg::clamp_min(interact.x, this->rect_delta.x); - new_rect.x = ekg::clamp_max(interact.x - this->rect_delta.x, this->rect_cache.x + this->rect_cache.w - ekg::viewport.minimum_possible_size); - new_rect.w = (this->rect_cache.x + this->rect_cache.w) - new_rect.x; - } - - if (ekg::has(this->target_dock_resize, ekg::dock::right)) { - new_rect.w = ekg::clamp_min(this->rect_delta.w + ((interact.x - new_rect.x) - this->rect_delta.x), ekg::viewport.minimum_possible_size); - } - - if (ekg::has(this->target_dock_resize, ekg::dock::top)) { - interact.y = ekg::clamp_min(interact.y, this->rect_delta.y); - new_rect.y = ekg::clamp_max(interact.y - this->rect_delta.y, this->rect_cache.y + this->rect_cache.h - ekg::viewport.minimum_possible_size); - new_rect.h = (this->rect_cache.y + this->rect_cache.h) - new_rect.y; - } - - if (ekg::has(this->target_dock_resize, ekg::dock::bottom)) { - new_rect.h = ekg::clamp_min(this->rect_delta.h + ((interact.y - new_rect.y) - this->rect_delta.y), ekg::viewport.minimum_possible_size); - } - } - - ekg::clamp_rect_by_square( - new_rect, - ekg::viewport.minimum_possible_size - ); - - if (rect != new_rect) { - if (this->properties.p_parent != nullptr) { - this->descriptor.rect.x = new_rect.x - this->p_parent_rect->x; - this->descriptor.rect.y = new_rect.y - this->p_parent_rect->y; - } else { - this->descriptor.rect.x = (int32_t) new_rect.x; - this->descriptor.rect.y = (int32_t) new_rect.y; - } - - this->descriptor.rect.w = (int32_t) new_rect.w; - this->descriptor.rect.h = (int32_t) new_rect.h; - - if (this->target_dock_resize != ekg::dock::none) { - this->states.is_targeting_absolute_parent = true; - ekg::p_core->dispatch_widget_op( - this, - ekg::io::operation::layout_docknize - ); - } - - ekg::viewport.redraw = true; - } - } else if ( - this->states.is_hovering - && - this->descriptor.resize_dock != ekg::dock::none - && - !this->states.is_active - ) { - ekg::vec2_t margin { - static_cast(this->descriptor.theme.actions_margin_pixel_thickness), - static_cast(this->descriptor.theme.actions_margin_pixel_thickness) - }; - - const float margin_factor {4.0f}; - ekg::scale_docker_by_margin( - this->docker_resize, - margin / margin_factor, - rect - ); - - resize_over_dock = ekg::vec2_collide_docker_if( - static_cast>(input.interact), - this->docker_resize, - this->descriptor.resize_dock - ); - } - - if (resize_over_dock != ekg::dock::none) { - bool is_top {ekg::has(resize_over_dock, ekg::dock::top)}; - bool is_bottom {ekg::has(resize_over_dock, ekg::dock::bottom)}; - bool is_left {ekg::has(resize_over_dock, ekg::dock::left)}; - bool is_right {ekg::has(resize_over_dock, ekg::dock::right)}; - - if ((is_top && is_left) || (is_bottom && is_right)) { - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::size_nwse; - } else if ((is_top && is_right) || (is_bottom && is_left)) { - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::size_nesw; - } else if (is_top || is_bottom) { - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::size_ns; - } else if (is_left || is_right) { - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::size_we; - } - } - - if (input.was_released) { - if (this->states.is_active) { - this->states.is_absolute = false; - - ekg::io::trigger( - this->states.is_hovering, - ekg::action::release, - this->descriptor.actions, -this->properties - ); - } - - this->target_dock_resize = ekg::dock::none; - this->target_dock_drag = ekg::dock::none; - this->states.is_active = false; - } - - break; - } - - default: { - ekg::ui::abstract::on_event(stage); - break; - } - } -} - -void ekg::ui::frame::on_update() { - -} - -void ekg::ui::frame::on_draw() { - ekg::rect_t &rect { - this->get_abs_rect() - }; - - EKG_ASSERT_SCISSOR( - this->scissor, - rect, - this->p_parent_scissor_rect - ); - - ekg::draw::rect( - rect, - this->descriptor.theme.background, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::background] - ); - - ekg::draw::rect( - rect, - this->descriptor.theme.outline, - ekg::draw_mode::filled - ); -} diff --git a/src/ui/frame/widget.cpp b/src/ui/frame/widget.cpp new file mode 100644 index 00000000..0d03a32d --- /dev/null +++ b/src/ui/frame/widget.cpp @@ -0,0 +1,355 @@ +/** + * MIT License + * + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ekg/ui/frame/widget.hpp" +#include "ekg/ui/abstract.hpp" +#include "ekg/handler/input.hpp" +#include "ekg/core/runtime.hpp" +#include "ekg/handler/callback.hpp" +#include "ekg/core/context.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/layout/docknize.hpp" +#include "ekg/draw/shape/shape.hpp" +#include "ekg/io/log.hpp" + +void ekg::ui::reload( + ekg::property_t &property, + ekg::frame_t &frame +) { + if (property.widget.should_refresh_size) { + frame.rect.h = ekg::layout::get_widget_height_by_children( + property + ); + property.widget.should_refresh_size = false; + } +} + +void ekg::ui::event( + ekg::property_t &property, + ekg::frame_t &frame, + const ekg::io::stage &stage +) { + switch (stage) { + case ekg::io::stage::pre: + ekg::ui::pre_event(property, frame.rect, frame.set_top_level); + break; + + case ekg::io::stage::post: + ekg::ui::post_event(property); + break; + + case ekg::io::stage::process: { + ekg::rect_t &rect {ekg::ui::get_abs_rect(property, frame.rect)}; + ekg::input_info_t &input {ekg::p_core->handler_input.input}; + ekg::flags_t resize_over_dock {}; + + /*ekg::io::trigger( + ( + input.has_motion + && + property.widget.is_hovering + && + (ekg::timing_t::second > ekg::tweaks.task_latency) + ), + ekg::action::motion, + frame.actions, + this->properties + );*/ + + if ( + input.was_pressed + && + ( + (frame.drag != ekg::dock::none && ekg::fire("frame-drag")) + || + (frame.resize != ekg::dock::none && ekg::fire("frame-resize")) + ) + ) { + + ekg::vec2_t margin { + static_cast(frame.color_scheme.actions_margin_pixel_thickness), + static_cast(frame.color_scheme.actions_margin_pixel_thickness) + }; + + ekg::scale_docker_by_margin( + frame.widget.docker_drag, + margin, + rect + ); + + const float margin_factor {4.0f}; + ekg::scale_docker_by_margin( + frame.widget.docker_resize, + margin / margin_factor, + rect + ); + + frame.widget.target_dock_drag = ekg::vec2_collide_docker_if( + static_cast>(input.interact), + frame.widget.docker_drag, + frame.drag + ); + + frame.widget.target_dock_resize = ekg::vec2_collide_docker_if( + static_cast>(input.interact), + frame.widget.docker_resize, + frame.resize + ); + + resize_over_dock = frame.widget.target_dock_resize; + + frame.widget.rect_delta.x = input.interact.x - rect.x; + frame.widget.rect_delta.y = input.interact.y - rect.y; + frame.widget.rect_delta.w = rect.w; + frame.widget.rect_delta.h = rect.h; + frame.widget.rect_cache = rect; + + property.widget.is_active = ( + frame.widget.target_dock_drag != ekg::dock::none + || + frame.widget.target_dock_resize != ekg::dock::none + ); + + property.widget.is_absolute = property.widget.is_active; + + /*ekg::io::trigger( + true, + ekg::action::press, + frame.actions, + this->properties + );*/ + } else if (input.has_motion && property.widget.is_active) { + ekg::rect_t new_rect {rect}; + ekg::vec2_t interact {static_cast>(input.interact)}; + + resize_over_dock = frame.widget.target_dock_resize; + + if (frame.widget.target_dock_drag != ekg::dock::none && frame.widget.target_dock_resize == ekg::dock::none) { + /*ekg::io::trigger( + ekg::timing_t::second > ekg::tweaks.task_latency, + ekg::action::drag, + frame.actions, + this->properties + );*/ + + new_rect.x = interact.x - frame.widget.rect_delta.x; + new_rect.y = interact.y - frame.widget.rect_delta.y; + ekg::p_core->p_platform_base->system_cursor = ekg::system_cursor::size_all; + } + + if (frame.widget.target_dock_resize != ekg::dock::none) { + /*ekg::io::trigger( + ekg::timing_t::second > ekg::tweaks.task_latency, + ekg::action::resize, + frame.actions, + this->properties + );*/ + + if (ekg::has(frame.widget.target_dock_resize, ekg::dock::left)) { + interact.x = ekg::clamp_min(interact.x, frame.widget.rect_delta.x); + new_rect.x = ekg::clamp_max(interact.x - frame.widget.rect_delta.x, frame.widget.rect_cache.x + frame.widget.rect_cache.w - ekg::dpi.min_sizes); + new_rect.w = (frame.widget.rect_cache.x + frame.widget.rect_cache.w) - new_rect.x; + } + + if (ekg::has(frame.widget.target_dock_resize, ekg::dock::right)) { + new_rect.w = ekg::clamp_min(frame.widget.rect_delta.w + ((interact.x - new_rect.x) - frame.widget.rect_delta.x), ekg::dpi.min_sizes); + } + + if (ekg::has(frame.widget.target_dock_resize, ekg::dock::top)) { + interact.y = ekg::clamp_min(interact.y, frame.widget.rect_delta.y); + new_rect.y = ekg::clamp_max(interact.y - frame.widget.rect_delta.y, frame.widget.rect_cache.y + frame.widget.rect_cache.h - ekg::dpi.min_sizes); + new_rect.h = (frame.widget.rect_cache.y + frame.widget.rect_cache.h) - new_rect.y; + } + + if (ekg::has(frame.widget.target_dock_resize, ekg::dock::bottom)) { + new_rect.h = ekg::clamp_min(frame.widget.rect_delta.h + ((interact.y - new_rect.y) - frame.widget.rect_delta.y), ekg::dpi.min_sizes); + } + } + + ekg::clamp_rect_by_square( + new_rect, + ekg::dpi.min_sizes + ); + + if (rect != new_rect) { + if (property.parent_at != ekg::at_t::not_found) { + ekg::property_t &parent_property {ekg::query(property.parent_at)}; + frame.rect.x = new_rect.x - parent_property.widget.rect.x; + frame.rect.y = new_rect.y - parent_property.widget.rect.y; + } else { + frame.rect.x = (int32_t) new_rect.x; + frame.rect.y = (int32_t) new_rect.y; + } + + frame.rect.w = (int32_t) new_rect.w; + frame.rect.h = (int32_t) new_rect.h; + + if (frame.widget.target_dock_resize != ekg::dock::none) { + property.widget.is_targeting_absolute_parent = true; + ekg::io::dispatch( + ekg::io::operation::docknize, + frame.at + ); + } + + ekg::gui.ui.redraw = true; + } + } else if ( + property.widget.is_hovering + && + frame.resize != ekg::dock::none + && + !property.widget.is_active + ) { + ekg::vec2_t margin { + static_cast(frame.color_scheme.actions_margin_pixel_thickness), + static_cast(frame.color_scheme.actions_margin_pixel_thickness) + }; + + const float margin_factor {4.0f}; + ekg::scale_docker_by_margin( + frame.widget.docker_resize, + margin / margin_factor, + rect + ); + + resize_over_dock = ekg::vec2_collide_docker_if( + static_cast>(input.interact), + frame.widget.docker_resize, + frame.resize + ); + } + + if (resize_over_dock != ekg::dock::none) { + bool is_top {ekg::has(resize_over_dock, ekg::dock::top)}; + bool is_bottom {ekg::has(resize_over_dock, ekg::dock::bottom)}; + bool is_left {ekg::has(resize_over_dock, ekg::dock::left)}; + bool is_right {ekg::has(resize_over_dock, ekg::dock::right)}; + + if ((is_top && is_left) || (is_bottom && is_right)) { + ekg::p_core->p_platform_base->system_cursor = ekg::system_cursor::size_nwse; + } else if ((is_top && is_right) || (is_bottom && is_left)) { + ekg::p_core->p_platform_base->system_cursor = ekg::system_cursor::size_nesw; + } else if (is_top || is_bottom) { + ekg::p_core->p_platform_base->system_cursor = ekg::system_cursor::size_ns; + } else if (is_left || is_right) { + ekg::p_core->p_platform_base->system_cursor = ekg::system_cursor::size_we; + } + } + + if (input.was_released) { + if (property.widget.is_active) { + property.widget.is_absolute = false; + + /*ekg::io::trigger( + property.widget.is_hovering, + ekg::action::release, + frame.actions, + this->properties + );*/ + } + + frame.widget.target_dock_resize = ekg::dock::none; + frame.widget.target_dock_drag = ekg::dock::none; + property.widget.is_active = false; + } + + break; + } + } +} + +void ekg::ui::high_frequency( + ekg::property_t &property, + ekg::frame_t &frame +) { + +} + +void ekg::ui::pass( + ekg::property_t &property, + ekg::frame_t &frame +) { + property.widget.should_buffering = true; +} + +void ekg::ui::buffering( + ekg::property_t &property, + ekg::frame_t &frame +) { + ekg::rect_t &rect {ekg::ui::get_abs_rect(property, frame.rect)}; + + ekg_assert_scissor( + property.widget.rect_scissor, + rect, + ekg::query(property.parent_at).widget.rect, + property.parent_at != ekg::at_t::not_found + ); + + ekg::draw::rect( + rect, + property.widget.is_focused ? frame.color_scheme.focused_background : frame.color_scheme.background, + ekg::draw::mode::fill, + ekg::sampler_t::not_found + ); + + if (property.widget.is_active) { + ekg::draw::rect( + rect, + frame.color_scheme.highlight, + ekg::draw::mode::fill, + ekg::sampler_t::not_found + ); + } + + if (property.widget.is_highlight) { + ekg::draw::rect( + rect, + frame.color_scheme.highlight, + ekg::draw::mode::fill, + ekg::sampler_t::not_found + ); + } + + ekg::draw::rect( + rect, + property.widget.is_focused ? frame.color_scheme.focused_outline : frame.color_scheme.outline, + ekg::draw::mode::outline, + ekg::sampler_t::not_found + ); + + if (property.widget.is_warning) { + ekg::draw::rect( + rect, + frame.color_scheme.warning_outline, + ekg::draw::mode::outline, + ekg::sampler_t::not_found + ); + } +} + +void ekg::ui::unmap( + ekg::frame_t &frame +) { + +} diff --git a/src/ui/label/label_widget.cpp b/src/ui/label/label_widget.cpp deleted file mode 100644 index fdaf1db3..00000000 --- a/src/ui/label/label_widget.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "ekg/ui/label/label_widget.hpp" -#include "ekg/draw/font_renderer.hpp" -#include "ekg/draw/shape.hpp" -#include "ekg/core/runtime.hpp" - -void ekg::ui::label::on_reload() { - this->get_abs_rect(); - - ekg::draw::font_renderer &text_fr { - ekg::draw::get_font_renderer(this->descriptor.text_font_size) - }; - - this->text_rect.w = text_fr.get_text_width(static_cast(this->descriptor.text)); - this->text_rect.h = text_fr.get_text_height(); - - ekg::axis pick_axis { - ekg::axis::horizontal - }; - - ekg::aligned_t aligned_dimension {}; - ekg::align_rect_dimension( - pick_axis, - this->text_rect, - ekg::viewport.minimum_possible_size, - aligned_dimension - ); - - this->descriptor.rect.scaled_height = ekg::clamp_min(this->descriptor.rect.scaled_height, ekg::one_pixel); - - if (this->properties.must_refresh_size) { - this->descriptor.rect.w = ekg::clamp_min(aligned_dimension.w, this->descriptor.rect.w); - this->properties.must_refresh_size = false; - } - - this->descriptor.rect.h = ekg::clamp_min(aligned_dimension.h * this->descriptor.rect.scaled_height, this->descriptor.rect.h); - - ekg::layout::mask &mask { - ekg::p_core->layout_mask - }; - - mask.preset( - { - aligned_dimension.offset, - aligned_dimension.offset, - aligned_dimension.h - }, - pick_axis, - this->descriptor.rect.w - ); - - mask.insert({&this->text_rect, this->descriptor.text_dock}); - mask.docknize(); -} - -void ekg::ui::label::on_draw() { - EKG_ASSERT_VALUE(this->descriptor.text); - - ekg::rect_t &rect { - this->get_abs_rect() - }; - - EKG_ASSERT_SCISSOR( - this->scissor, - rect, - this->p_parent_scissor_rect - ); - - ekg::draw::rect( - rect, - this->descriptor.theme.background, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::layer::background] - ); - - ekg::draw::get_font_renderer(this->descriptor.text_font_size) - .blit( - static_cast(this->descriptor.text), - rect.x + this->text_rect.x, - rect.y + this->text_rect.y, - this->descriptor.theme.text - ); - - ekg::draw::rect( - rect, - this->descriptor.theme.outline, - ekg::draw_mode::outline - ); -} diff --git a/src/ui/listbox/ui_listbox.cpp b/src/ui/listbox/ui_listbox.cpp deleted file mode 100644 index 5c3a8f98..00000000 --- a/src/ui/listbox/ui_listbox.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/listbox/ui_listbox.hpp" -#include "ekg/util/gui.hpp" - -ekg::ui::listbox *ekg::ui::listbox::set_item_font_size(ekg::font font_size) { - if (this->item_font_size != font_size) { - this->item_font_size = font_size; - ekg::reload(this->id); - } - - return this; -} - -ekg::font ekg::ui::listbox::get_item_font_size() { - return this->item_font_size; -} - -ekg::ui::listbox *ekg::ui::listbox::set_column_header_font_size(ekg::font font_size) { - if (this->column_header_font_size != font_size) { - this->column_header_font_size = font_size; - ekg::reload(this->id); - } - - return this; -} - -ekg::font ekg::ui::listbox::get_column_header_font_size() { - return this->column_header_font_size; -} - - -ekg::ui::listbox *ekg::ui::listbox::set_width(float w) { - if (this->sync_ui.w != w) { - this->sync_ui.w = w; - - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::dimension)); - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::set_width)); - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -float ekg::ui::listbox::get_width() { - return this->rect_widget.w; -} - -float ekg::ui::listbox::get_height() { - return this->rect_widget.h; -} - -ekg::ui::listbox *ekg::ui::listbox::set_place(ekg::flags dock) { - if (this->dock_flags != dock) { - this->dock_flags = dock; - ekg::synclayout(this->parent_id); - } - - return this; -} - -ekg::ui::listbox *ekg::ui::listbox::set_mode(ekg::mode mode) { - this->current_mode = mode; - return this; -} - -ekg::mode ekg::ui::listbox::get_mode() { - return this->current_mode; -} - -ekg::ui::listbox *ekg::ui::listbox::set_scaled_height(int32_t h) { - if (this->scaled_height != h) { - this->scaled_height = h; - ekg::reload(this->id); - } - - return this; -} - -int32_t ekg::ui::listbox::get_scaled_height() { - return this->scaled_height; -} - -ekg::ui::listbox *ekg::ui::listbox::set_column_header_scaled_height(int32_t h) { - if (this->column_header_scaled_height != h) { - this->column_header_scaled_height = h; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -int32_t ekg::ui::listbox::get_column_header_scaled_height() { - return this->column_header_scaled_height; -} - -ekg::ui::listbox *ekg::ui::listbox::set_item_scaled_height(int32_t h) { - if (this->item_scaled_height != h) { - this->item_scaled_height = h; - ekg::reload(this->id); - } - - return this; -} - -int32_t ekg::ui::listbox::get_item_scaled_height() { - return this->item_scaled_height; -} - -ekg::ui::listbox *ekg::ui::listbox::set_column_header_align(ekg::flags flags) { - if (this->column_header_dock_flags != flags) { - this->column_header_dock_flags = flags; - ekg::reload(this->id); - } - - return this; -} - -ekg::flags ekg::ui::listbox::get_column_header_align() { - return this->column_header_dock_flags; -} \ No newline at end of file diff --git a/src/ui/listbox/ui_listbox_widget.cpp b/src/ui/listbox/ui_listbox_widget.cpp deleted file mode 100644 index 15b00d65..00000000 --- a/src/ui/listbox/ui_listbox_widget.cpp +++ /dev/null @@ -1,1189 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/listbox/ui_listbox_widget.hpp" -#include "ekg/core/runtime.hpp" -#include "ekg/draw/draw.hpp" -#include "ekg/ui/display.hpp" -#include "ekg/util/geometry.hpp" - -#include - -void ekg::ui::listbox_widget::on_create() { - this->must_update_items = true; -} - -void ekg::ui::listbox_widget::on_reload() { - ekg::ui::listbox *p_ui {static_cast(this->p_data)}; - ekg::rect &rect {this->get_abs_rect()}; - - ekg::draw::font_renderer &f_renderer_item {ekg::f_renderer(p_ui->get_item_font_size())}; - ekg::draw::font_renderer &f_renderer_column_header {ekg::f_renderer(p_ui->get_column_header_font_size())}; - ekg::layout::mask &mask {ekg::core->mask}; - - float text_height {f_renderer_item.get_text_height()}; - float dimension_offset {text_height / 2}; - float offset {ekg::find_min_offset(text_height, dimension_offset)}; - - if (p_ui->is_auto_initial_dimension()) { - this->dimension.w = ekg_min(this->dimension.w, text_height); - this->min_size.x = ekg_min(this->min_size.x, text_height); - p_ui->set_auto_initial_dimension(false); - } - - this->dimension.h = (text_height + dimension_offset) * static_cast(p_ui->get_scaled_height()); - this->min_size.y = ekg_min(this->min_size.y, text_height); - - ekg::font item_font {p_ui->get_item_font_size()}; - ekg::rect relative_rect {}; - ekg::rect relative_largest_rect {}; - - uint64_t arbitrary_index_pos {}; - uint64_t highest_arbitrary_index_pos {}; - uint64_t rendering_cache_arbitrary_index_pos {}; - - int32_t item_scaled_height {p_ui->get_item_scaled_height()}; - int32_t column_header_scaled_height {p_ui->get_column_header_scaled_height()}; - ekg::flags column_header_dock_flags {p_ui->get_column_header_align()}; - - float text_width {}; - int32_t text_lines {}; - uint64_t items_header_size {p_ui->p_value->size()}; - - text_height = f_renderer_column_header.get_text_height(); - dimension_offset = text_height / 2; - - ekg::mode mode {p_ui->get_mode()}; - bool opened {}; - - float scaled_width {rect.w}; - this->header_relative_x = 0.0f; - - for (uint64_t it {}; it < items_header_size; it++) { - relative_rect.y = 0.0f; - - ekg::item &item {p_ui->p_value->at(it)}; - ekg::placement &placement {item.unsafe_get_placement()}; - - text_lines = 1; - text_width = f_renderer_column_header.get_text_width(item.get_value(), text_lines); - - if (ekg_bitwise_contains(column_header_dock_flags, ekg::dock::fill)) { - /** - * Check for the note in `ekg/layout/docknize.hpp`::docknize widgets function, - * for understand the reason of integer 32bits casting here. - **/ - placement.rect.w = static_cast( - (scaled_width) / static_cast(items_header_size) - ); - } else { - placement.rect.w = ekg_min(placement.rect.w, text_width); - } - - relative_rect.w = placement.rect.w; - placement.rect.h = (text_height + dimension_offset) * static_cast(column_header_scaled_height); - - placement.rect.x = relative_rect.x; - placement.rect.y = relative_rect.y; - - arbitrary_index_pos = 0; - rendering_cache_arbitrary_index_pos = 0; - - if ( - (ekg_bitwise_contains(column_header_dock_flags, ekg::dock::top)) - || - (!ekg_bitwise_contains(column_header_dock_flags, ekg::dock::bottom)) - ) { - this->rect_content_place.y = placement.rect.h + ekg_pixel; - this->column_header_height = placement.rect.h + ekg_pixel; - } - - if (this->must_update_items) { - if (it >= this->item_rendering_cache.size()) { - this->item_rendering_cache.emplace_back(); - } - - ekg::item &rendering_cache {this->item_rendering_cache.at(it)}; - ekg::ui::listbox_template_reload( - rendering_cache, - item, - rect, - placement.rect, - item_font, - relative_rect, - item_scaled_height, - it, - arbitrary_index_pos, - rendering_cache_arbitrary_index_pos, - opened, - mode, - &this->must_update_items - ); - - if (arbitrary_index_pos < rendering_cache.size()) { - rendering_cache.erase( - rendering_cache.begin() + arbitrary_index_pos + 1, - rendering_cache.end() - ); - } else { - rendering_cache.resize(arbitrary_index_pos); - } - - rendering_cache.unsafe_set_visible_count(rendering_cache_arbitrary_index_pos); - } - - placement.rect_text.w = text_width; - placement.rect_text.h = text_height * static_cast(text_lines); - - mask.preset( - { - offset, offset, placement.rect.h - }, - ekg::axis::horizontal, - placement.rect.w - ); - - mask.insert({&placement.rect_text, placement.text_dock_flags}); - mask.docknize(); - - ekg::item &rendering_cache {this->item_rendering_cache.at(it)}; - rendering_cache.unsafe_get_placement() = item.unsafe_get_placement(); - rendering_cache.set_value(item.get_value()); - rendering_cache.set_attr(item.get_attr()); - rendering_cache.set_text_align(item.get_text_align()); - - this->header_relative_x += placement.rect.w + ekg_pixel; - relative_rect.x += relative_rect.w + ekg_pixel; - - if (relative_rect.x > relative_largest_rect.w) { - relative_largest_rect.w = relative_rect.x; - } - - /* get the largest visible size to scroll use as rect! */ - - if (rendering_cache_arbitrary_index_pos > highest_arbitrary_index_pos) { - highest_arbitrary_index_pos = rendering_cache_arbitrary_index_pos; - } - } - - if (this->must_update_items) { - relative_largest_rect.h = ( - (highest_arbitrary_index_pos * relative_rect.h) + this->embedded_scroll.rect_horizontal_scroll_bar.h + ekg_pixel - ); - - this->embedded_scroll.rect_child = relative_largest_rect; - } else { - this->embedded_scroll.rect_child.w = relative_largest_rect.w; - } - - this->embedded_scroll.acceleration.x = (text_height * 3.0f) + (offset * 2.0f); - this->embedded_scroll.acceleration.y = this->embedded_scroll.acceleration.x; - this->embedded_scroll.p_rect_mother = &this->rect_content_abs; - this->embedded_scroll.widget_id = this->p_data->get_id(); - this->embedded_scroll.on_reload(); - this->must_update_items = false; -} - -void ekg::ui::listbox_widget::on_pre_event(ekg::os::io_event_serial &io_event_serial) { - abstract_widget::on_pre_event(io_event_serial); - this->embedded_scroll.on_pre_event(io_event_serial); - - /** - * We do not want the scroll to interfere with some header target action. - **/ - if (!this->flag.extra_state) { - this->flag.absolute = ( - this->embedded_scroll.is_dragging_bar() || - this->embedded_scroll.flag.activity || - this->flag.state - ); - } -} - -void ekg::ui::listbox_widget::on_event(ekg::os::io_event_serial &io_event_serial) { - bool pressed_open {ekg::input::action("listbox-activity-open")}; - bool pressed_select_many {ekg::input::action("listbox-activity-select-many")}; - bool pressed_select {ekg::input::action("listbox-activity-select")}; - bool released {ekg::input::released()}; - bool motion {ekg::input::motion()}; - - bool is_some_header_targeted { - this->flag.extra_state - }; - - this->embedded_scroll.on_event(io_event_serial); - - this->flag.was_hovered = this->flag.hovered; - if (!this->flag.was_hovered && this->is_high_frequency) { - this->flag.was_hovered = false; - } - - if ((this->flag.hovered || (this->flag.absolute && !is_some_header_targeted) || this->embedded_scroll.flag.activity) && !this->is_high_frequency) { - ekg::update_high_frequency(this); - } - - if (is_some_header_targeted && released) { - this->latest_target_dragging = this->target_dragging; - this->target_resizing = -1; - this->target_dragging = -1; - this->targeting_header_to_resize = -1; - this->targeting_header_to_drag = -1; - this->flag.extra_state = false; - this->flag.absolute = false; - } - - bool unnecessary_operations { - (!motion && !released && !pressed_select && !pressed_open && !pressed_select_many) - }; - - /** - * If input interact position is not colliding with the listbox, - * there is no reason to waste calls; but with exception of absolute flag, - * because when a header is targeted, the absolute flag is set to true, - * but if the interact position is out of listbox boudings, it must keep - * process the header targeted if drag or resize is going on. - **/ - bool not_hovering_or_is_not_refresh_time { - !this->flag.hovered && !this->was_hovered && !is_some_header_targeted && !this->was_selected - }; - - bool hovering_scroll_bars_or_updating_items { - (this->embedded_scroll.flag.hovered || this->must_update_items) - }; - - bool stop_processs_if_scrolling { - false - }; - - if ( - unnecessary_operations || - not_hovering_or_is_not_refresh_time || - hovering_scroll_bars_or_updating_items || - stop_processs_if_scrolling - ) { - return; - } - - if (!this->flag.extra_state) { - is_some_header_targeted = false; - } - - ekg::rect &rect {this->get_abs_rect()}; - this->was_hovered = this->flag.hovered; - this->was_selected = false; - - ekg::rect relative_largest_rect {}; - ekg::rect relative_rect {}; - ekg::rect scrollable_rect {this->rect_content_abs + this->embedded_scroll.scroll}; - ekg::vec2 ui_pos {rect.x, rect.y}; - ekg::rect delta_rect {}; - ekg::vec4 &interact {ekg::input::interact()}; - ekg::vec4 point {}; - ekg::rect item_rect {}; - - ekg::ui::listbox *p_ui {static_cast(this->p_data)}; - ekg::mode mode {p_ui->get_mode()}; - - ekg::ui::listbox_widget::op_mode op_mode { - static_cast( - pressed_select || pressed_select_many || pressed_open || is_some_header_targeted || this->latest_target_dragging != -1 || this->must_force_tree_refresh - ) - }; - - bool is_dragging_or_resizing {}; - bool is_multicolumn {mode == ekg::mode::multicolumn}; - bool must {}; - bool hovering {}; - - ekg::flags flags {}; - uint64_t arbitrary_index_pos {}; - uint64_t highest_arbitrary_index_pos {}; - uint64_t visible_begin_index {}; - uint64_t visible_count {}; - uint64_t size {}; - - float bottom_place {this->rect_content_abs.y + this->rect_content_abs.h}; - - is_some_header_targeted = ( - is_some_header_targeted - || - ( - (this->targeting_header_to_resize != -1 || this->targeting_header_to_drag != -1) - && - pressed_select - ) - ); - - this->header_relative_x = 0.0f; - - switch (op_mode) { - case ekg::ui::listbox_widget::op_mode::recursive_tree_update: - size = p_ui->p_value->size(); - ekg::ui::redraw = true; - if (this->latest_target_dragging != -1) { - uint64_t set_header_new_index {static_cast(this->latest_target_dragging)}; - point.x = ekg_clamp(interact.x, rect.x, rect.x + rect.w); - - for (uint64_t it {}; it < size; it++) { - ekg::item &item_header {p_ui->p_value->at(it)}; - ekg::placement &placement_header {item_header.unsafe_get_placement()}; - - item_rect = placement_header.rect + rect; - item_rect.x += this->embedded_scroll.scroll.x; - - point.y = ekg_clamp( - interact.y, - item_rect.y, - item_rect.y + this->rect_original_dragging_targeted_header.h - ); - - if (ekg::rect_collide_vec_precisely(item_rect, point) && this->latest_target_dragging != it) { - set_header_new_index = it; - break; - } - } - - if (set_header_new_index != this->latest_target_dragging) { - std::swap( - p_ui->p_value->at(this->latest_target_dragging), - p_ui->p_value->at(set_header_new_index) - ); - } - - this->latest_target_dragging = -1; - } - - for (uint64_t it {}; it < size; it++) { - relative_rect.y = 0.0f; - arbitrary_index_pos = 0; - - ekg::item &rendering_cache {this->item_rendering_cache.at(it)}; - ekg::item &item_header {p_ui->p_value->at(it)}; - ekg::placement &placement_header {item_header.unsafe_get_placement()}; - - placement_header.rect.x = this->header_relative_x; - - if (this->targeting_header_to_resize == it && this->target_resizing == -1) { - this->target_resizing = this->targeting_header_to_resize; - this->rect_header_delta.x = (this->rect_targeting_header.x + this->rect_targeting_header.w) - interact.x; - } - - if (this->target_resizing == static_cast(it)) { - placement_header.rect.w = ekg_min((interact.x + this->rect_header_delta.x) - this->rect_targeting_header.x, placement_header.rect.h); - ekg::cursor = ekg::system_cursor::size_we; - ekg::ui::redraw = true; - this->flag.absolute = true; - } - - if (this->targeting_header_to_drag == it && this->target_dragging == -1 && this->target_resizing == -1) { - this->target_dragging = this->targeting_header_to_drag; - this->rect_header_delta.x = interact.x - this->rect_targeting_header.x; - this->rect_original_dragging_targeted_header = this->rect_targeting_header; - } - - if (this->target_dragging == it && this->target_resizing == -1) { - placement_header.rect.x = interact.x - this->rect_header_delta.x; - this->rect_current_dragging_targeted_header = placement_header.rect; - ekg::cursor = ekg::system_cursor::size_all; - ekg::ui::redraw = true; - this->flag.absolute = true; - } - - - is_dragging_or_resizing = ( - this->flag.extra_state = this->target_resizing != -1 || this->target_dragging != -1 - ); - - delta_rect.x = rect.x; - delta_rect.w = rect.w; - delta_rect.h = item_rect.y; - delta_rect.h = item_rect.h; - - must = ( - ekg::rect_collide_vec(delta_rect, interact) - || - is_dragging_or_resizing - ); - - /** - * Do not copy all the content by simple one reason: - * Copying the the major header from `p_value` to rendering cache element, - * erase ALL reserved memory from reload, re-reserving is performanceless here. - * - * Passing only necessary stuff (attributes, value, etc). - **/ - - rendering_cache.unsafe_get_placement() = placement_header; - rendering_cache.set_value(item_header.get_value()); - rendering_cache.set_attr(item_header.get_attr()); - rendering_cache.set_text_align(item_header.get_text_align()); - - if (!must && !is_dragging_or_resizing) { - ekg::ui::listbox_template_on_event( - io_event_serial, - arbitrary_index_pos, - rendering_cache, - motion, - released, - pressed_select_many, - pressed_select, - pressed_open, - this->was_selected, - this->was_hovered, - item_header, - ui_pos, - scrollable_rect, - relative_rect, - mode - ); - - rendering_cache.unsafe_set_visible_count(arbitrary_index_pos); - } - - relative_rect.w = placement_header.rect.w; - this->header_relative_x += relative_rect.w + ekg_pixel; - relative_rect.x += relative_rect.w + !is_multicolumn; - - if (relative_rect.x > relative_largest_rect.w) { - relative_largest_rect.w = relative_rect.x; - } - - /* get the largest visible size to scroll use as rect! */ - - if (arbitrary_index_pos > highest_arbitrary_index_pos) { - highest_arbitrary_index_pos = arbitrary_index_pos; - } - } - - if (is_dragging_or_resizing) { - this->embedded_scroll.rect_child.w = relative_largest_rect.w; - } else { - relative_largest_rect.h = ( - (highest_arbitrary_index_pos * relative_rect.h) + this->embedded_scroll.rect_horizontal_scroll_bar.h + ekg_pixel - ); - - this->embedded_scroll.rect_child = relative_largest_rect; - } - - break; - case ui::listbox_widget::op_mode::cached_update: - ekg::ui::redraw = true; - - float between_headers_target_resize {ekg_pixel * 4}; - float normalized_horizontal_scroll {this->embedded_scroll.get_normalized_vertical_scroll()}; - bool is_header_flags_fill {static_cast(ekg_bitwise_contains(p_ui->get_column_header_align(), ekg::dock::fill))}; - - this->targeting_header_to_resize = -1; - this->targeting_header_to_drag = -1; - int32_t some_targeted_header {-1}; - uint64_t rendering_cache_size {this->item_rendering_cache.size()}; - - for (uint64_t it_header {}; it_header < rendering_cache_size; it_header++) { - ekg::item &item_header {this->item_rendering_cache.at(it_header)}; - ekg::placement &placement_header {item_header.unsafe_get_placement()}; - - item_rect = placement_header.rect + rect; - item_rect.x = item_rect.x + this->embedded_scroll.scroll.x; - - flags = item_header.get_attr(); - delta_rect = item_rect; - delta_rect.x = delta_rect.x + delta_rect.w - between_headers_target_resize; - delta_rect.w = between_headers_target_resize + between_headers_target_resize; - - must = ( - !is_header_flags_fill && some_targeted_header == -1 && !this->flag.extra_state && ekg::rect_collide_vec(delta_rect, interact) - ); - - if (must) { - some_targeted_header = static_cast(it_header); - this->rect_targeting_header = item_rect; - this->targeting_header_to_resize = static_cast(it_header); - ekg::cursor = ekg::system_cursor::size_we; - } - - delta_rect = item_rect; - must = ( - !must && some_targeted_header == -1 && ekg::rect_collide_vec(delta_rect, interact) && rendering_cache_size > 1 - ); // `!must &&` because we do not want to interfe in resize targeting. - - if (must) { - some_targeted_header = static_cast(it_header); - this->rect_targeting_header = item_rect; - this->targeting_header_to_drag = static_cast(it_header); - } - - visible_count = item_header.get_visible_count(); - visible_begin_index = ekg::get_index_by_normalized_scroll(normalized_horizontal_scroll, visible_count); - - delta_rect.x = rect.x; - delta_rect.w = rect.w; - delta_rect.h = item_rect.y; - delta_rect.h = item_rect.h; - - must = ( - ekg::rect_collide_vec(delta_rect, interact) - ); - - // there is no reason for calculate items if interact is hovering the header - if (must) { - continue; - } - - for (uint64_t it_item {visible_begin_index}; it_item < visible_count; it_item++) { - ekg::item &item {item_header.at(it_item)}; - ekg::placement &placement {item.unsafe_get_placement()}; - - item_rect = placement.rect + scrollable_rect; - item_rect.w = (item_rect.w * !is_multicolumn) + (rect.w * is_multicolumn); // is_multicolumn ? ui_rect.w : item_rect.w - item_rect.x = (item_rect.x * !is_multicolumn) + (rect.x * is_multicolumn); - - flags = item.get_attr(); - hovering = ekg::rect_collide_vec(item_rect, interact); - - if (!ekg_bitwise_contains(flags, ekg::attr::hovering) && hovering) { - ekg_bitwise_add(flags, ekg::attr::hovering); - item.set_attr(flags); - } else if (ekg_bitwise_contains(flags, ekg::attr::hovering) && !hovering) { - ekg_bitwise_remove(flags, ekg::attr::hovering); - item.set_attr(flags); - } - - if (ekg_bitwise_contains(flags, ekg::attr::focused)) { - was_selected = true; - } - - item.unsafe_get_addressed()->set_attr(item.get_attr()); - item.unsafe_get_addressed()->unsafe_get_placement() = item.unsafe_get_placement(); - - if (item_rect.y + item_rect.h > bottom_place) { - break; - } - } - - this->header_relative_x += placement_header.rect.w; - } - - break; - } -} - -void ekg::ui::listbox_widget::on_post_event(ekg::os::io_event_serial &io_event_serial) { - abstract_widget::on_post_event(io_event_serial); - this->embedded_scroll.flag.hovered = false; - this->embedded_scroll.flag.activity = false; -} - -void ekg::ui::listbox_widget::on_update() { - this->embedded_scroll.on_update(); - this->is_high_frequency = this->embedded_scroll.check_activity_state(this->flag.was_hovered); -} - -void ekg::ui::listbox_widget::on_draw_refresh() { - ekg::rect &rect {this->get_abs_rect()}; - ekg::ui::listbox *p_ui {static_cast(this->p_data)}; - ekg::service::theme_scheme_t &theme_scheme {ekg::current_theme_scheme()}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(p_ui->get_item_font_size())}; - - if (this->must_update_items) { - ekg::reload(this); - } - - /* The header offset transformed to content place! */ - - this->rect_content_place.x = 0.0f; - this->rect_content_place.h = rect.h - this->rect_content_place.y - this->embedded_scroll.rect_horizontal_scroll_bar.h; - this->rect_content_place.w = rect.w - this->embedded_scroll.rect_vertical_scroll_bar.w; - - /* Insert the absolute rect position to the place, the content abs is addressed to scrollbar! */ - - this->rect_content_abs = this->rect_content_place + rect; - this->embedded_scroll.clamp_scroll(); - - EKG_ASSERT_SCISSOR( - this->scissor, - rect, - this->p_parent_scissor - ); - ekg_draw_assert_scissor(); - - ekg::draw::rect( - rect, - theme_scheme.listbox_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::background) - ); - - ekg::rect scrollable_rect {this->rect_content_abs + this->embedded_scroll.scroll}; - ekg::rect content_scissor_bounding {}; - ekg::rect widget_absolute_rect_scissor {this->scissor}; - - float bottom_place {this->rect_content_abs.y + this->rect_content_abs.h}; - - ekg::flags flags {p_ui->get_column_header_align()}; - uint64_t rendering_cache_size {this->item_rendering_cache.size()}; - - bool is_header_targeted {}; - bool is_multicolumn {p_ui->get_mode() == ekg::mode::multicolumn}; - - bool is_column_header_top { - ekg_bitwise_contains(flags, ekg::dock::top) || - !ekg_bitwise_contains(flags, ekg::dock::bottom) - }; - - float normalized_horizontal_scroll {this->embedded_scroll.get_normalized_vertical_scroll()}; - this->header_relative_x = 0.0f; - - scrollable_rect.x = static_cast(scrollable_rect.x); - scrollable_rect.y = static_cast(scrollable_rect.y); - - for (uint64_t it_header {}; it_header < rendering_cache_size; it_header++) { - ekg::item &item_header {this->item_rendering_cache.at(it_header)}; - ekg::placement &placement_header {item_header.unsafe_get_placement()}; - - is_header_targeted = this->target_dragging == static_cast(it_header); - - if (is_header_targeted) { - this->header_relative_x += placement_header.rect.w + ekg_pixel; - continue; - } - - this->render_item( - item_header, - placement_header, - widget_absolute_rect_scissor, - scrollable_rect, - content_scissor_bounding, - bottom_place, - normalized_horizontal_scroll, - is_header_targeted, - is_column_header_top, - is_multicolumn, - f_renderer - ); - - this->header_relative_x += placement_header.rect.w + ekg_pixel; - } - - EKG_ASSERT_SCISSOR( - this->scissor, - rect, - this->p_parent_scissor - ); - - /* instead of rendering the scroll content with a reduced borders, we re-clamp value to the widget abs-rect boundings */ - - this->rect_content_abs.h = rect.h - this->rect_content_place.y; - this->rect_content_abs.x = rect.x; - this->rect_content_abs.w = rect.w; - - this->embedded_scroll.scissor = this->scissor; - this->embedded_scroll.on_draw_refresh(); - - ekg::draw::rect( - rect, - theme_scheme.listbox_outline, - ekg::draw_mode::outline - ); - - if (this->target_dragging != -1 && this->target_dragging < rendering_cache_size) { - ekg::item &item_header {this->item_rendering_cache.at(this->target_dragging)}; - ekg::placement &placement_header {item_header.unsafe_get_placement()}; - - is_header_targeted = true; - this->render_item( - item_header, - placement_header, - widget_absolute_rect_scissor, - scrollable_rect, - content_scissor_bounding, - bottom_place, - normalized_horizontal_scroll, - is_header_targeted, - is_column_header_top, - is_multicolumn, - f_renderer - ); - } -} - -void ekg::ui::listbox_widget::render_item( - ekg::item &item_header, - ekg::placement &placement_header, - ekg::rect widget_absolute_rect_scissor, - ekg::rect scrollable_rect, - ekg::rect content_scissor_bounding, - float bottom_place, - float normalized_horizontal_scroll, - bool is_header_targeted, - bool is_column_header_top, - bool is_multicolumn, - ekg::draw::font_renderer &f_renderer -) { - ekg::service::theme_scheme_t &theme_scheme {ekg::current_theme_scheme()}; - ekg::vec4 &interact {ekg::input::interact()}; - - ekg::rect multicolumn_item_rect {}; - ekg::rect &rect {this->get_abs_rect()}; - ekg::rect item_rect {}; - - bool must_stop_rendering {}; - ekg::flags flags {item_header.get_attr()}; - - ekg::rect targeted_rect {this->rect_current_dragging_targeted_header}; - targeted_rect.y += rect.y; - - if (is_header_targeted) { - item_rect = targeted_rect; - item_rect.h = rect.h; - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_drag_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::background) - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_drag_outline, - ekg::draw_mode::outline - ); - - item_rect = targeted_rect; - } else { - item_rect = placement_header.rect + rect; - item_rect.x = item_rect.x + this->embedded_scroll.scroll.x; - } - - EKG_ASSERT_SCISSOR( - this->scissor, - item_rect, - &widget_absolute_rect_scissor - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_header_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::background) - ); - - if (ekg_bitwise_contains(flags, ekg::attr::hovering)) { - ekg::draw::rect( - item_rect, - theme_scheme.listbox_header_highlight, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::highlight) - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_header_highlight_outline, - ekg::draw_mode::outline, - ekg_layer(ekg::layer::highlight) - ); - } - - f_renderer.blit( - item_header.get_value(), - item_rect.x + placement_header.rect_text.x, - item_rect.y + placement_header.rect_text.y, - theme_scheme.listbox_header_string - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_header_outline, - ekg::draw_mode::outline - ); - - EKG_ASSERT_SCISSOR( - this->scissor, - this->rect_content_abs, - this->p_parent_scissor - ); - - content_scissor_bounding = this->scissor; - - uint64_t visible_count {item_header.get_visible_count()}; - uint64_t visible_begin_index {ekg::get_index_by_normalized_scroll(normalized_horizontal_scroll, visible_count)}; - - for (uint64_t it_item {visible_begin_index}; it_item < visible_count; it_item++) { - ekg::item &item {item_header.at(it_item)}; - ekg::placement &placement {item.unsafe_get_placement()}; - - placement.rect.w = placement_header.rect.w - placement.offset; - flags = item.get_attr(); - - if (is_header_targeted) { - item_rect = placement.rect + scrollable_rect; - item_rect.x = targeted_rect.x + placement.offset; - } else { - placement.rect.x = this->header_relative_x + placement.offset; // must update the size when resize a header - item_rect = placement.rect + scrollable_rect; - } - - /** - * The point of adding one pixel on width, fix the rendering of - * offset between headers space. The header is not the same - * size of items for highlight & select, so the item must be - * complete filled and not remains the offset header. - **/ - item_rect.w += ekg_pixel; - - EKG_ASSERT_SCISSOR( - this->scissor, - item_rect, - &content_scissor_bounding - ); - - must_stop_rendering = item_rect.y + item_rect.h > bottom_place; - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_item_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::sub_background) - ); - - if (ekg_bitwise_contains(flags, ekg::attr::hovering)) { - ekg::draw::rect( - item_rect, - theme_scheme.listbox_item_highlight, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::highlight) - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_item_highlight_outline, - ekg::draw_mode::outline - ); - - /** - * I chosen to desync the cache and the item, due a glitch while hovering and - * scrolling fast, making spam tons of items-hovered; - * - * Normally it is unhovered but if the input is totally stop, item does not, - * also the not-rendering items are stop, then on rendering is the best - * place to clean items flags. - **/ - - multicolumn_item_rect = item_rect; - multicolumn_item_rect.x = rect.x; - multicolumn_item_rect.w = rect.w; - - if ( - (is_multicolumn && !ekg::rect_collide_vec(multicolumn_item_rect, interact)) - || - (!is_multicolumn && !ekg::rect_collide_vec(item_rect, interact)) - ) { - item.set_attr(ekg_bitwise_remove(flags, ekg::attr::hovering)); - } - } - - if (ekg_bitwise_contains(flags, ekg::attr::focused)) { - ekg::draw::rect( - item_rect, - theme_scheme.listbox_item_focused, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::activity) - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_item_highlight_outline, - ekg::draw_mode::outline - ); - } - - f_renderer.blit( - item.get_value(), - item_rect.x + placement.rect_text.x, - item_rect.y + placement.rect_text.y, - theme_scheme.listbox_item_string - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_item_outline, - ekg::draw_mode::outline - ); - - if (must_stop_rendering) { - break; - } - } - - if (!is_header_targeted && is_column_header_top) { - item_rect = placement_header.rect + rect; - item_rect.x = static_cast(item_rect.x + this->embedded_scroll.scroll.x) - ekg_pixel; - item_rect.y = static_cast(item_rect.y + item_rect.h); - item_rect.w = ekg_pixel; - item_rect.h = rect.h; - - EKG_ASSERT_SCISSOR( - this->scissor, - item_rect, - &widget_absolute_rect_scissor - ); - - ekg::draw::rect( - item_rect, - theme_scheme.listbox_line_separator, - ekg::draw_mode::filled - ); - } -} - -void ekg::ui::listbox_extent_align(ekg::rect &rect) { - rect.x -= ekg_pixel; - rect.w += ekg_pixel; -} - -void ekg::ui::listbox_template_reload( - ekg::item &rendering_cache, - ekg::item &parent, - ekg::rect &ui_rect, - ekg::rect &header_rect, - ekg::font &item_font, - ekg::rect &relative_rect, - int32_t item_scaled_height, - uint64_t header_index, - uint64_t &arbitrary_index_pos, - uint64_t &rendering_cache_arbitrary_index_pos, - bool &opened, - ekg::mode mode, - bool *p_semaphore -) { - ekg::layout::mask &mask {ekg::core->mask}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(item_font)}; - ekg::rect item_rect {}; - ekg::service::theme_scheme_t &theme_scheme {ekg::current_theme_scheme()}; - - uint64_t it {}; - int32_t text_lines {}; - ekg::flags flags {}; - - bool is_empty {}; - bool is_major_column {arbitrary_index_pos == 0}; - bool is_opened_flagged {}; - - float text_width {}; - float text_height {f_renderer.get_text_height()}; - - float dimension_offset {text_height / 2}; - float offset {}; - - bool should_apply_offset_by_column_based { - (mode == ekg::mode::multicolumn && header_index == 0) || mode == ekg::mode::singlecolumn - }; - - float additional_offset_by_column_based { - (theme_scheme.listbox_subitem_offset_space + ekg_pixel) * should_apply_offset_by_column_based - }; - - for (; it < parent.size(); it++) { - ekg::item &item {parent.at(it)}; - ekg::placement &placement {item.unsafe_get_placement()}; - - item.unsafe_set_semaphore(p_semaphore); - item_rect = ui_rect + placement.rect; - flags = item.get_attr(); - - text_lines = 1; - text_width = f_renderer.get_text_width(item.get_value(), text_lines); - - offset = ekg::find_min_offset(text_width, dimension_offset); - placement.offset = ekg_min(relative_rect.x - header_rect.x, 0.0f); - - placement.rect.x = relative_rect.x; - placement.rect.y = relative_rect.y; - - placement.rect.w = relative_rect.w - placement.offset; - placement.rect.h = (text_height + dimension_offset) * static_cast(item_scaled_height); - - placement.rect_text.w = text_width; - placement.rect_text.h = text_height * static_cast(text_lines); - - mask.preset( - { - offset, offset, placement.rect.h - }, - ekg::axis::horizontal, - placement.rect.w - ); - - mask.insert({&placement.rect_text, placement.text_dock_flags}); - mask.docknize(); - - relative_rect.h = placement.rect.h; - - if (is_major_column || opened) { - if (rendering_cache_arbitrary_index_pos >= rendering_cache.size()) { - rendering_cache.emplace_back(); - } - - rendering_cache[rendering_cache_arbitrary_index_pos].unsafe_get_placement() = placement; - rendering_cache[rendering_cache_arbitrary_index_pos].set_value(item.get_value()); - rendering_cache[rendering_cache_arbitrary_index_pos].set_attr(item.get_attr()); - rendering_cache[rendering_cache_arbitrary_index_pos].set_text_align(item.get_text_align()); - rendering_cache[rendering_cache_arbitrary_index_pos].unsafe_set_addressed(&item); - rendering_cache_arbitrary_index_pos++; - } - - is_opened_flagged = ekg_bitwise_contains(flags, ekg::attr::opened); - is_empty = item.empty(); - - opened = is_opened_flagged; - arbitrary_index_pos++; - - if (!is_empty) { - relative_rect.x += additional_offset_by_column_based; - //relative_rect.w -= additional_offset_by_column_based; - - ekg::ui::listbox_template_reload( - rendering_cache, - item, - ui_rect, - header_rect, - item_font, - relative_rect, - item_scaled_height, - header_index, - arbitrary_index_pos, - rendering_cache_arbitrary_index_pos, - opened, - mode, - p_semaphore - ); - - relative_rect.x -= additional_offset_by_column_based; - //relative_rect.w += additional_offset_by_column_based; - } - - if (is_opened_flagged) { - opened = 0; - } - - relative_rect.y -= (relative_rect.y - placement.rect.y) * !is_opened_flagged; - relative_rect.y += placement.rect.h; - } -} - -void ekg::ui::listbox_template_on_event( - ekg::os::io_event_serial &io_event_serial, - uint64_t &arbitrary_index_pos, - ekg::item &rendering_cache, - bool motion, - bool released, - bool pressed_select_many, - bool pressed_select, - bool pressed_open, - bool &was_selected, - bool was_hovered, - ekg::item &parent, - ekg::vec2 &ui_pos, - ekg::rect &ui_rect, - ekg::rect &relative_rect, - ekg::mode mode -) { - ekg::vec4 &interact {ekg::input::interact()}; - ekg::rect item_rect {}; - - bool hovering {}; - ekg::flags flags {}; - bool contains_flag {}; - bool is_multicolumn {mode == ekg::mode::multicolumn}; - uint64_t rendering_cache_size {rendering_cache.size()}; - - for (uint64_t it {}; it < parent.size(); it++) { - ekg::item &item {parent.at(it)}; - ekg::placement &placement {item.unsafe_get_placement()}; - - item_rect = placement.rect + ui_rect; - item_rect.w = (item_rect.w * !is_multicolumn) + (ui_rect.w * is_multicolumn); // multicolumn ? ui_rect.w : item_rect.w - item_rect.x = (item_rect.x * !is_multicolumn) + (ui_rect.x * is_multicolumn); - - flags = item.get_attr(); - hovering = ekg::rect_collide_vec(item_rect, interact); - - if (!item.empty() && hovering && pressed_open) { - contains_flag = static_cast(ekg_bitwise_contains(flags, ekg::attr::opened)); - ekg_bitwise_remove(flags, ekg::attr::opened * contains_flag); - ekg_bitwise_add(flags, ekg::attr::opened * !contains_flag); - item.set_attr(flags); - } - - if (hovering && pressed_select_many && was_hovered) { - contains_flag = static_cast(ekg_bitwise_contains(flags, ekg::attr::focused)); - ekg_bitwise_remove(flags, ekg::attr::focused * contains_flag); - ekg_bitwise_add(flags, ekg::attr::focused * !contains_flag); - item.set_attr(flags); - } else if (pressed_select && hovering && !ekg_bitwise_contains(flags, ekg::attr::focused) && was_hovered) { - ekg_bitwise_add(flags, ekg::attr::focused); - item.set_attr(flags); - } else if (!hovering && pressed_select && ekg_bitwise_contains(flags, ekg::attr::focused) && was_hovered) { - ekg_bitwise_remove(flags, ekg::attr::focused); - item.set_attr(flags); - } - - if (ekg_bitwise_contains(flags, ekg::attr::focused)) { - was_selected = true; - } - - placement.rect.y = relative_rect.y; - relative_rect.h = placement.rect.h; - relative_rect.y += placement.rect.h; - - if (arbitrary_index_pos < rendering_cache_size) { - rendering_cache[arbitrary_index_pos].unsafe_get_placement() = placement; - rendering_cache[arbitrary_index_pos].set_value(item.get_value()); - rendering_cache[arbitrary_index_pos].set_attr(item.get_attr()); - rendering_cache[arbitrary_index_pos].set_text_align(item.get_text_align()); - rendering_cache[arbitrary_index_pos].unsafe_set_addressed(&item); - arbitrary_index_pos++; - } - - if (!item.empty() && ekg_bitwise_contains(item.get_attr(), ekg::attr::opened)) { - ekg::ui::listbox_template_on_event( - io_event_serial, - arbitrary_index_pos, - rendering_cache, - motion, - released, - pressed_select_many, - pressed_select, - pressed_open, - was_selected, - was_hovered, - item, - ui_pos, - ui_rect, - relative_rect, - mode - ); - } - } -} diff --git a/src/ui/popup/ui_popup.cpp b/src/ui/popup/ui_popup.cpp deleted file mode 100644 index 336ec9fc..00000000 --- a/src/ui/popup/ui_popup.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/popup/ui_popup.hpp" -#include "ekg/util/gui.hpp" -#include "ekg/util/text.hpp" - -int64_t ekg::ui::popup::contains(std::string_view item_name) { - for (uint32_t it {}; it < this->item_list.size(); it++) { - if (this->item_list.at(it).name == item_name) { - return it; - } - } - - return -1; -} - -ekg::ui::popup *ekg::ui::popup::insert(const std::vector &item_name_list) { - for (const std::string &item_name: item_name_list) { - this->insert(item_name); - } - - return this; -} - -ekg::ui::popup *ekg::ui::popup::insert(std::string_view item_name) { - ekg::flags attribute_flags {}; - uint8_t start_index {ekg::check_attribute_flags(item_name, attribute_flags)}; - - if (start_index) { - item_name = item_name.substr(start_index, item_name.size()); - } - - if (item_name.empty() || this->contains(item_name) != -1) { - return this; - } - - ekg::ui::item item {}; - item.name = item_name; - item.attributes = attribute_flags; - this->item_list.push_back(item); - return this; -} - -ekg::ui::popup *ekg::ui::popup::link(std::string_view item_name, ekg::ui::popup *popup_linked) { - int64_t index {this->contains(item_name)}; - - if (index == -1) { - this->insert(item_name); - } - - auto &item {this->item_list.at(index)}; - item.linked_id = 0; - - if (popup_linked) { - popup_linked->set_visible(false); - item.linked_id = popup_linked->get_id(); - this->add_child(item.linked_id); - } - - return this; -} - -ekg::ui::popup *ekg::ui::popup::erase(std::string_view item_name) { - std::vector new_list {}; - for (ekg::ui::item &item: this->item_list) { - if (item.name == item_name) { - this->remove_child(item.linked_id); - continue; - } - - new_list.push_back(item); - } - - this->item_list.clear(); - this->item_list.insert(this->item_list.end(), new_list.begin(), new_list.end()); - - return this; -} - -ekg::ui::item &ekg::ui::popup::get(uint64_t index) { - return this->item_list.at(index); -} - -std::vector &ekg::ui::popup::get_item_list() { - return this->item_list; -} - -ekg::ui::popup *ekg::ui::popup::set_text_align(ekg::flags flags) { - if (this->dock_flags != flags) { - this->dock_flags = flags; - ekg::reload(this->id); - } - - return this; -} - -ekg::flags ekg::ui::popup::get_text_align() { - return this->dock_flags; -} - -ekg::ui::popup *ekg::ui::popup::set_width(float width) { - if (this->sync_ui.w != width) { - this->sync_ui.w = width; - - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::dimension)); - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::set_width)); - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -float ekg::ui::popup::get_width() { - return this->rect_widget.w; -} - -ekg::ui::popup *ekg::ui::popup::set_scaled_height(int32_t scaled_height_factor) { - if (this->scaled_height != scaled_height_factor) { - this->scaled_height = scaled_height_factor; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -int32_t ekg::ui::popup::get_scaled_height() { - return this->scaled_height; -} - -float ekg::ui::popup::get_height() { - return this->rect_widget.h; -} - -ekg::ui::popup *ekg::ui::popup::set_font_size(ekg::font f_size) { - if (this->font_size != f_size) { - this->font_size = f_size; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -ekg::font ekg::ui::popup::get_font_size() { - return this->font_size; -} - -ekg::ui::popup *ekg::ui::popup::set_pos(float x, float y) { - if (this->sync_ui.x != x || this->sync_ui.y != y) { - this->sync_ui.x = x; - this->sync_ui.y = y; - - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::dimension)); - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::set_x)); - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::set_y)); - - ekg::reload(this->id); - } - - return this; -} - -ekg::vec2 ekg::ui::popup::get_pos() { - return {this->rect_widget.x, this->rect_widget.y}; -} \ No newline at end of file diff --git a/src/ui/popup/ui_popup_widget.cpp b/src/ui/popup/ui_popup_widget.cpp deleted file mode 100644 index f261b704..00000000 --- a/src/ui/popup/ui_popup_widget.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/popup/ui_popup_widget.hpp" -#include "ekg/ui/popup/ui_popup.hpp" -#include "ekg/core/runtime.hpp" -#include "ekg/draw/draw.hpp" - -void ekg::ui::popup_widget::get_popup_path(std::string &path) { - if (this->hovered_element == -1) { - return; - } - - ekg::ui::popup *p_ui {(ekg::ui::popup *) this->p_data}; - auto &item {p_ui->get_item_list().at(this->hovered_element)}; - - ekg::ui::popup_widget *p_popup {}; - path += item.name; - - if (this->popup_opened != -1 && - (p_popup = (ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(item.linked_id)) != nullptr) { - path += " | "; - p_popup->get_popup_path(path); - } -} - -bool ekg::ui::popup_widget::is_hovering_any_popup(int32_t top_level) { - ekg::ui::popup *p_ui {(ekg::ui::popup *) this->p_data}; - bool is_hovering {ekg::rect_collide_vec(this->get_abs_rect(), ekg::input::interact())}; - - ekg::ui::item item {}; - ekg::ui::popup_widget *p_popup {}; - int32_t item_it {this->popup_opened}; - - if (top_level != this->top_level_popup && - (p_popup = (ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(top_level)) != nullptr) { - item_it = p_popup->popup_opened; - } - - if (item_it != -1 && (item = p_ui->get_item_list().at(item_it)).linked_id != 0 && - (p_popup = (ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(item.linked_id)) != nullptr) { - is_hovering = is_hovering || p_popup->is_hovering_any_popup(p_popup->p_data->get_id()); - } - - return is_hovering; -} - -void ekg::ui::popup_widget::unset_visible_all_sub_popup() { - if (this->popup_opened == -1) { - return; - } - - ekg::ui::popup *p_ui {(ekg::ui::popup *) this->p_data}; - auto &item {p_ui->get_item_list().at(this->popup_opened)}; - auto p_popup {(ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(item.linked_id)}; - - if (p_popup->p_data->is_visible()) { - p_popup->p_data->set_visible(false); - ekg::reload(item.linked_id); - ekg::dispatch(ekg::env::redraw); - } - - p_popup->unset_visible_all_sub_popup(); - this->popup_opened = -1; -} - -void ekg::ui::popup_widget::on_reload() { - ekg::ui::abstract_widget::on_reload(); - - ekg::ui::popup *p_ui {(ekg::ui::popup *) this->p_data}; - auto &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - auto &item_list {p_ui->get_item_list()}; - ekg::layout::mask &mask {ekg::core->mask}; - - float max_width {}; - float text_width {}; - float text_height {f_renderer.get_text_height()}; - float dimension_offset {text_height / 2}; - - /* First phase: get text metrics and prevent multi-iterations. */ - for (ekg::ui::item &items: item_list) { - text_width = f_renderer.get_text_width(items.name); - items.rect_content.w = text_width; - items.rect_content.h = text_height; - - if (text_width > max_width) { - max_width = text_width; - } - } - - /* Second phase: compute bounds and layout mask. */ - - auto text_dock_flags {p_ui->get_text_align()}; - float offset {ekg::find_min_offset(max_width, dimension_offset)}; - float size {(text_height + dimension_offset) * static_cast(p_ui->get_scaled_height())}; - - ekg::vec3 layout_offset {offset, offset, text_height + dimension_offset}; - ekg::rect &layout_mask {mask.get_rect()}; - - this->dimension.w = ekg_min(this->dimension.w, max_width + dimension_offset); - this->dimension.h = offset; - this->separator_offset = offset; - - for (ekg::ui::item &items: item_list) { - items.rect_dimension.x = 0.0f; - items.rect_dimension.y = this->dimension.h; - items.rect_dimension.w = this->dimension.w; - items.rect_dimension.h = size; - - layout_offset.z = items.rect_dimension.h; - - mask.preset(layout_offset, ekg::axis::horizontal, this->dimension.w); - mask.insert({&items.rect_content, text_dock_flags}); - mask.docknize(); - - // Process the popup height based in layout mask. - this->dimension.h += layout_mask.h; - } - - // @TODO fix the final size of popup which is a bit more higher than the initial offset. - - /* Extra offset to final rect. */ - this->dimension.h += offset; - - /* Reset scissor height if current state is invisible. */ - if (!p_ui->is_visible()) { - this->scissor.h = this->scissor_opened_height = 0; - this->is_high_frequency = false; - } else if (p_ui->is_visible() && !this->is_high_frequency) { - ekg::update_high_frequency(this); - this->elapsed_animation_ticks = ekg::timing::ticks; - } - - /* Fix screen position at space. */ - if (!p_ui->has_parent()) { - const float display_w {static_cast(ekg::ui::width)}; - const float display_h {static_cast(ekg::ui::height)}; - - if (this->p_parent->x < 0) { - this->p_parent->x = 0; - } - - if (this->p_parent->y < 0) { - this->p_parent->y = 0; - } - - if (this->p_parent->x + this->dimension.w > display_w) { - this->p_parent->x = display_w - this->dimension.w; - } - - if (this->p_parent->y + this->dimension.h > display_h) { - this->p_parent->y = display_h - this->dimension.h; - } - } - - this->min_size.x = ekg_min(this->min_size.x, this->dimension.w); - this->min_size.y = ekg_min(this->min_size.y, this->dimension.h); -} - -void ekg::ui::popup_widget::on_pre_event(ekg::os::io_event_serial &io_event_serial) { - if (!this->p_data->is_visible()) { - return; - } - - abstract_widget::on_pre_event(io_event_serial); -} - -void ekg::ui::popup_widget::on_event(ekg::os::io_event_serial &io_event_serial) { - if (!this->p_data->is_visible()) { - return; - } - - bool check_hovered {}; - bool should_destroy {}; - bool pressed {ekg::input::pressed() && ekg::input::action("popup-activity")}; - bool released {ekg::input::released()}; - - ekg::ui::popup *p_ui {(ekg::ui::popup *) this->p_data}; - auto &item_list {p_ui->get_item_list()}; - auto &rect {this->get_abs_rect()}; - - ekg::ui::popup_widget *p_popup {}; - - /* Auto-check is this popup is the top-level popup. */ - this->parent_id_popup_opened = !p_ui->has_parent() || this->parent_id_popup_opened; - this->top_level_popup = !p_ui->has_parent() ? p_ui->get_id() : this->top_level_popup; - - int32_t hovered {-1}; - if (ekg::input::motion() || pressed || released) { - check_hovered = this->flag.hovered; - } - - if (check_hovered) { - ekg::vec4 &interact {ekg::input::interact()}; - ekg::rect &rect {this->get_abs_rect()}; - - float ypos {}; - int32_t hover {}; - float popup_offset {2.0f}; - - for (int32_t it {}; it < item_list.size(); it++) { - auto &item {item_list.at(it)}; - hover = -1; - - if (ekg::rect_collide_vec(item.rect_dimension + rect, interact)) { - hovered = it; - hover = it; - } - - switch (hover) { - case -1: - if (this->popup_opened == it && item.linked_id != 0 && - (p_popup = (ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(item.linked_id)) != nullptr) { - p_popup->p_data->set_visible(false); - p_popup->top_level_popup = this->top_level_popup; - - p_popup->parent_id_popup_opened = false; - p_popup->unset_visible_all_sub_popup(); - - ekg::reload(item.linked_id); - ekg::dispatch(ekg::env::redraw); - this->popup_opened = -1; - } - - break; - default: - /* When the popup is linked with another some other popup widget, this should showcase the popup linked. */ - if (this->popup_opened != hover && item.linked_id != 0 && - (p_popup = (ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(item.linked_id)) != nullptr) { - p_popup->p_data->set_visible(true); - p_popup->top_level_popup = this->top_level_popup; - - auto &popup_rect {p_popup->dimension}; - const float display_w {static_cast(ekg::ui::width)}; - const float display_h {static_cast(ekg::ui::height)}; - - if (rect.x + rect.w + (popup_rect.w - popup_offset) > display_w) { - popup_rect.x = -(popup_rect.w - popup_offset); - } else { - popup_rect.x = rect.w - popup_offset; - } - - if (rect.y + ypos + popup_rect.h > display_h) { - popup_rect.y = ypos - (rect.y + ypos + popup_rect.h - display_h); - } else { - popup_rect.y = ypos; - } - - ekg::reload(p_popup->p_data->get_id()); - ekg::dispatch(ekg::env::redraw); - - p_popup->parent_id_popup_opened = true; - this->popup_opened = hover; - } - - /* And if there hovered is not linked with any and there is a shown popup, must close the shown popup. */ - if (this->popup_opened != -1 && item.linked_id == 0) { - auto &popup_opened_item {item_list.at(this->popup_opened)}; - if (popup_opened_item.linked_id != 0 && - (p_popup = (ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(popup_opened_item.linked_id)) != - nullptr) { - p_popup->p_data->set_visible(false); - p_popup->top_level_popup = this->top_level_popup; - - p_popup->parent_id_popup_opened = false; - p_popup->unset_visible_all_sub_popup(); - - ekg::reload(item.linked_id); - ekg::dispatch(ekg::env::redraw); - } - - this->popup_opened = -1; - } - - break; - } - - ypos += item.rect_dimension.h; - } - - ekg::set(this->hovered_element, hovered); - } - - bool is_hovering_any_popup_flag {this->is_hovering_any_popup(this->top_level_popup)}; - - /* This should be destroy when a click happens out of any sub-popup or top-level popup rect. */ - if (!check_hovered && !is_hovering_any_popup_flag && !p_ui->has_parent() && (pressed || released)) { - should_destroy = true; - this->popup_opened = -1; - } - - /* Process the final input. */ - if (((pressed || released) && this->popup_opened == -1 && is_hovering_any_popup_flag)) { - p_popup = (ekg::ui::popup_widget *) ekg::core->get_fast_widget_by_id(this->top_level_popup); - p_popup->p_data->destroy(); - - std::string path_result {}; - p_popup->get_popup_path(path_result); - return; - } - - if (should_destroy) { - this->p_data->destroy(); - } -} - -void ekg::ui::popup_widget::on_update() { - auto &rect {this->get_abs_rect()}; - float animation { - ekg::smooth(static_cast(ekg::current_theme_scheme().popup_drop_animation_delay), ekg::timing::ticks - this->elapsed_animation_ticks) - }; - - this->scissor_opened_height = animation * this->dimension.h; - - if (ekg_equals_float(this->scissor_opened_height, this->dimension.h)) { - scissor_opened_height = this->dimension.h; - this->is_high_frequency = false; - } - - this->scissor.x = rect.x; - this->scissor.y = rect.y; - this->scissor.w = rect.w; - this->scissor.h = this->scissor_opened_height; -} - -void ekg::ui::popup_widget::on_draw_refresh() { - ekg::ui::popup *p_ui {(ekg::ui::popup *) this->p_data}; - auto &rect {this->get_abs_rect()}; - auto &theme_scheme {ekg::current_theme_scheme()}; - auto &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - auto &item_list {p_ui->get_item_list()}; - - EKG_ASSERT_SCISSOR(this ->scissor, rect, this->p_parent_scissor); - ekg::draw::rect(rect, theme_scheme.popup_background, ekg::draw_mode::filled, ekg_layer(ekg::layer::background)); - - ekg::rect button_rect {}; - - for (int32_t it {}; it < item_list.size(); it++) { - auto &item {item_list.at(it)}; - - // Draw the separator. - if (ekg_bitwise_contains(item.attributes, ekg::attr::separator)) { - ekg::draw::rect( - rect.x + item.rect_dimension.x, - rect.y + item.rect_dimension.y + item.rect_dimension.h - ekg_pixel_div_2, - item.rect_dimension.w, - 1.0f, - theme_scheme.popup_separator, - ekg::draw_mode::filled - ); - } - - if (this->hovered_element == it) { - ekg::draw::rect( - item.rect_dimension + rect, - theme_scheme.popup_highlight, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::highlight) - ); - } - - f_renderer.blit( - item.name, - rect.x + item.rect_dimension.x + item.rect_content.x, - rect.y + item.rect_dimension.y + item.rect_content.y, - theme_scheme.popup_string - ); - } - - ekg::draw::rect(rect, theme_scheme.popup_outline, ekg::draw_mode::outline); -} diff --git a/include/ekg/ui/combobox/ui_combobox_widget.hpp b/src/ui/property.cpp similarity index 87% rename from include/ekg/ui/combobox/ui_combobox_widget.hpp rename to src/ui/property.cpp index be69fa12..fc9ca5b3 100644 --- a/include/ekg/ui/combobox/ui_combobox_widget.hpp +++ b/src/ui/property.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,9 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/ui/property.hpp" -#ifndef EKG_UI_COMBOBOX_WIDGET_H -#define EKG_UI_COMBOBOX_WIDGET_H +ekg::property_t ekg::property_t::not_found { + .at = ekg::at_t::not_found +}; - -#endif \ No newline at end of file diff --git a/src/ui/scrollbar/scrollbar_widget.cpp b/src/ui/scrollbar/scrollbar_widget.cpp deleted file mode 100644 index ffb4af94..00000000 --- a/src/ui/scrollbar/scrollbar_widget.cpp +++ /dev/null @@ -1,674 +0,0 @@ -#include "ekg/ui/scrollbar/scrollbar_widget.hpp" -#include "ekg/draw/font_renderer.hpp" -#include "ekg/core/runtime.hpp" -#include "ekg/draw/shape.hpp" - -float ekg::ui::scrollbar::get_horizontal_scroll_normalized() { - if (this->descriptor.p_binded_rect == nullptr) { - return 0.0f; - } - - return (this->scroll.x / this->descriptor.p_binded_rect->w); -} - -float ekg::ui::scrollbar::get_vertical_scroll_normalized() { - if (this->descriptor.p_binded_rect == nullptr) { - return 0.0f; - } - - return (this->scroll.y / this->descriptor.p_binded_rect->h); -} - -void ekg::ui::scrollbar::check_scroll() { - if (this->descriptor.p_binded_rect == nullptr) { - return; - } - - this->states.is_scrolling.z = this->scrollable_area.w > this->descriptor.p_binded_rect->w; - this->states.is_scrolling.w = this->scrollable_area.h > this->descriptor.p_binded_rect->h; -} - -void ekg::ui::scrollbar::reset_scroll() { - this->scroll.x = this->scroll.z; - this->scroll.y = this->scroll.w; -} - -void ekg::ui::scrollbar::clamp_scroll() { - if (this->descriptor.p_binded_rect == nullptr) { - return; - } - - ekg::vec2_t vertical_scroll_limit {0.0f, this->descriptor.p_binded_rect->h - this->scrollable_area.h}; - ekg::vec2_t horizontal_scroll_limit {0.0f, this->descriptor.p_binded_rect->w - this->scrollable_area.w}; - - if (this->scrollable_area.h < this->descriptor.p_binded_rect->h) { - this->scroll.y = 0.0f; - this->scroll.w = 0.0f; - } else if (this->scroll.y < vertical_scroll_limit.y) { - this->scroll.y = vertical_scroll_limit.y; - this->scroll.w = vertical_scroll_limit.y; - } else if (this->scroll.y > vertical_scroll_limit.x) { - this->scroll.y = vertical_scroll_limit.x; - this->scroll.w = vertical_scroll_limit.x; - } - - if (this->scrollable_area.w < this->descriptor.p_binded_rect->w) { - this->scroll.x = 0.0f; - this->scroll.z = 0.0f; - } else if (this->scroll.x < horizontal_scroll_limit.y) { - this->scroll.x = horizontal_scroll_limit.y; - this->scroll.z = horizontal_scroll_limit.y; - } else if (this->scroll.x > horizontal_scroll_limit.x) { - this->scroll.x = horizontal_scroll_limit.x; - this->scroll.z = horizontal_scroll_limit.x; - } -} - -bool ekg::ui::scrollbar::is_scrolling(bool state) { - state = ( - state - || - static_cast(roundf(this->scroll.x)) != static_cast(roundf(this->scroll.z)) - || - static_cast(roundf(this->scroll.y)) != static_cast(roundf(this->scroll.w)) - ); - - if (!state) { - this->scroll.x = this->scroll.z; - this->scroll.y = this->scroll.w; - } - - return state; -} - -void ekg::ui::scrollbar::on_create() { - ekg::ui::abstract::on_create(); - - if (!this->descriptor.p_binded_rect) { - this->descriptor.p_binded_rect = &this->scrollable_area; - } - - if (!this->p_descriptor_rect) { - this->p_descriptor_rect = &this->_blank_descriptor_rect; - } -} - -void ekg::ui::scrollbar::on_reload() { - if ( - this->descriptor.p_binded_rect == nullptr - ) { - return; - } - - this->check_scroll(); - - if ( - this->descriptor.p_binded_children == nullptr - ) { - return; - } - - this->scrollable_area = ekg::rect_t {}; - this->acceleration.x = ekg::draw::get_font_renderer(ekg::font::normal).get_text_height(); - - float place {}; - for (ekg::properties_t *&p_properties : *this->descriptor.p_binded_children) { - if ( - p_properties == nullptr - || - p_properties->p_widget == nullptr - || - p_properties->type == ekg::type::scrollbar - ) { - continue; - } - - ekg::ui::abstract *p_widget { - static_cast(p_properties->p_widget) - }; - - place = p_widget->p_descriptor_rect->x + p_widget->p_descriptor_rect->w; - if (place > this->scrollable_area.w) { - this->scrollable_area.w = place; - } - - place = p_widget->p_descriptor_rect->y + p_widget->p_descriptor_rect->h; - if (place > this->scrollable_area.h) { - this->scrollable_area.h = place; - } - - if (p_widget->p_descriptor_rect->h < this->acceleration.y) { - this->acceleration.y = p_widget->p_descriptor_rect->h; - } - - p_widget->p_scroll_vec = &this->scroll; - } - - this->acceleration.y += this->acceleration.x + (this->acceleration.x / 2.0f); - - this->check_scroll(); - this->clamp_scroll(); -} - -void ekg::ui::scrollbar::on_event(ekg::io::stage stage) { - if (this->descriptor.p_binded_rect == nullptr) { - return; - } - - switch (stage) { - case ekg::io::stage::pre: { - ekg::input_t &input {ekg::p_core->service_input.input}; - ekg::vec2_t interact {input.interact}; - - if ( - input.was_pressed - || - input.was_released - || - input.has_motion - || - input.was_wheel - ) { - ekg::rect_t bar_horizontal {this->bar_horizontal}; - bar_horizontal.x += this->descriptor.p_binded_rect->x; - - ekg::rect_t bar_vertical {this->bar_vertical}; - bar_vertical.y += this->descriptor.p_binded_rect->y; - - bool visible { - ekg::rect_collide_vec2(this->scissor, interact) - }; - - this->states.is_active = ( - visible - && - (this->states.is_scrolling.z || this->states.is_scrolling.w) - && - ekg::fire("scrollbar-scroll") - ); - - this->bar_horizontal_states.is_hovering = ( - visible - && - ekg::rect_collide_vec2(bar_horizontal, interact) - ); - - this->bar_vertical_states.is_hovering = ( - visible - && - ekg::rect_collide_vec2(bar_vertical, interact) - ); - - this->states.is_hovering = ( - this->states.is_active - || - this->bar_horizontal_states.is_hovering - || - this->bar_vertical_states.is_hovering - ); - } - - this->states.is_absolute = this->states.is_scrolling.x || this->states.is_scrolling.y || this->states.is_active; - break; - } - - case ekg::io::stage::process: { - this->check_scroll(); - - ekg::input_t &input {ekg::p_core->service_input.input}; - bool is_scroll_fired {ekg::fire("scrollbar-scroll")}; - - this->states.is_scrolling.x = false; - this->states.is_scrolling.y = false; - - #if defined(ANROID) - if (this->states.is_hovering && this->states.is_scrolling.z &is_scroll_fired) { - this->states.is_scrolling.x = true; - - this->scroll.z = ekg::clamp( - this->scroll.x + (-input.interact.z * this->acceleration.y), - this->descriptor.p_binded_rect->w - this->scrollable_area.w, - 0.0f - ); - } - - if (this->states.is_hovering && this->states.is_scrolling.w && is_scroll_fired) { - this->states.is_scrolling.y = true; - this->scroll.w = ekg::clamp( - this->scroll.y + (input.interact.w * this->acceleration.y), - this->descriptor.p_binded_rect->h - this->scrollable_area.h, - 0.0f - ); - } - #else - bool is_scroll_horizontal_fired { - ekg::fire("scrollbar-scroll-horizontal") - }; - - if (this->states.is_hovering && this->states.is_scrolling.z && is_scroll_fired && is_scroll_horizontal_fired) { - this->states.is_scrolling.x = true; - this->scroll.z = ekg::clamp( - this->scroll.x + (input.interact.w * this->acceleration.x), - this->descriptor.p_binded_rect->w - this->scrollable_area.w, - 0.0f - ); - } - - if (this->states.is_hovering && this->states.is_scrolling.w && is_scroll_fired && !is_scroll_horizontal_fired) { - this->states.is_scrolling.y = true; - this->scroll.w = ekg::clamp( - this->scroll.y + (input.interact.w * this->acceleration.y), - this->descriptor.p_binded_rect->h - this->scrollable_area.h, - 0.0f - ); - } - #endif - - if (input.has_motion) { - ekg::io::set( - this->bar_horizontal_states.is_highlighting, - this->bar_horizontal_states.is_hovering - ); - - ekg::io::set( - this->bar_vertical_states.is_highlighting, - this->bar_vertical_states.is_hovering - ); - } - - if ( - this->states.is_hovering - && - input.was_pressed - && - ekg::fire("scrollbar-drag") - ) { - ekg::rect_t bar_horizontal {this->bar_horizontal}; - bar_horizontal.x += this->descriptor.p_binded_rect->x; - this->delta.x = input.interact.x - bar_vertical.x; - - ekg::io::set( - this->bar_horizontal_states.is_active, - this->bar_horizontal_states.is_hovering - ); - - ekg::rect_t bar_vertical {this->bar_vertical}; - bar_vertical.y += this->descriptor.p_binded_rect->y; - this->delta.y = input.interact.y - bar_vertical.y; - - ekg::io::set( - this->bar_vertical_states.is_active, - this->bar_vertical_states.is_hovering - ); - } - - this->bar_horizontal_states.is_scrolling.x = ( - input.has_motion - && - this->bar_horizontal_states.is_active - && - !is_scroll_fired - ); - - if (this->bar_horizontal_states.is_scrolling.x) { - this->states.is_scrolling.x = true; - ekg::rect_t bar_horizontal {this->bar_horizontal}; - bar_horizontal.x = input.interact.x - this->delta.x; - bar_horizontal.x -= this->descriptor.p_binded_rect->x; - - this->scroll.z = ( - ( - -ekg::clamp( - bar_horizontal.x / (this->descriptor.p_binded_rect->w - this->bar_horizontal.w), - 0.0f, - 1.0f - ) - ) - * - ( - this->scrollable_area.w - this->descriptor.p_binded_rect->w - ) - ); - - this->scroll.x = this->scroll.z; - ekg::viewport.redraw = true; - } - - this->bar_vertical_states.is_scrolling.y = ( - input.has_motion - && - this->bar_vertical_states.is_active - && - !is_scroll_fired - ); - - if (this->bar_vertical_states.is_scrolling.y) { - this->states.is_scrolling.y = true; - ekg::rect_t bar_vertical {this->bar_horizontal}; - bar_vertical.y = input.interact.y - this->delta.y; - bar_vertical.y -= this->descriptor.p_binded_rect->y; - - this->scroll.w = ( - ( - -ekg::clamp( - bar_vertical.y / (this->descriptor.p_binded_rect->h - this->bar_vertical.h), - 0.0f, - 1.0f - ) - ) - * - ( - this->scrollable_area.h - this->descriptor.p_binded_rect->h - ) - ); - - this->scroll.y = this->scroll.w; - } - - if (input.was_released) { - ekg::io::trigger( - ( - input.was_pressed - && - ( - this->bar_horizontal_states.is_active - || - this->bar_vertical_states.is_active - ) - ), - ekg::action::release, - this->descriptor.actions, - &this->properties - ); - - ekg::io::set( - this->bar_horizontal_states.is_active, - (this->states.is_scrolling.x = false) - ); - - ekg::io::set( - this->bar_vertical_states.is_active, - (this->states.is_scrolling.y = false) - ); - } - - if (this->properties.p_parent && this->properties.p_parent->p_widget) { - ekg::ui::abstract *p_parent_widget { - static_cast(this->properties.p_parent->p_widget) - }; - - p_parent_widget->states.is_scrolling.z = this->states.is_scrolling.z; - p_parent_widget->states.is_scrolling.w = this->states.is_scrolling.w; - p_parent_widget->states.nearest_scroll_bar_thickness = this->descriptor.theme.pixel_thickness; - } - - if ((this->states.is_active || this->states.is_absolute) && !this->states.is_high_frequency) { - ekg::p_core->dispatch_widget_op(this, ekg::io::operation::high_frequency); - } - - if (this->bar_horizontal_states.is_hovering || this->bar_vertical_states.is_hovering) { - ekg::p_core->p_os_platform->system_cursor = ekg::system_cursor_type::arrow; - } - - if (this->states.is_scrolling.x || this->states.is_scrolling.y) { - ekg::viewport.redraw = true; - } - - ekg::io::trigger( - ( - input.has_motion - && - ( - this->bar_horizontal_states.is_scrolling.x - || - this->bar_vertical_states.is_scrolling.y - ) - && - (ekg::timing_t::second > ekg::tweaks.task_latency) - ), - ekg::action::drag, - this->descriptor.actions, - &this->properties - ); - - ekg::io::trigger( - ( - input.has_motion - && - ( - this->bar_horizontal_states.is_hovering - || - this->bar_vertical_states.is_hovering - ) - && - (ekg::timing_t::second > ekg::tweaks.task_latency) - ), - ekg::action::hover, - this->descriptor.actions, - &this->properties - ); - - ekg::io::trigger( - ( - input.was_pressed - && - ( - this->bar_horizontal_states.is_active - || - this->bar_vertical_states.is_active - ) - ), - ekg::action::press, - this->descriptor.actions, - &this->properties - ); - - break; - } - - case ekg::io::stage::post: { - this->states.is_hovering = false; - this->bar_horizontal_states.is_hovering = false; - this->bar_vertical_states.is_hovering = false; - break; - } - } -} - -void ekg::ui::scrollbar::on_update() { - float speed { - ekg::p_core->service_input.input.scroll_speed - }; - - this->scroll.x = ekg::lerp( - this->scroll.x, - this->scroll.z, - ekg::viewport.dt * this->acceleration.x * speed - ); - - this->scroll.y = ekg::lerp( - this->scroll.y, - this->scroll.w, - ekg::viewport.dt * this->acceleration.y * speed - ); - - #if defined(__ANDROID__) - #endif - this->clamp_scroll(); - - if ( - ( - this->states.is_high_frequency = ( - this->is_scrolling( - this->states.is_hovering || this->states.is_active - ) - ) - ) - ) { - ekg::viewport.redraw = true; - } -} - -void ekg::ui::scrollbar::on_draw() { - if (this->descriptor.p_binded_rect == nullptr) { - return; - } - - this->p_descriptor_rect->w = this->descriptor.p_binded_rect->w; - this->p_descriptor_rect->h = this->descriptor.p_binded_rect->h; - - EKG_ASSERT_SCISSOR( - this->scissor, - this->get_abs_rect(), - this->p_parent_scissor_rect - ); - - this->bar_horizontal.w = 0.0f; - this->bar_vertical.h = 0.0f; - - this->states.is_scrolling.z = this->scrollable_area.w > this->descriptor.p_binded_rect->w; - this->states.is_scrolling.w = this->scrollable_area.h > this->descriptor.p_binded_rect->h; - - if (!this->states.is_scrolling.z && !this->states.is_scrolling.w) { - return; - } - - this->bar_vertical.w = static_cast(this->descriptor.theme.pixel_thickness * this->states.is_scrolling.w); - - this->bar_vertical.x = ( - this->descriptor.p_binded_rect->x - + - this->descriptor.p_binded_rect->w - - - this->bar_vertical.w - ); - - float out_of_binded_rect_width {(this->scrollable_area.h - this->descriptor.p_binded_rect->h)}; - float y_pos_factor {abs(this->scroll.y) / out_of_binded_rect_width}; - - this->bar_vertical.h = ( - this->descriptor.p_binded_rect->h - ( - static_cast(out_of_binded_rect_width) < 0 - ? - this->descriptor.p_binded_rect->h - : - ekg::clamp_max( - out_of_binded_rect_width, - this->descriptor.p_binded_rect->h - this->descriptor.theme.min_bar_size - ) - ) - ); - - this->bar_vertical.y = y_pos_factor * (this->descriptor.p_binded_rect->h - this->bar_vertical.h); - - ekg::draw::rect( - this->bar_vertical.x, - this->descriptor.p_binded_rect->y + this->bar_vertical.y, - this->bar_vertical.w, - this->bar_vertical.h, - this->descriptor.theme.background, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::scrollbar_t::vertical][ekg::layer::background] - ); - - if (this->bar_vertical_states.is_highlighting) { - ekg::draw::rect( - this->bar_vertical.x, - this->descriptor.p_binded_rect->y + this->bar_vertical.y, - this->bar_vertical.w, - this->bar_vertical.h, - this->descriptor.theme.highlight, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::scrollbar_t::vertical][ekg::layer::highlight] - ); - } - - if (this->bar_vertical_states.is_active) { - ekg::draw::rect( - this->bar_vertical.x, - this->descriptor.p_binded_rect->y + this->bar_vertical.y, - this->bar_vertical.w, - this->bar_vertical.h, - this->descriptor.theme.active, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::scrollbar_t::vertical][ekg::layer::active] - ); - } - - ekg::draw::rect( - this->bar_vertical.x, - this->descriptor.p_binded_rect->y + this->bar_vertical.y, - this->bar_vertical.w, - this->bar_vertical.h, - this->descriptor.theme.outline, - ekg::draw_mode::outline - ); - - this->bar_horizontal.h = static_cast(this->descriptor.theme.pixel_thickness * this->states.is_scrolling.z); - this->bar_horizontal.y = ( - this->descriptor.p_binded_rect->y + this->descriptor.p_binded_rect->h - this->bar_horizontal.h - ); - - float out_of_mother_width {(this->scrollable_area.w - this->descriptor.p_binded_rect->w)}; - float x_pos_factor {abs(this->scroll.x) / out_of_mother_width}; - - this->bar_horizontal.w = ( - this->descriptor.p_binded_rect->w - - - ( - static_cast(out_of_mother_width) < 0 - ? - this->descriptor.p_binded_rect->w - : - ekg::clamp_max( - out_of_mother_width, - this->descriptor.p_binded_rect->w - this->descriptor.theme.min_bar_size - ) - ) - ); - - this->bar_horizontal.x = x_pos_factor * (this->descriptor.p_binded_rect->w - this->bar_horizontal.w); - - ekg::draw::rect( - this->descriptor.p_binded_rect->x + this->bar_horizontal.x, - this->bar_horizontal.y, - this->bar_horizontal.w, - this->bar_horizontal.h, - this->descriptor.theme.background, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::scrollbar_t::horizontal][ekg::layer::background] - ); - - if (this->bar_horizontal_states.is_highlighting) { - ekg::draw::rect( - this->descriptor.p_binded_rect->x + this->bar_horizontal.x, - this->bar_horizontal.y, - this->bar_horizontal.w, - this->bar_horizontal.h, - this->descriptor.theme.highlight, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::scrollbar_t::horizontal][ekg::layer::highlight] - ); - } - - if (this->bar_horizontal_states.is_active) { - ekg::draw::rect( - this->descriptor.p_binded_rect->x + this->bar_horizontal.x, - this->bar_horizontal.y, - this->bar_horizontal.w, - this->bar_horizontal.h, - this->descriptor.theme.active, - ekg::draw_mode::filled, - this->descriptor.theme.layers[ekg::scrollbar_t::horizontal][ekg::layer::active] - ); - } - - ekg::draw::rect( - this->descriptor.p_binded_rect->x + this->bar_horizontal.x, - this->bar_horizontal.y, - this->bar_horizontal.w, - this->bar_horizontal.h, - this->descriptor.theme.outline, - ekg::draw_mode::outline - ); -} diff --git a/src/ui/slider/ui_slider.cpp b/src/ui/slider/ui_slider.cpp deleted file mode 100644 index 228b4a15..00000000 --- a/src/ui/slider/ui_slider.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/slider/ui_slider_widget.hpp" -#include "ekg/util/gui.hpp" -#include "ekg/ui/slider/ui_slider.hpp" - -ekg::ui::slider *ekg::ui::slider::unsafe_set_number(ekg::number number) { - this->number = number; - return this; -} - -uint64_t ekg::ui::slider::get_range_count() { - return this->range_map.size(); -} - -ekg::number ekg::ui::slider::get_number() { - return this->number; -} - -ekg::ui::slider *ekg::ui::slider::set_axis(ekg::axis new_axis) { - if (this->axis != new_axis) { - this->axis = new_axis; - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - - } - return this; -} - -ekg::axis ekg::ui::slider::get_axis() { - return this->axis; -} - -ekg::ui::slider *ekg::ui::slider::set_text_align(ekg::flags enum_docks) { - if (this->dock_text != enum_docks) { - this->dock_text = enum_docks; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -ekg::flags ekg::ui::slider::get_text_align() { - return this->dock_text; -} - -ekg::ui::slider *ekg::ui::slider::set_width(float w) { - if (this->sync_ui.w != w) { - this->sync_ui.w = w; - - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::dimension)); - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::set_width)); - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -float ekg::ui::slider::get_width() { - return this->rect_widget.w; -} - -ekg::ui::slider *ekg::ui::slider::set_scaled_height(int32_t scaled_height_factor) { - if (this->scaled_height != scaled_height_factor) { - this->scaled_height = scaled_height_factor; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -float ekg::ui::slider::get_height() { - return this->rect_widget.h; -} - -int32_t ekg::ui::slider::get_scaled_height() { - return this->scaled_height; -} - -ekg::ui::slider *ekg::ui::slider::set_place(ekg::flags flags) { - if (this->dock_flags != flags) { - this->dock_flags = flags; - - ekg::reload(this->id); - ekg::synclayout(this->id); - } - - return this; -} - -ekg::ui::slider *ekg::ui::slider::set_font_size(ekg::font font) { - if (this->font_size != font) { - this->font_size = font; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -ekg::font ekg::ui::slider::get_font_size() { - return this->font_size; -} - -ekg::ui::slider *ekg::ui::slider::set_bar_offset(float offset) { - if (this->bar_offset != offset) { - this->bar_offset = offset; - ekg::reload(this->id); - } - - return this; -} - -float ekg::ui::slider::get_bar_offset() { - return this->bar_offset; -} \ No newline at end of file diff --git a/src/ui/slider/ui_slider_widget.cpp b/src/ui/slider/ui_slider_widget.cpp deleted file mode 100644 index a1ac1e0b..00000000 --- a/src/ui/slider/ui_slider_widget.cpp +++ /dev/null @@ -1,763 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/slider/ui_slider_widget.hpp" -#include "ekg/ui/slider/ui_slider.hpp" -#include "ekg/core/runtime.hpp" -#include "ekg/draw/draw.hpp" - -void ekg::ui::slider_widget::on_reload() { - abstract_widget::on_reload(); - - this->get_abs_rect(); - - ekg::ui::slider *p_ui {static_cast(this->p_data)}; - ekg::service::theme_scheme_t &theme_scheme {ekg::current_theme_scheme()}; - - ekg::font base_font_size {p_ui->get_font_size()}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(base_font_size)}; - - ekg::axis axis {p_ui->get_axis()}; - ekg::number number {p_ui->get_number()}; - ekg::flags text_align_flags {p_ui->get_text_align()}; - - bool is_text_enabled {static_cast(ekg_bitwise_contains(text_align_flags, ekg::dock::none))}; - - float base_text_height {f_renderer.get_text_height()}; - float dimension_offset {static_cast((int32_t) (base_text_height / 2.0f))}; - float bar_offset {p_ui->get_bar_offset()}; - - float min_text_width {}; - float max_text_width {}; - - float bar_thickness { - static_cast(theme_scheme.slider_bar_thickness) / 100 - }; - - ekg::layout::mask &mask {ekg::core->mask}; - - switch (axis) { - case ekg::axis::horizontal: { - if (p_ui->is_auto_initial_dimension()) { - this->dimension.w = ekg_min(this->dimension.w, base_text_height); - this->min_size.x = ekg_min(this->min_size.x, base_text_height); - p_ui->set_auto_initial_dimension(false); - } - - this->dimension.h = (base_text_height + dimension_offset) * static_cast(p_ui->get_scaled_height()); - this->min_size.x = ekg_min(this->min_size.x, base_text_height); - - uint64_t range_list_size { - p_ui->get_range_count() - }; - - this->range_list.resize(range_list_size); - - // left top/bottom or right top/bottom or center top/bottom - // docknize with small font height - - // center - // docknize with no small font (normal) height - - // target - // no-docknize, target the drag cursor but with no small font (normal) height - - // target top/bottom - // no-docknize, target the drag cursor with small font height - - mask.preset( - {bar_offset, bar_offset, this->dimension.h}, - axis, - this->dimension.w - ); - - for (uint64_t it {}; it < range_list_size; it++) { - ekg::ui::slider_widget::range &range {this->range_list.at(it)}; - range.rect.h = this->dimension.h * bar_thickness; - - if (!is_text_enabled) { - ekg::ui::slider_widget_get_metrics( - p_ui, - number, - it, - f_renderer, - &min_text_width, - &max_text_width - ); - - if (min_text_width > max_text_width) { - max_text_width = min_text_width; - } - - range.rect_text.w = max_text_width; - range.rect_text.h = base_text_height; - range.font_size = base_font_size; - - range.last_dimension = -1.0f; - } - - if (text_align_flags == ekg::dock::left || text_align_flags == ekg::dock::right || is_text_enabled) { - mask.insert( - {&range.rect_text, text_align_flags} - ); - - mask.insert( - {&range.rect, (is_text_enabled ? ekg::dock::left : text_align_flags) | ekg::dock::fill} - ); - } else if ( - (ekg_bitwise_contains(text_align_flags, ekg::dock::top)) - || - (ekg_bitwise_contains(text_align_flags, ekg::dock::bottom)) - || - (ekg_bitwise_contains(text_align_flags, ekg::dock::center)) - ) { - mask.insert( - { - &range.rect, - (ekg_bitwise_contains(text_align_flags, ekg::dock::right) ? static_cast(ekg::dock::right) : static_cast(ekg::dock::left)) | ekg::dock::fill | ekg::dock::bind - } - ); - - mask.insert( - {&range.rect_text, text_align_flags} - ); - } - } - - mask.docknize(); - break; - } - - case ekg::axis::vertical: { - break; - } - } -} - -void ekg::ui::slider_widget::on_pre_event(ekg::os::io_event_serial &io_event_serial) { - abstract_widget::on_pre_event(io_event_serial); - - this->flag.state = ( - this->flag.hovered && ekg::input::action("slider-bar-modifier") && - (ekg::input::action("slider-bar-increase") || ekg::input::action("slider-bar-decrease")) - ); - - this->flag.absolute = this->flag.state; -} - -void ekg::ui::slider_widget::on_event(ekg::os::io_event_serial &io_event_serial) { - bool pressed {ekg::input::pressed()}; - - if (!this->flag.activity && pressed && ekg::input::action("slider-drag-activity")) { - ekg::rect &rect {this->get_abs_rect()}; - ekg::vec4 &interact {ekg::input::interact()}; - - for (uint64_t it {}; it < this->range_list.size(); it++) { - ekg::ui::slider_widget::range &range {this->range_list.at(it)}; - if (ekg::rect_collide_vec(range.rect + rect, interact)) { - this->flag.activity = true; - this->targetted_range_index = it; - break; - } - } - } else if (this->flag.activity && ekg::input::released()) { - this->flag.activity = false; - this->targetted_range_index = UINT64_MAX; - } else if (this->flag.activity && this->targetted_range_index != UINT64_MAX && ekg::input::motion()) { - ekg::ui::slider_widget::range &range {this->range_list.at(this->targetted_range_index)}; - ekg::ui::slider *p_ui {static_cast(this->p_data)}; - - ekg::vec4 &interact {ekg::input::interact()}; - ekg::rect &rect {this->get_abs_rect()}; - ekg::rect bar {range.rect + rect}; - - float factor {}; - float dimension {}; - - switch (p_ui->get_axis()) { - case ekg::axis::horizontal: - factor = ekg_clamp(interact.x - bar.x, 0.0f, range.rect.w); - dimension = range.rect.w; - break; - case ekg::axis::vertical: - factor = ekg_clamp(interact.y - bar.y, 0.0f, range.rect.h); - dimension = range.rect.h; - break; - } - - ekg::ui::slider_widget_calculate_value( - p_ui, - p_ui->get_number(), - factor, - dimension, - this->targetted_range_index - ); - - ekg::reload(this); - } -} - -void ekg::ui::slider_widget::on_draw_refresh() { - ekg::rect &rect {this->get_abs_rect()}; - ekg::service::theme_scheme_t &theme_scheme {ekg::current_theme_scheme()}; - ekg::ui::slider *p_ui {static_cast(this->p_data)}; - ekg::number number {p_ui->get_number()}; - ekg::flags text_align_flags {p_ui->get_text_align()}; - bool is_text_enabled {!ekg_bitwise_contains(text_align_flags, ekg::dock::none)}; - - EKG_ASSERT_SCISSOR(this->scissor, rect, this->p_parent_scissor); - ekg_draw_assert_scissor(); - - ekg::draw::rect( - rect, - theme_scheme.slider_bar_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::background) - ); - - switch (p_ui->get_axis()) { - case ekg::axis::horizontal: - for (uint64_t it {}; it < this->range_list.size(); it++) { - ekg::ui::slider_widget::range &range {this->range_list.at(it)}; - - range.target = range.rect; - range.target.w = ekg_clamp( - ekg::ui::slider_widget_calculate_target_pos( - p_ui, - number, - range.rect.w, - it - ), - 0.0f, - range.rect.w - ); - - /** - * May it be a risky to EKG performance, but I believe it is the best - * temp solution for low-latency CPU usage. - **/ - if (is_text_enabled && range.last_dimension != range.target.w) { - range.last_dimension = range.target.w; - ekg::ui::slider_widget_get_value_label( - p_ui, - number, - it, - range.text - ); - } - - ekg::draw::rect( - range.rect + rect, - theme_scheme.slider_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::sub_background) - ); - - ekg::draw::rect( - range.target + rect, - theme_scheme.slider_activity_bar, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::activity) - ); - - ekg::draw::rect( - range.rect + rect, - theme_scheme.slider_bar_outline, - ekg::draw_mode::outline - ); - - ekg::f_renderer(range.font_size).blit( - range.text, - range.rect_text.x + rect.x, - range.rect_text.y + rect.y, - theme_scheme.slider_string - ); - } - break; - case ekg::axis::vertical: - for (uint64_t it {}; it < this->range_list.size(); it++) { - ekg::ui::slider_widget::range &range {this->range_list.at(it)}; - - range.target.h = ekg::ui::slider_widget_calculate_target_pos( - p_ui, - number, - range.rect.h, - it - ); - - ekg::draw::rect( - range.rect + rect, - theme_scheme.slider_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::sub_background) - ); - - ekg::draw::rect( - range.target + rect, - theme_scheme.slider_activity_bar, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::activity) - ); - } - break; - } - - ekg::draw::rect( - rect, - theme_scheme.slider_outline, - ekg::draw_mode::outline - ); -} - -float ekg::ui::slider_widget_calculate_target_pos( - ekg::ui::slider *&p_ui, - ekg::number number, - float dimension, - uint64_t index -) { - switch (number) { - case ekg::number::float64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f64.align_ownership_mem_if_necessary(); - range.f64.set_value(ekg_clamp(range.f64.get_value(), range.f64_min, range.f64_max)); - return (dimension * (range.f64.get_value() - range.f64_min) / (range.f64_max - range.f64_min)); - } - - case ekg::number::float32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f32.align_ownership_mem_if_necessary(); - range.f32.set_value(ekg_clamp(range.f32.get_value(), range.f32_min, range.f32_max)); - return (dimension * (range.f32.get_value() - range.f32_min) / (range.f32_max - range.f32_min)); - } - - case ekg::number::int64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i64.align_ownership_mem_if_necessary(); - range.i64.set_value(ekg_clamp(range.i64.get_value(), range.i64_min, range.i64_max)); - return (dimension * (range.i64.get_value() - range.i64_min) / (range.i64_max - range.i64_min)); - } - - case ekg::number::uint64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u64.align_ownership_mem_if_necessary(); - range.u64.set_value(ekg_clamp(range.u64.get_value(), range.u64_min, range.u64_max)); - return (dimension * (range.u64.get_value() - range.u64_min) / (range.u64_max - range.u64_min)); - } - - case ekg::number::int32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i32.align_ownership_mem_if_necessary(); - range.i32.set_value(ekg_clamp(range.i32.get_value(), range.i32_min, range.i32_max)); - return (dimension * (range.i32.get_value() - range.i32_min) / (range.i32_max - range.i32_min)); - } - - case ekg::number::uint32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u32.align_ownership_mem_if_necessary(); - range.u32.set_value(ekg_clamp(range.u32.get_value(), range.u32_min, range.u32_max)); - return (dimension * (range.u32.get_value() - range.u32_min) / (range.u32_max - range.u32_min)); - } - - case ekg::number::int16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i16.align_ownership_mem_if_necessary(); - range.i16.set_value(ekg_clamp(range.i16.get_value(), range.i16_min, range.i16_max)); - return (dimension * (range.i16.get_value() - range.i16_min) / (range.i16_max - range.i16_min)); - } - - case ekg::number::uint16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u16.align_ownership_mem_if_necessary(); - range.u16.set_value(ekg_clamp(range.u16.get_value(), range.u16_min, range.u16_max)); - return (dimension * (range.u16.get_value() - range.u16_min) / (range.u16_max - range.u16_min)); - } - - case ekg::number::int8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i8.align_ownership_mem_if_necessary(); - range.i8.set_value(ekg_clamp(range.i8.get_value(), range.i8_min, range.i8_max)); - return (dimension * (range.i8.get_value() - range.i8_min) / (range.i8_max - range.i8_min)); - } - - case ekg::number::uint8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u8.align_ownership_mem_if_necessary(); - range.u8.set_value(ekg_clamp(range.u8.get_value(), range.u8_min, range.u8_max)); - return (dimension * (range.u8.get_value() - range.u8_min) / (range.u8_max - range.u8_min)); - } - } - - return 0.0f; -} - -void ekg::ui::slider_widget_calculate_value( - ekg::ui::slider *&p_ui, - ekg::number number, - float factor, - float dimension, - uint64_t index -) { - switch (number) { - case ekg::number::float64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f64.align_ownership_mem_if_necessary(); - range.f64.set_value(ekg_clamp(range.f64.get_value(), range.f64_min, range.f64_max)); - - if (factor == 0) { - range.f64.set_value(range.f64_min); - } else { - range.f64.set_value(static_cast((factor / dimension) * (range.f64_max - range.f64_min) + range.f64_min)); - } - break; - } - - case ekg::number::float32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f32.align_ownership_mem_if_necessary(); - range.f32.set_value(ekg_clamp(range.f32.get_value(), range.f32_min, range.f32_max)); - - if (factor == 0) { - range.f32.set_value(range.f32_min); - } else { - range.f32.set_value(static_cast((factor / dimension) * (range.f32_max - range.f32_min) + range.f32_min)); - } - - break; - } - - case ekg::number::int64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i64.align_ownership_mem_if_necessary(); - range.i64.set_value(ekg_clamp(range.i64.get_value(), range.i64_min, range.i64_max)); - - if (factor == 0) { - range.i64.set_value(range.i64_min); - } else { - range.i64.set_value(static_cast((factor / dimension) * (range.i64_max - range.i64_min) + range.i64_min)); - } - break; - } - - case ekg::number::uint64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u64.align_ownership_mem_if_necessary(); - range.u64.set_value(ekg_clamp(range.u64.get_value(), range.u64_min, range.u64_max)); - - if (factor == 0) { - range.u64.set_value(range.u64_min); - } else { - range.u64.set_value(static_cast((factor / dimension) * (range.u64_max - range.u64_min) + range.u64_min)); - } - break; - } - - case ekg::number::int32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i32.align_ownership_mem_if_necessary(); - range.i32.set_value(ekg_clamp(range.i32.get_value(), range.i32_min, range.i32_max)); - - if (factor == 0) { - range.i32.set_value(range.i32_min); - } else { - range.i32.set_value(static_cast((factor / dimension) * (range.i32_max - range.i32_min) + range.i32_min)); - } - break; - } - - case ekg::number::uint32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u32.align_ownership_mem_if_necessary(); - range.u32.set_value(ekg_clamp(range.u32.get_value(), range.u32_min, range.u32_max)); - - if (factor == 0) { - range.u32.set_value(range.u32_min); - } else { - range.u32.set_value(static_cast((factor / dimension) * (range.u32_max - range.u32_min) + range.u32_min)); - } - break; - } - - case ekg::number::int16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i16.align_ownership_mem_if_necessary(); - range.i16.set_value(ekg_clamp(range.i16.get_value(), range.i16_min, range.i16_max)); - - if (factor == 0) { - range.i16.set_value(range.i16_min); - } else { - range.i16.set_value(static_cast((factor / dimension) * (range.i16_max - range.i16_min) + range.i16_min)); - } - break; - } - - case ekg::number::uint16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u16.align_ownership_mem_if_necessary(); - range.u16.set_value(ekg_clamp(range.u16.get_value(), range.u16_min, range.u16_max)); - - if (factor == 0) { - range.u16.set_value(range.u16_min); - } else { - range.u16.set_value(static_cast((factor / dimension) * (range.u16_max - range.u16_min) + range.u16_min)); - } - break; - } - - case ekg::number::int8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i8.align_ownership_mem_if_necessary(); - range.i8.set_value(ekg_clamp(range.i8.get_value(), range.i8_min, range.i8_max)); - - if (factor == 0) { - range.i8.set_value(range.i8_min); - } else { - range.i8.set_value(static_cast((factor / dimension) * (range.i8_max - range.i8_min) + range.i8_min)); - } - break; - } - - case ekg::number::uint8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u8.align_ownership_mem_if_necessary(); - range.u8.set_value(ekg_clamp(range.u8.get_value(), range.u8_min, range.u8_max)); - - if (factor == 0) { - range.u8.set_value(range.u8_min); - } else { - range.u8.set_value(static_cast((factor / dimension) * (range.u8_max - range.u8_min) + range.u8_min)); - } - break; - } - } -} - -void ekg::ui::slider_widget_get_value_label( - ekg::ui::slider *&p_ui, - ekg::number number, - uint64_t index, - std::string &content -) { - switch (number) { - case ekg::number::float64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f64.align_ownership_mem_if_necessary(); - range.f64.set_value(ekg_clamp(range.f64.get_value(), range.f64_min, range.f64_max)); - content = ekg::string_float64_precision(range.f64.get_value(), range.display_precision); - break; - } - - case ekg::number::float32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f32.align_ownership_mem_if_necessary(); - range.f32.set_value(ekg_clamp(range.f32.get_value(), range.f32_min, range.f32_max)); - content = ekg::string_float_precision(range.f32.get_value(), range.display_precision); - break; - } - - case ekg::number::int64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i64.align_ownership_mem_if_necessary(); - range.i64.set_value(ekg_clamp(range.i64.get_value(), range.i64_min, range.i64_max)); - content = std::to_string(range.i64.get_value()); - break; - } - - case ekg::number::uint64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u64.align_ownership_mem_if_necessary(); - range.u64.set_value(ekg_clamp(range.u64.get_value(), range.u64_min, range.u64_max)); - content = std::to_string(range.u64.get_value()); - break; - } - - case ekg::number::int32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i32.align_ownership_mem_if_necessary(); - range.i32.set_value(ekg_clamp(range.i32.get_value(), range.i32_min, range.i32_max)); - content = std::to_string(range.i32.get_value()); - break; - } - - case ekg::number::uint32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u32.align_ownership_mem_if_necessary(); - range.u32.set_value(ekg_clamp(range.u32.get_value(), range.u32_min, range.u32_max)); - content = std::to_string(range.u32.get_value()); - break; - } - - case ekg::number::int16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i16.align_ownership_mem_if_necessary(); - range.i16.set_value(ekg_clamp(range.i16.get_value(), range.i16_min, range.i16_max)); - content = std::to_string(range.i16.get_value()); - break; - } - - case ekg::number::uint16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u16.align_ownership_mem_if_necessary(); - range.u16.set_value(ekg_clamp(range.u16.get_value(), range.u16_min, range.u16_max)); - content = std::to_string(range.u16.get_value()); - break; - } - - case ekg::number::int8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i8.align_ownership_mem_if_necessary(); - range.i8.set_value(ekg_clamp(range.i8.get_value(), range.i8_min, range.i8_max)); - content = std::to_string(range.i8.get_value()); - break; - } - - case ekg::number::uint8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u8.align_ownership_mem_if_necessary(); - range.u8.set_value(ekg_clamp(range.u8.get_value(), range.u8_min, range.u8_max)); - content = std::to_string(range.u8.get_value()); - break; - } - } -} - -void ekg::ui::slider_widget_get_metrics( - ekg::ui::slider *&p_ui, - ekg::number number, - uint64_t index, - ekg::draw::font_renderer &f_renderer, - float *p_min_text_width, - float *p_max_text_width -) { - switch (number) { - case ekg::number::float64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f64.align_ownership_mem_if_necessary(); - range.f64.set_value(ekg_clamp(range.f64.get_value(), range.f64_min, range.f64_max)); - - *p_min_text_width = f_renderer.get_text_width(ekg::string_float64_precision(range.f64_min, range.display_precision)); - *p_max_text_width = f_renderer.get_text_width(ekg::string_float64_precision(range.f64_min, range.display_precision)); - - break; - } - - case ekg::number::float32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.f32.align_ownership_mem_if_necessary(); - range.f32.set_value(ekg_clamp(range.f32.get_value(), range.f32_min, range.f32_max)); - - *p_min_text_width = f_renderer.get_text_width(ekg::string_float_precision(range.f32_min, range.display_precision)); - *p_max_text_width = f_renderer.get_text_width(ekg::string_float_precision(range.f32_max, range.display_precision)); - - break; - } - - case ekg::number::int64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i64.align_ownership_mem_if_necessary(); - range.i64.set_value(ekg_clamp(range.i64.get_value(), range.i64_min, range.i64_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.i64_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.i64_max)); - - break; - } - - case ekg::number::uint64: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u64.align_ownership_mem_if_necessary(); - range.u64.set_value(ekg_clamp(range.u64.get_value(), range.u64_min, range.u64_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.u64_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.u64_max)); - - break; - } - - case ekg::number::int32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i32.align_ownership_mem_if_necessary(); - range.i32.set_value(ekg_clamp(range.i32.get_value(), range.i32_min, range.i32_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.i32_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.i32_max)); - - break; - } - - case ekg::number::uint32: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u32.align_ownership_mem_if_necessary(); - range.u32.set_value(ekg_clamp(range.u32.get_value(), range.u32_min, range.u32_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.u32_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.u32_max)); - - break; - } - - case ekg::number::int16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i16.align_ownership_mem_if_necessary(); - range.i16.set_value(ekg_clamp(range.i16.get_value(), range.i16_min, range.i16_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.i16_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.i16_max)); - - break; - } - - case ekg::number::uint16: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u16.align_ownership_mem_if_necessary(); - range.u16.set_value(ekg_clamp(range.u16.get_value(), range.u16_min, range.u16_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.u16_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.u16_max)); - - break; - } - - case ekg::number::int8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.i8.align_ownership_mem_if_necessary(); - range.i8.set_value(ekg_clamp(range.i8.get_value(), range.i8_min, range.i8_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.i8_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.i8_max)); - - break; - } - - case ekg::number::uint8: { - ekg::ui::slider::range_t &range {p_ui->range(index)}; - range.u8.align_ownership_mem_if_necessary(); - range.u8.set_value(ekg_clamp(range.u8.get_value(), range.u8_min, range.u8_max)); - - *p_min_text_width = f_renderer.get_text_width(std::to_string(range.u8_min)); - *p_max_text_width = f_renderer.get_text_width(std::to_string(range.u8_max)); - - break; - } - } -} diff --git a/src/os/platform.cpp b/src/ui/stack.cpp similarity index 89% rename from src/os/platform.cpp rename to src/ui/stack.cpp index 84b55c46..38c5aa71 100644 --- a/src/os/platform.cpp +++ b/src/ui/stack.cpp @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com + * Copyright (c) 2022-2025 Rina Wilk / vokegpu@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,5 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "ekg/ui/stack.hpp" -#include "ekg/os/platform.hpp" \ No newline at end of file +ekg::stack_t ekg::stack_t::not_found {}; diff --git a/src/ui/textbox/ui_textbox.cpp b/src/ui/textbox/ui_textbox.cpp deleted file mode 100644 index ba83c800..00000000 --- a/src/ui/textbox/ui_textbox.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/core/runtime.hpp" -#include "ekg/ui/textbox/ui_textbox.hpp" -#include "ekg/ui/textbox/ui_textbox_widget.hpp" - -ekg::ui::textbox *ekg::ui::textbox::set_must_format_text(bool state) { - this->must_format_text = state; - return this; -} - -bool ekg::ui::textbox::is_must_format_text() { - return this->must_format_text; -} - -ekg::ui::textbox *ekg::ui::textbox::set_tab_size(uint8_t size) { - this->tab_size = ekg_min(size, 1); - return this; -} - -uint8_t ekg::ui::textbox::get_tab_size() { - return this->tab_size; -} - -ekg::ui::textbox *ekg::ui::textbox::set_place(ekg::flags flags) { - if (this->dock_flags != flags) { - this->dock_flags = flags; - ekg::synclayout(this->parent_id); - } - - return this; -} - -std::string ekg::ui::textbox::get_text() { - if (this->must_format_text) { - uint64_t text_chunk_size {this->p_value->size()}; - for (uint64_t it {}; it < this->p_value->size(); it++) { - this->formatted_text += this->p_value->at(it); - this->formatted_text += '\n' && (it + 1 == text_chunk_size); - } - - this->must_format_text = false; - } - - return this->formatted_text; -} - -ekg::ui::textbox *ekg::ui::textbox::set_width(float w) { - if (this->sync_ui.w != w) { - this->sync_ui.w = w; - - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::dimension)); - ekg_bitwise_add(this->sync_flags, static_cast(ekg::ui_sync::set_width)); - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -float ekg::ui::textbox::get_width() { - return this->rect_widget.w; -} - -ekg::ui::textbox *ekg::ui::textbox::set_scaled_height(int32_t scaled_factor_height) { - if (this->scaled_height != scaled_factor_height) { - this->scaled_height = scaled_factor_height; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -float ekg::ui::textbox::get_height() { - return this->rect_widget.h; -} - -int32_t ekg::ui::textbox::get_scaled_height() { - return this->scaled_height; -} - -ekg::ui::textbox *ekg::ui::textbox::set_font_size(ekg::font font) { - if (this->font_size != font) { - this->font_size = font; - - ekg::reload(this->id); - ekg::synclayout(this->parent_id); - } - - return this; -} - -ekg::font ekg::ui::textbox::get_font_size() { - return this->font_size; -} - -ekg::ui::textbox *ekg::ui::textbox::set_max_lines(uint64_t lines) { - if (this->max_lines != lines) { - this->max_lines = lines; - ekg::reload(this->id); - } - - return this; -} - -uint64_t ekg::ui::textbox::get_max_lines() { - return this->max_lines; -} - -ekg::ui::textbox *ekg::ui::textbox::set_max_chars_per_line(uint64_t chars_per_line) { - if (this->max_chars_per_line != chars_per_line) { - this->max_chars_per_line = chars_per_line; - ekg::reload(this->id); - } - - return this; -} - -uint64_t ekg::ui::textbox::get_max_chars_per_line() { - return this->max_chars_per_line; -} - -ekg::ui::textbox *ekg::ui::textbox::set_typing_state(ekg::state state) { - this->typing_state = state; - return this; -} - -ekg::state ekg::ui::textbox::get_typing_state() { - return this->typing_state; -} diff --git a/src/ui/textbox/ui_textbox_widget.cpp b/src/ui/textbox/ui_textbox_widget.cpp deleted file mode 100644 index c3fb7008..00000000 --- a/src/ui/textbox/ui_textbox_widget.cpp +++ /dev/null @@ -1,1459 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/ui/textbox/ui_textbox_widget.hpp" -#include "ekg/ui/textbox/ui_textbox.hpp" -#include "ekg/core/runtime.hpp" -#include "ekg/draw/draw.hpp" -#include "ekg/util/geometry.hpp" -#include "ekg/util/text.hpp" - -void ekg::ui::textbox_widget::check_nearest_word(ekg::ui::textbox_widget::cursor &cursor, int64_t &x, int64_t &y) { - if (!this->is_action_modifier_enable) { - return; - } - - ekg::ui::textbox_widget::cursor_pos &target_cursor_pos { - cursor.pos[3] == cursor.pos[0] ? cursor.pos[0] : cursor.pos[1] - }; - - ekg::ui::textbox *p_ui {(ekg::ui::textbox*) this->p_data}; - std::string &cursor_text {ekg_textbox_get_cursor_text(target_cursor_pos)}; - uint64_t cursor_text_size {cursor_text.size()}; - - if (cursor_text.empty() || - (x == 0 || (x < 0 && target_cursor_pos.text_index == 0) || - (x > 0 && target_cursor_pos.text_index == cursor_text_size)) - ) { - return; - } - - bool is_dir_right {x > 0}; - bool should_update_last_char {}; - - uint64_t it {}; - int64_t utf_index {}; - - std::string utf_string {}; - char32_t char32 {}; - uint8_t char8 {}; - - x = 0; - - /** - * the cursor index text position is utf encoded, - * not string coded, so it should start by begin - * and not by cursor. - */ - - while (it < cursor_text_size) { - char8 = static_cast(cursor_text.at(it)); - - if (is_dir_right && utf_index >= target_cursor_pos.text_index) { - if (char8 != 32) { - should_update_last_char = true; - } else if (should_update_last_char) { - x = utf_index - target_cursor_pos.text_index; - break; - } - } else if (!is_dir_right) { - if (utf_index == target_cursor_pos.text_index) { - break; - } - - if (char8 == 32) { - should_update_last_char = true; - } else if (should_update_last_char) { - x = utf_index; - should_update_last_char = false; - } - } - - utf_index++; - it += ekg::utf_check_sequence(char8, char32, utf_string, cursor_text, it) + 1; - } - - /** - * when the loop is finished, sometime it does not check any possible index, - * so it is important to force at latest one. - */ - if (is_dir_right && x == 0) { - x = utf_index - target_cursor_pos.text_index; - x = x == 0 ? 1 : x; - } else if (!is_dir_right) { - x = x - utf_index; - } -} - -/** - * If the cursor is not selected, then target the first cursor, - * therefore when the cursor is selected, the code check if moving cursor is - * enabled, then set the A & B pos based on direction. - */ -void ekg::ui::textbox_widget::move_target_cursor(ekg::ui::textbox_widget::cursor &cursor, int64_t x, int64_t y) { - ekg::reset(ekg::core->ui_timing); - ekg::dispatch(ekg::env::redraw); - - ekg::ui::textbox_widget::cursor_pos target_cursor_pos {}; - if ((cursor.pos[0] == cursor.pos[3] && this->is_action_select_enable) || - ((x < 0 || y < 0) && cursor.pos[0] != cursor.pos[1] && !this->is_action_select_enable)) { - target_cursor_pos = cursor.pos[0]; - } else { - target_cursor_pos = cursor.pos[1]; - } - - if (cursor.pos[0] == cursor.pos[1] || this->is_action_select_enable) { - this->move_cursor(target_cursor_pos, x, y); - } - - if (!this->is_action_select_enable) { - cursor.pos[0] = target_cursor_pos; - cursor.pos[1] = target_cursor_pos; - cursor.pos[2] = target_cursor_pos; - cursor.pos[3] = target_cursor_pos; - return; - } - - cursor.pos[3] = target_cursor_pos; - - if (cursor.pos[3] >= cursor.pos[2]) { - cursor.pos[0] = cursor.pos[2]; - cursor.pos[1] = target_cursor_pos; - } else { - cursor.pos[0] = target_cursor_pos; - cursor.pos[1] = cursor.pos[2]; - } -} - -/** - * This method is not called all the time, the part of batching rects for rendering after, - * works okay, but I will write a fast select rect batching. - */ -void ekg::ui::textbox_widget::update_ui_text_data() { - this->get_abs_rect(); - - ekg::ui::textbox *p_ui {(ekg::ui::textbox*) this->p_data}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - - this->rect_text.w = 0.0f; - this->total_utf_chars = 0; - std::string formated_text {}; - - uint64_t text_chunk_size {p_ui->p_value->size()}; - - this->rect_cursor.w = 2.0f; - this->rect_cursor.h = this->text_height; - this->is_ui_enabled = p_ui->get_typing_state() == ekg::state::enable; - - for (int64_t chunk_index {}; chunk_index < text_chunk_size; chunk_index++) { - this->rect_text.w = ekg_min(this->rect_text.w, f_renderer.get_text_width(p_ui->p_value->at(chunk_index))); - } - - this->embedded_scroll.acceleration.x = (this->text_height * 3.0f) + (this->text_offset * 2.0f); - this->embedded_scroll.acceleration.y = this->embedded_scroll.acceleration.x; - this->rect_text.w += this->text_offset * 2.0f; - p_ui->set_must_format_text(true); -} - -void ekg::ui::textbox_widget::move_cursor(ekg::ui::textbox_widget::cursor_pos &cursor, int64_t x, int64_t y) { - ekg::ui::textbox* p_ui {static_cast(this->p_data)} ; - std::string &cursor_text {ekg_textbox_get_cursor_text(cursor)}; - int64_t cursor_text_size {static_cast(ekg::utf_length(cursor_text))}; - - bool chunk_bounding_size_index {cursor.chunk_index + 1 == p_ui->p_value->size()}; - bool check_cursor_x {x != 0}; - - if (y < 0) { - if (cursor.chunk_index == 0) { - cursor.text_index = 0; - } else { - cursor.chunk_index--; - cursor_text_size = static_cast(ekg::utf_length(ekg_textbox_get_cursor_text(cursor))); - - cursor.text_index = cursor.last_text_index; - cursor.text_index = ekg_max(cursor.text_index, static_cast(cursor_text_size)); - } - } else if (y > 0) { - if (chunk_bounding_size_index) { - cursor.text_index = cursor_text_size; - } else { - cursor.chunk_index++; - - cursor_text_size = static_cast(ekg::utf_length(ekg_textbox_get_cursor_text(cursor))); - cursor.text_index = cursor.last_text_index; - cursor.text_index = ekg_max(cursor.text_index, static_cast(cursor_text_size)); - } - } - - cursor.text_index += x; - - if (cursor.text_index < 0 && cursor.chunk_index > 0 && check_cursor_x) { - cursor.chunk_index--; - y = -1; - cursor.text_index = static_cast(ekg::utf_length(ekg_textbox_get_cursor_text(cursor))); - cursor_text_size = cursor.text_index; - } - - if (cursor.text_index > cursor_text_size && chunk_bounding_size_index && check_cursor_x) { - cursor.text_index = cursor_text_size; - y = -1; - } else if (cursor.text_index > cursor_text_size && check_cursor_x) { - cursor.chunk_index++; - cursor.text_index = 0; - y = 1; - } - - cursor.text_index = ekg_min(cursor.text_index, static_cast(0)); - cursor.chunk_index = ekg_max(cursor.chunk_index, static_cast(p_ui->p_value->size())); - - if (check_cursor_x) { - cursor.last_text_index = cursor.text_index; - } - - std::string ¤t_cursor_text {ekg_textbox_get_cursor_text(cursor)}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - ekg::rect &rect {this->get_abs_rect()}; - - const ekg::vec2 cursor_pos { - ( - rect.x + this->embedded_scroll.scroll.x + - f_renderer.get_text_width(ekg::utf_substr(current_cursor_text, 0, cursor.text_index)) - ), - ( - rect.y + this->embedded_scroll.scroll.y + this->text_offset + - (static_cast(cursor.chunk_index) * this->text_height) - ) - }; - - const ekg::vec4 cursor_outspace_screen { - rect.x - cursor_pos.x, - rect.y - cursor_pos.y, - (cursor_pos.x + this->cursor_char_wsize[1] + this->cursor_char_wsize[2]) - - (rect.x + rect.w - this->rect_cursor.w), - (cursor_pos.y + this->rect_cursor.h) - (rect.y + rect.h - this->text_offset) - }; - - if (cursor_outspace_screen.x > 0.0f && - (x < 0 || cursor_outspace_screen.x > this->cursor_char_wsize[0] + this->rect_cursor.w)) { - this->embedded_scroll.scroll.z += cursor_outspace_screen.x; - } else if ( - ( - (cursor_outspace_screen.z > 0.0f) && - (x > 0 || cursor_outspace_screen.z > this->cursor_char_wsize[1] + this->cursor_char_wsize[2] + this->rect_cursor.w) - )) { - this->embedded_scroll.scroll.z -= cursor_outspace_screen.z; - } - - /** - * Instead of targeting the scroll using the render cursor pos, this use the current cursor chunk it. - */ - - if (cursor_outspace_screen.y > 0.0f && - (y < 0 || cursor_outspace_screen.y > this->rect_cursor.h + this->text_offset)) { - this->embedded_scroll.scroll.w += cursor_outspace_screen.y; - } else if (cursor_outspace_screen.w > 0.0f && - (y > 0 || cursor_outspace_screen.w > this->rect_cursor.h + this->rect_cursor.h + this->text_offset)) { - this->embedded_scroll.scroll.w -= cursor_outspace_screen.w; - } - - ekg::reset(ekg::core->ui_timing); - ekg::dispatch(ekg::env::redraw); -} - -void ekg::ui::textbox_widget::process_text( - ekg::ui::textbox_widget::cursor &cursor, - std::string_view text, - ekg::ui::textbox_widget::action action, - int64_t direction -) { - ekg::ui::textbox *p_ui {(ekg::ui::textbox*) this->p_data}; - if (!(this->is_ui_enabled = p_ui->get_typing_state() == ekg::state::enable) && !(text == "clipboard" && this->is_clipboard_copy)) { - return; - } - - this->text_edited = true; - - std::string &cursor_text_a {ekg_textbox_get_cursor_text(cursor.pos[0])}; - std::string &cursor_text_b {ekg_textbox_get_cursor_text(cursor.pos[1])}; - - uint64_t ui_max_chars_per_line {p_ui->get_max_chars_per_line()}; - uint64_t ui_max_lines {p_ui->get_max_lines()}; - uint64_t previous_text_chunk_size {p_ui->p_value->size()}; - - bool max_size_reached[2] { - cursor_text_a.size() == ui_max_chars_per_line, - cursor_text_b.size() == ui_max_chars_per_line - }; - - switch (action) { - case ekg::ui::textbox_widget::action::add_text: - if ((this->is_action_modifier_enable && - !(this->is_clipboard_cut || this->is_clipboard_copy || this->is_clipboard_paste))) { - break; - } - - /** - * Always the tab is pressed, the process should add the spaces instead of \t, - * it is cached to prevent useless iteration. - */ - if (text == "\t") { - uint8_t ui_tab_size {p_ui->get_tab_size()}; - - if (this->cached_tab_size.size() != ui_tab_size) { - this->cached_tab_size.clear(); - for (uint8_t it {}; it < ui_tab_size; it++) { - this->cached_tab_size += ' '; - } - } - - text = this->cached_tab_size; - direction = static_cast(ui_tab_size); - } else if (text == "clipboard") { - text = ""; - - if (this->is_clipboard_cut || this->is_clipboard_copy) { - if (cursor.pos[0] != cursor.pos[1]) { - std::string copy_text {}; - if (cursor.pos[0].chunk_index == cursor.pos[1].chunk_index) { - /** - * For some reason the STL substr implementation actually - * do the following addition for B (second parameter), - * (B += A), the subtract result between cursor 1 and 0 - * should temp fix this. - */ - copy_text += ekg::utf_substr( - cursor_text_a, cursor.pos[0].text_index, - cursor.pos[1].text_index - cursor.pos[0].text_index - ); - } else { - copy_text += ekg::utf_substr(cursor_text_a, cursor.pos[0].text_index, ekg::utf_length(cursor_text_a)); - copy_text += '\n'; - - uint64_t it {static_cast(cursor.pos[0].chunk_index + 1)}; - uint64_t size {p_ui->p_value->size()}; - - bool not_last_line {}; - bool last_line {}; - - while (it < cursor.pos[1].chunk_index && it < size) { - copy_text += p_ui->p_value->at(it); - - last_line = it + 1 == size; - not_last_line = !last_line; - - if (not_last_line || (p_ui->p_value->at(it).empty() && last_line)) { - copy_text += '\n'; - } - - it++; - } - - copy_text += ekg::utf_substr(cursor_text_b, 0, cursor.pos[1].text_index); - } - - ekg::core->p_os_platform->set_clipboard_text(copy_text.c_str()); - } - - if (this->is_clipboard_copy) { - break; - } - } - } - - if (max_size_reached[0]) { - break; - } - - cursor_text_a = ( - ekg::utf_substr(cursor_text_a, 0, cursor.pos[0].text_index) + - text.data() + - ekg::utf_substr(cursor_text_b, cursor.pos[1].text_index, ekg::utf_length(cursor_text_b)) - ); - - ekg_textbox_clamp_line(cursor_text_a, ui_max_chars_per_line); - - if (cursor.pos[0].chunk_index != cursor.pos[1].chunk_index) { - p_ui->p_value->erase( - p_ui->p_value->begin() + cursor.pos[0].chunk_index + 1, - p_ui->p_value->begin() + cursor.pos[1].chunk_index + 1 - ); - } - - if (this->is_clipboard_paste && ekg::core->p_os_platform->has_clipboard_text() && !(text = ekg::core->p_os_platform->get_clipboard_text()).empty()) { - direction = static_cast(ekg::utf_length(text)); - - std::vector utf_clipboard_decoded {}; - ekg::utf_decode(text, utf_clipboard_decoded); - - uint64_t sum_decoded_size {p_ui->p_value->size() + utf_clipboard_decoded.size() - 1}; - if (sum_decoded_size > ui_max_lines) { - uint64_t subtract_decoded_size {sum_decoded_size - ui_max_lines}; - utf_clipboard_decoded.erase( - utf_clipboard_decoded.begin() + static_cast(subtract_decoded_size), - utf_clipboard_decoded.end() - ); - } - - if (utf_clipboard_decoded.size() == 1) { - cursor_text_a = ( - ekg::utf_substr(cursor_text_a, 0, cursor.pos[0].text_index) + - utf_clipboard_decoded.at(0) + - ekg::utf_substr(cursor_text_a, cursor.pos[0].text_index, ekg::utf_length(cursor_text_a)) - ); - } else if (utf_clipboard_decoded.size() > 1) { - int64_t last_clipboard_list_index {static_cast(utf_clipboard_decoded.size() - 1)}; - std::string &last_clipboard_line {utf_clipboard_decoded.at(last_clipboard_list_index)}; - - cursor.pos[1].text_index = static_cast(ekg::utf_length(last_clipboard_line)); - cursor.pos[1].chunk_index = cursor.pos[0].chunk_index + last_clipboard_list_index; - cursor.pos[1].select_index = cursor.pos[1].text_index; - - std::string stored_text = ekg::utf_substr( - cursor_text_a, cursor.pos[0].text_index, - ekg::utf_length(cursor_text_a) - ); - - cursor_text_a = ( - ekg::utf_substr(cursor_text_a, 0, cursor.pos[0].text_index) + utf_clipboard_decoded.at(0) - ); - - ekg_textbox_clamp_line(cursor_text_a, ui_max_chars_per_line); - - last_clipboard_line = last_clipboard_line + stored_text; - p_ui->p_value->insert( - p_ui->p_value->begin() + cursor.pos[0].chunk_index + 1, - utf_clipboard_decoded.begin() + 1, - utf_clipboard_decoded.end() - ); - - cursor.pos[0] = cursor.pos[1]; - direction = 0; - } - } - - ekg_textbox_clamp_text_chunk_size(p_ui->p_value, ui_max_lines); - this->move_cursor(cursor.pos[0], direction, 0); - - break; - - case ekg::ui::textbox_widget::action::erase_text: - if (this->is_action_modifier_enable && cursor.pos[0] == cursor.pos[1] && - (cursor.pos[0].text_index > 0 || direction > 0)) { - int64_t cursor_dir[2] {direction, 0}; - - if (this->is_action_select_enable) { - cursor_dir[0] = ( - direction < 0 ? (-cursor.pos[0].text_index) : (static_cast(ekg::utf_length(cursor_text_a)) - cursor.pos[0].text_index) - ); - } else { - this->check_nearest_word(cursor, cursor_dir[0], cursor_dir[1]); - } - - this->move_cursor(direction < 0 ? cursor.pos[0] : cursor.pos[1], cursor_dir[0], cursor_dir[1]); - } - - if (cursor.pos[0] == cursor.pos[1] && direction < 0 && - (cursor.pos[0].text_index > 0 || cursor.pos[0].chunk_index > 0) - ) { - if (cursor.pos[0].text_index - 1 < 0 && cursor.pos[0].chunk_index > 0) { - std::string stored_text {cursor_text_a}; - - this->move_cursor(cursor.pos[0], -1, 0); - p_ui->p_value->erase(p_ui->p_value->begin() + cursor.pos[0].chunk_index + 1); - - std::string &upper_line_text {ekg_textbox_get_cursor_text(cursor.pos[0])}; - upper_line_text += stored_text; - - ekg_textbox_clamp_line(upper_line_text, ui_max_chars_per_line); - } else { - int64_t it {ekg_min(cursor.pos[0].text_index - 1, static_cast(0))}; - cursor_text_a = ( - ekg::utf_substr(cursor_text_a, 0, it) + - ekg::utf_substr(cursor_text_a, it + 1, ekg::utf_length(cursor_text_a)) - ); - - ekg_textbox_clamp_line(cursor_text_a, ui_max_chars_per_line); - this->move_cursor(cursor.pos[0], -1, 0); - } - } else if (cursor.pos[0] != cursor.pos[1] && direction != 0) { - cursor_text_a = ekg::utf_substr(cursor_text_a, 0, cursor.pos[0].text_index) + - ekg::utf_substr(cursor_text_b, cursor.pos[1].text_index, ekg::utf_length(cursor_text_b)); - - if (cursor.pos[0].chunk_index != cursor.pos[1].chunk_index) { - p_ui->p_value->erase( - p_ui->p_value->begin() + cursor.pos[0].chunk_index + 1, - p_ui->p_value->begin() + cursor.pos[1].chunk_index + 1 - ); - } - - ekg_textbox_clamp_line(cursor_text_a, ui_max_chars_per_line); - } else if (cursor.pos[0] == cursor.pos[1] && direction > 0) { - int64_t cursor_text_size {static_cast(ekg::utf_length(cursor_text_a))}; - bool chunk_bounding_size_index {cursor.pos[0].chunk_index + 1 == p_ui->p_value->size()}; - - if (cursor.pos[0].text_index >= cursor_text_size && !chunk_bounding_size_index) { - cursor_text_a += p_ui->p_value->at(cursor.pos[0].chunk_index + 1); - p_ui->p_value->erase(p_ui->p_value->begin() + cursor.pos[0].chunk_index + 1); - } else if (cursor.pos[0].text_index < cursor_text_size) { - int64_t it {cursor.pos[0].text_index}; - cursor_text_a = ekg::utf_substr(cursor_text_a, 0, it) + - ekg::utf_substr(cursor_text_a, it + 1, ekg::utf_length(cursor_text_a)); - } - - ekg_textbox_clamp_line(cursor_text_a, ui_max_chars_per_line); - } - - break; - - case ekg::ui::textbox_widget::action::break_line: - if (p_ui->p_value->size() >= ui_max_lines) { - break; - } - - int64_t cursor_dir[2] {0, 1}; - std::string line {}; - - if (!this->is_action_modifier_enable) { - line = ekg::utf_substr(cursor_text_a, cursor.pos[0].text_index, ekg::utf_length(cursor_text_a)); - cursor_text_a = ekg::utf_substr(cursor_text_a, 0, cursor.pos[0].text_index); - - cursor_dir[0] = 1; - cursor_dir[1] = 0; - } - - p_ui->p_value->insert(p_ui->p_value->begin() + cursor.pos[0].chunk_index + 1, line); - - ekg_textbox_clamp_text_chunk_size(p_ui->p_value, ui_max_lines); - this->move_cursor(cursor.pos[0], cursor_dir[0], cursor_dir[1]); - - ekg::reset(ekg::core->ui_timing); - ekg::dispatch(ekg::env::redraw); - - break; - } - - if (!this->is_clipboard_copy) { - cursor.pos[2] = cursor.pos[1] = cursor.pos[0]; - cursor.pos[3] = cursor.pos[0]; - } - - if (previous_text_chunk_size != p_ui->p_value->size()) { - this->rect_text.h = (this->text_height * static_cast(p_ui->p_value->size())); - this->embedded_scroll.rect_child.h = this->rect_text.h; - this->embedded_scroll.clamp_scroll(); - this->update_ui_text = true; - } - - ekg::reset(ekg::core->ui_timing); - ekg::dispatch(ekg::env::redraw); -} - -void ekg::ui::textbox_widget::check_cursor_text_bounding( - ekg::ui::textbox_widget::cursor &cursor, - bool reset_second_cursor_pos -) { - if (!this->flag.focused) { - return; - } - - ekg::ui::textbox *p_ui {(ekg::ui::textbox*) this->p_data}; - ekg::rect &rect {this->get_abs_rect()}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - - float x {}; - float y {rect.y + this->embedded_scroll.scroll.y + this->text_offset}; - - ekg::rect char_rect {}; - ekg::vec4 interact {ekg::input::interact()}; - - interact.x = ekg_clamp(interact.x, rect.x + this->rect_cursor.w, rect.x + rect.w - this->rect_cursor.w); - interact.y = ekg_clamp(interact.y, rect.y + this->text_offset * 2.0f, rect.y + rect.h - this->text_offset * 2.0f); - - uint64_t text_chunk_size {p_ui->p_value->size()}; - bool checked {}; - - char32_t char32 {}; - uint8_t char8 {}; - - uint64_t utf_char_index {}; - std::string utf_string {}; - bool is_index_chunk_at_end {}; - - uint64_t text_index {}; - uint64_t chunk_index {}; - uint64_t it {}; - - FT_Face ft_face {}; - FT_UInt ft_uint_previous {}; - FT_Vector_ ft_vector_previous_char {}; - - y += (this->text_height * static_cast(this->visible_text[1])); - for (chunk_index = this->visible_text[1]; chunk_index < text_chunk_size; chunk_index++) { - std::string &text {p_ui->p_value->at(chunk_index)}; - - x = rect.x + this->rect_cursor.w + this->embedded_scroll.scroll.x; - utf_char_index = 0; - ft_uint_previous = 0; - is_index_chunk_at_end = chunk_index == text_chunk_size - 1; - - if (y > rect.y + rect.h) { - break; - } - - for (it = 0; it < text.size(); it++) { - char8 = static_cast(text.at(it)); - it += ekg::utf_check_sequence(char8, char32, utf_string, text, it); - - if (f_renderer.ft_bool_kerning && ft_uint_previous) { - FT_Get_Kerning(ft_face, ft_uint_previous, char32, 0, &ft_vector_previous_char); - x += static_cast(ft_vector_previous_char.x >> 6); - } - - char_rect.x = x; - char_rect.y = y; - char_rect.w = (f_renderer.mapped_glyph_char_data[char32].wsize) / 2; - char_rect.h = this->text_height + 1.0f; - - if (ekg::rect_collide_vec_precisely(char_rect, interact)) { - text_index = utf_char_index; - checked = true; - break; - } - - char_rect.w += char_rect.w; - if (ekg::rect_collide_vec_precisely(char_rect, interact)) { - text_index = utf_char_index + 1; - checked = true; - break; - } - - utf_char_index++; - ft_uint_previous = char32; - x += char_rect.w; - } - - /** - * If interact position is not colliding with any char in this line, then set index to the end or begin of line. - */ - char_rect.x = rect.x; - char_rect.y = y; - char_rect.w = rect.w; - char_rect.h = this->text_height + 1.0f + (rect.h * static_cast(is_index_chunk_at_end)); - - if (ekg::rect_collide_vec(char_rect, interact) && (!checked || text.empty())) { - char_rect.w = this->text_offset; - if (ekg::rect_collide_vec(char_rect, interact)) { - text_index = 0; - } else { - text_index = utf_char_index; - } - - checked = true; - break; - } - - if (checked) break; - y += this->text_height; - } - - if (reset_second_cursor_pos) { - if (checked) { - cursor.pos[0].chunk_index = static_cast(chunk_index); - cursor.pos[0].text_index = static_cast(text_index); - cursor.pos[0].last_text_index = static_cast(text_index); - } else { - cursor.pos[0].chunk_index = static_cast(chunk_index - (!p_ui->p_value->empty())); - cursor.pos[0].text_index = static_cast(utf_char_index); - cursor.pos[0].last_text_index = static_cast(text_index); - } - - cursor.pos[1] = cursor.pos[0]; - cursor.pos[2] = cursor.pos[0]; - cursor.pos[3] = cursor.pos[0]; - } else { - if (checked) { - it = ( - ((text_index < cursor.pos[2].text_index && chunk_index == cursor.pos[2].chunk_index) || (chunk_index < cursor.pos[2].chunk_index)) - ? 0 : 1 - ); - - cursor.pos[it].chunk_index = static_cast(chunk_index); - cursor.pos[it].text_index = static_cast(text_index); - cursor.pos[it].last_text_index = static_cast(text_index); - } else { - it = text_index < cursor.pos[2].text_index && chunk_index <= cursor.pos[2].chunk_index ? 0 : 1; - - cursor.pos[it].chunk_index = static_cast(chunk_index - (!p_ui->p_value->empty())); - cursor.pos[it].text_index = static_cast(utf_char_index); - cursor.pos[it].last_text_index = static_cast(text_index); - } - - if (it == 1) { - cursor.pos[0] = cursor.pos[2]; - cursor.pos[3] = cursor.pos[1]; - } else { - cursor.pos[1] = cursor.pos[2]; - cursor.pos[3] = cursor.pos[0]; - } - } - - ekg::dispatch(ekg::env::redraw); -} - -void ekg::ui::textbox_widget::on_create() { - ekg::ui::textbox*p_ui {(ekg::ui::textbox*) this->p_data}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - f_renderer.init(); -} - -void ekg::ui::textbox_widget::on_reload() { - ekg::ui::textbox *p_ui {(ekg::ui::textbox*) this->p_data}; - ekg::rect &rect {this->get_abs_rect()}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - - int32_t scaled_height {p_ui->get_scaled_height()}; - float text_width {f_renderer.get_text_width(p_ui->get_tag())}; - float dimension_offset {this->text_height / 2}; - - this->text_height = f_renderer.get_text_height(); - this->text_offset = ekg::find_min_offset(text_width, dimension_offset); - - if (p_ui->is_auto_initial_dimension()) { - this->dimension.w = ekg_min(this->dimension.w, text_width); - this->min_size.x = ekg_min(this->min_size.x, this->text_height); - p_ui->set_auto_initial_dimension(false); - } - - this->dimension.h = (this->text_height + dimension_offset) * static_cast(scaled_height); - this->min_size.y = ekg_min(this->min_size.y, this->dimension.h); - - if (this->last_text_chunk_size != p_ui->p_value->size()) { - /** - * Generate default empty text for say to the rest of textbox, - * that is empty and not with null lines. - */ - if (p_ui->p_value->empty()) { - p_ui->p_value->emplace_back(); - } - - if (!this->is_high_frequency) { - ekg::update_high_frequency(this); - } - } - - this->embedded_scroll.p_rect_mother = ▭ - this->embedded_scroll.rect_child = this->rect_text; - this->embedded_scroll.widget_id = this->p_data->get_id(); - this->embedded_scroll.on_reload(); -} - -void ekg::ui::textbox_widget::on_pre_event(ekg::os::io_event_serial &io_event_serial) { - abstract_widget::on_pre_event(io_event_serial); - - this->embedded_scroll.on_pre_event(io_event_serial); - this->flag.absolute = ( - this->embedded_scroll.is_dragging_bar() || - this->embedded_scroll.flag.activity || - this->flag.state - ); -} - -void ekg::ui::textbox_widget::on_event(ekg::os::io_event_serial &io_event_serial) { - bool pressed {ekg::input::action("textbox-activity")}; - bool released {ekg::input::released()}; - bool motion {ekg::input::motion()}; - ekg::ui::textbox *p_ui {static_cast(this->p_data)}; - - if (this->flag.hovered) { - ekg::cursor = ekg::system_cursor::ibeam; - } - - this->is_action_select_enable = ekg::input::action("textbox-action-select"); - if (this->flag.hovered && pressed) { - ekg::set(this->flag.focused, this->flag.hovered); - ekg::reset(ekg::core->ui_timing); - - ekg::ui::textbox_widget::cursor &main_cursor {this->loaded_multi_cursor_list.at(0)}; - ekg::ui::textbox_widget::cursor clicked_pos {}; - this->check_cursor_text_bounding(clicked_pos, true); - - ekg::vec4 &interact {ekg::input::interact()}; - this->cursor_delta.x = interact.x; - this->cursor_delta.y = interact.y; - - bool movement_input_enabled {this->flag.focused && this->is_action_select_enable}; - if (movement_input_enabled && !this->flag.state) { - if (clicked_pos.pos[0] >= main_cursor.pos[2]) { - main_cursor.pos[2] = main_cursor.pos[0]; - main_cursor.pos[1] = clicked_pos.pos[0]; - main_cursor.pos[3] = clicked_pos.pos[0]; - } else { - main_cursor.pos[2] = main_cursor.pos[1]; - main_cursor.pos[0] = clicked_pos.pos[0]; - main_cursor.pos[3] = clicked_pos.pos[1]; - } - } else { - main_cursor.pos[0] = clicked_pos.pos[0]; - main_cursor.pos[1] = clicked_pos.pos[1]; - main_cursor.pos[2] = clicked_pos.pos[2]; - main_cursor.pos[3] = clicked_pos.pos[0]; - } - - this->flag.state = this->flag.hovered; - } else if (this->flag.state && motion && !this->embedded_scroll.is_dragging_bar()) { - ekg::vec4 &interact {ekg::input::interact()}; - ekg::vec2 diff { - interact.x - this->cursor_delta.x, - interact.y - this->cursor_delta.y - }; - - this->check_cursor_text_bounding( - this->loaded_multi_cursor_list.at(0), - !(static_cast(diff.x * diff.x + diff.y * diff.y) > 5) - ); - } - - if (released) { - this->flag.state = false; - } - - this->embedded_scroll.on_event(io_event_serial); - - this->flag.was_hovered = this->flag.hovered; - if (!this->flag.was_hovered && this->is_high_frequency) { - this->flag.was_hovered = false; - } - - if ((this->flag.focused || this->flag.hovered || this->flag.absolute || this->embedded_scroll.flag.activity) && !this->is_high_frequency) { - ekg::update_high_frequency(this); - } - - if (!this->flag.hovered && pressed && !ekg::input::typed()) { - ekg::ui::textbox_widget::cursor main_cursor {this->loaded_multi_cursor_list.at(0)}; - main_cursor.pos[1] = main_cursor.pos[0]; - - this->loaded_multi_cursor_list.clear(); - this->loaded_multi_cursor_list.push_back(main_cursor); - - ekg::set(this->flag.focused, false); - } - - // firing clipboard input by ekg::input::clipboard does not accept if mouse is out of textbox, - // in the case of popup with clipboard operations, it will not process the action. - - this->is_clipboard_cut = ekg::input::action("clipboard-cut"); - this->is_clipboard_paste = ekg::input::action("clipboard-paste"); - this->is_clipboard_copy = ekg::input::action("clipboard-copy"); - - bool should_process_textbox { - this->flag.focused && ( - (io_event_serial.event_type == ekg::platform_event_type::key_down || io_event_serial.event_type == ekg::platform_event_type::text_input) || - (this->is_clipboard_copy || this->is_clipboard_cut || this->is_clipboard_paste) - ) - }; - - if (!should_process_textbox) { - return; - } - - this->was_typed = true; - - if (io_event_serial.event_type == ekg::platform_event_type::text_input) { - for (ekg::ui::textbox_widget::cursor &cursor: this->loaded_multi_cursor_list) { - this->process_text( - cursor, - io_event_serial.text_input, - ekg::ui::textbox_widget::action::add_text, - 1 - ); - } - } else if (io_event_serial.event_type == ekg::platform_event_type::key_down && ekg::input::receive("escape")) { - ekg::ui::textbox_widget::cursor main_cursor {this->loaded_multi_cursor_list.at(0)}; - main_cursor.pos[1] = main_cursor.pos[0]; - - this->loaded_multi_cursor_list.clear(); - this->loaded_multi_cursor_list.push_back(main_cursor); - - ekg::set(this->flag.focused, false); - } else if (ekg::input::action("textbox-action-select-all")) { - ekg::ui::textbox_widget::cursor main_cursor {this->loaded_multi_cursor_list.at(0)}; - main_cursor.pos[0].chunk_index = 0; - main_cursor.pos[0].text_index = 0; - main_cursor.pos[0].last_text_index = 0; - - main_cursor.pos[1].chunk_index = ekg_min(static_cast(p_ui->p_value->size()) - 1, static_cast(0)); - main_cursor.pos[1].text_index = static_cast(ekg::utf_length(ekg_textbox_get_cursor_text(main_cursor.pos[1]))); - main_cursor.pos[1].last_text_index = main_cursor.pos[1].chunk_index; - - this->loaded_multi_cursor_list.clear(); - this->loaded_multi_cursor_list.push_back(main_cursor); - } else { - int64_t cursor_dir[2] {}; - this->is_action_modifier_enable = ekg::input::action("textbox-action-modifier"); - - for (ekg::ui::textbox_widget::cursor &cursor: this->loaded_multi_cursor_list) { - cursor_dir[0] = cursor_dir[1] = 0; - - if (ekg::input::action("textbox-action-up")) { - cursor_dir[1] = -1; - } else if (ekg::input::action("textbox-action-down")) { - cursor_dir[1] = 1; - } else if (ekg::input::action("textbox-action-left")) { - cursor_dir[0] = -1; - } else if (ekg::input::action("textbox-action-right")) { - cursor_dir[0] = 1; - } else if (ekg::input::action("textbox-action-delete-left")) { - this->process_text(cursor, "backspace", ekg::ui::textbox_widget::action::erase_text, -1); - } else if (ekg::input::action("textbox-action-delete-right")) { - this->process_text(cursor, "delete", ekg::ui::textbox_widget::action::erase_text, 1); - } else if (this->p_data->get_task(ekg::action::activity) != nullptr && ekg::input::action("textbox-action-activity")) { - ekg_action_dispatch( - true, - ekg::action::activity - ); - - this->was_typed = false; - break; - } else if (ekg::input::action("textbox-action-break-line")) { - this->process_text(cursor, "return", ekg::ui::textbox_widget::action::break_line, 1); - } else if (ekg::input::action("textbox-action-tab")) { - this->process_text(cursor, "\t", ekg::ui::textbox_widget::action::add_text, 1); - } else if (this->is_clipboard_copy || this->is_clipboard_paste || this->is_clipboard_cut) { - this->process_text(cursor, "clipboard", ekg::ui::textbox_widget::action::add_text, 0); - } - - if (cursor_dir[0] != 0 || cursor_dir[1] != 0) { - this->check_nearest_word(cursor, cursor_dir[0], cursor_dir[1]); - this->move_target_cursor(cursor, cursor_dir[0], cursor_dir[1]); - } - } - } -} - -void ekg::ui::textbox_widget::on_post_event(ekg::os::io_event_serial &io_event_serial) { - abstract_widget::on_post_event(io_event_serial); - this->embedded_scroll.flag.hovered = false; - this->embedded_scroll.flag.activity = false; -} - -void ekg::ui::textbox_widget::on_update() { - ekg::ui::textbox*p_ui {static_cast(this->p_data)}; - - /** - * @TODO Ticking system and rendering system. - * - * A critic issue with the rendering system and ticking system: - * Any modification should tell to re-draw the GUI always one text is inserted/erased from UI vector otherwise, - * nothing changes until one re-draw call is performed. - * - * I think two solutions (one is too stupid): - * 1- Overhead the vector calls and add one atomic-semaphore to re-draw the entire GUI. - * 2- The stupid but operable: let the application GUI do lot of re-draw calls naturally. - * - * The 1 is ok, I think works better than the 2, but the performance can be little reduced, due the virtuals necessary. - * The 2 requires the application works, and update lot of stuff concurrent to perform a smooth text update. - * - * I will not code it for now, too much to my silly brain, - * Rina. - **/ - uint64_t chunk_text_size {p_ui->p_value->size()}; - if (this->last_text_chunk_size != chunk_text_size) { - this->text_edited = true; - this->update_ui_text = true; - this->rect_text.h = (this->text_height * static_cast(p_ui->p_value->size())); - - float vertical_scroll_limit {this->rect_text.h - this->dimension.h}; - float new_text_height_diff { - this->text_height * - ( - /*** - * before casting to float, must cast to int64_t, - * uint64_t range is large than int64_t - * but uint64_t can not subtract less than 0, - * otherwise it returns overflow. - */ - static_cast( - static_cast( - p_ui->p_value->size() - ) - this->last_text_chunk_size - ) + - 1.0f - ) - }; - - /** - * If the difference between the new and old texts, - * is nearest of scrolling y, it should follow the scrolling. - */ - if (vertical_scroll_limit > 0 && this->embedded_scroll.scroll.w < -(vertical_scroll_limit - new_text_height_diff)) { - this->embedded_scroll.scroll.w = -vertical_scroll_limit; - } - - this->last_text_chunk_size = chunk_text_size; - } - - this->embedded_scroll.on_update(); - this->is_high_frequency = this->embedded_scroll.check_activity_state(this->flag.was_hovered); -} - -/** - * The find cursor method perform bloat, - * but how the entire selection system is CPU-batched (it means not called all the render time), - * the performance is not necessary bad. - */ -bool ekg::ui::textbox_widget::find_cursor( - ekg::ui::textbox_widget::cursor &target_cursor, - int64_t utf_char_index, - int64_t chunk_index, - bool last_line_utf_char_index -) { - bool a_cursor_pos {}; - bool b_cursor_pos {}; - - for (ekg::ui::textbox_widget::cursor &cursor: this->loaded_multi_cursor_list) { - a_cursor_pos = ( - utf_char_index >= cursor.pos[0].text_index || - ( - last_line_utf_char_index && - utf_char_index + 1 >= cursor.pos[0].text_index) - ) && chunk_index == cursor.pos[0].chunk_index; - - b_cursor_pos = ( - ( - ( - cursor.pos[0] == cursor.pos[1] && - utf_char_index <= cursor.pos[1].text_index - ) || - utf_char_index < cursor.pos[1].text_index - ) || - ( - last_line_utf_char_index && - utf_char_index + 1 < cursor.pos[1].text_index) - ) && chunk_index == cursor.pos[1].chunk_index; - - if ( - ( - cursor.pos[0].chunk_index != cursor.pos[1].chunk_index && - (a_cursor_pos || b_cursor_pos) - ) || - ( - a_cursor_pos && b_cursor_pos - ) || - ( - chunk_index > cursor.pos[0].chunk_index && - chunk_index < cursor.pos[1].chunk_index - ) - ) { - target_cursor = cursor; - return true; - } - } - - return false; -} - -void ekg::ui::textbox_widget::on_draw_refresh() { - ekg::ui::textbox *p_ui {(ekg::ui::textbox*) this->p_data}; - ekg::rect &rect {this->get_abs_rect()}; - ekg::draw::font_renderer &f_renderer {ekg::f_renderer(p_ui->get_font_size())}; - ekg::service::theme_scheme_t &theme_scheme {ekg::current_theme_scheme()}; - ekg::gpu::allocator &allocator {ekg::core->gpu_allocator}; - - this->embedded_scroll.clamp_scroll(); - - float x {rect.x + this->embedded_scroll.scroll.x}; - float y {}; - - EKG_ASSERT_SCISSOR( - this->scissor, - rect, - this->p_parent_scissor - ); - - ekg_draw_assert_scissor(); - - ekg::draw::rect( - rect, - theme_scheme.textbox_background, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::background) - ); - - ekg::gpu::data_t &data {allocator.bind_current_data()}; - ekg::vec4 color {theme_scheme.textbox_string}; - - x = static_cast(static_cast(x)); - data.buffer_content[0] = x; - - data.buffer_content[2] = static_cast(-f_renderer.non_swizzlable_range); - data.buffer_content[3] = static_cast(ekg::gpu::allocator::concave); - - data.buffer_content[4] = color.x; - data.buffer_content[5] = color.y; - data.buffer_content[6] = color.z; - data.buffer_content[7] = color.w; - data.factor = 1; - - ekg::rect vertices {}; - ekg::rect coordinates {}; - - this->rect_text.x = 0.0f; - this->rect_text.y = 0.0f; - this->is_ui_enabled = p_ui->get_typing_state() == ekg::state::enable; - - uint64_t text_chunk_size {p_ui->p_value->size()}; - if (!text_chunk_size) { - p_ui->p_value->emplace_back(); - text_chunk_size++; - } - - if (!this->was_typed && this->flag.focused) { - for (ekg::ui::textbox_widget::cursor &cursor : this->loaded_multi_cursor_list) { - if ( - this->latest_size_until_refresh != text_chunk_size || - ( - - /** - * Check if the cursors are bounding correctly the text, - * then reset the cursors position. - **/ - - ( - (cursor.pos[0].chunk_index > text_chunk_size) || - (cursor.pos[0].chunk_index < text_chunk_size && cursor.pos[0].text_index > p_ui->p_value->at(cursor.pos[0].chunk_index).size()) - ) || - ( - (cursor.pos[0] != cursor.pos[1]) && - (cursor.pos[1].chunk_index < text_chunk_size && cursor.pos[1].text_index > p_ui->p_value->at(cursor.pos[1].chunk_index).size()) - ) - ) - ) { - this->check_cursor_text_bounding(cursor, true); - } - } - } - - this->latest_size_until_refresh = text_chunk_size; - this->was_typed = false; - - char32_t char32 {}; - std::string utf_string {}; - uint8_t char8 {}; - - ekg::ui::textbox_widget::cursor cursor {}; - - uint64_t text_size {}; - uint64_t utf_char_index {}; - uint64_t it {}; - - FT_Face ft_face {}; - FT_UInt ft_uint_previous {}; - FT_Vector_ ft_vector_previous_char {}; - - bool do_not_fill_line {}; - bool is_utf_char_last_index {}; - - /** - * 0 == previous char wsize - * 1 == current char wisze - * 2 == next char wsize - */ - this->cursor_char_wsize[0] = 0.0f; - this->cursor_char_wsize[1] = 0.0f; - this->cursor_char_wsize[2] = 0.0f; - - if (this->update_ui_text) { - this->update_ui_text_data(); - this->update_ui_text = false; - } - - this->cursor_draw_data_list.clear(); - this->rect_text.h = (this->text_height * static_cast(text_chunk_size)); - - /** - * This line of code check the renderable text chunk index value, - * dynamically calculating the amount of scroll with the size of - * rect text height. - */ - this->visible_text[1] = ekg::get_index_by_scroll( - this->embedded_scroll.scroll.y, - this->rect_text.h, - text_chunk_size - ); - - // Multiply with the current visible index for get the perfect y position. - y = (this->text_height * static_cast(this->visible_text[1])); - - // Get the diff. between the visible text position and subtract with rect position for performn the scrolling effect. - float visible_text_height {this->text_height * this->visible_text[1]}; - float rendering_text_scroller_diff {rect.y - (rect.y + this->embedded_scroll.scroll.y + visible_text_height)}; - - data.buffer_content[1] = floorf(rect.y); - - // Prevent from floating point loss in GPU. - y = static_cast(static_cast(floorf(-rendering_text_scroller_diff))); - - /** - * The text iterator jump utf 8 - 16 - 32 sequences. - * For better performance, textbox does not render texts out of rect. - */ - for (uint64_t chunk_index {this->visible_text[1]}; chunk_index < text_chunk_size; chunk_index++) { - std::string &text {p_ui->p_value->at(chunk_index)}; - - x = this->rect_cursor.w; - ft_uint_previous = 0; - utf_char_index = 0; - do_not_fill_line = false; - - data.factor += static_cast(y) + 32; - if (y > rect.h) { - data.factor += ekg_generate_factor_hash( - static_cast(text_chunk_size) * 32, 32, 32 - ); - break; - } - - if ( - text.empty() - && - this->find_cursor(cursor, static_cast(utf_char_index), static_cast(chunk_index), false) - ) { - do_not_fill_line = true; - - ekg::rect &select_rect {this->cursor_draw_data_list.emplace_back()}; - select_rect.x = x; - select_rect.y = y; - select_rect.w = this->rect_cursor.w + ((this->rect_cursor.w) * static_cast(cursor.pos[0] != cursor.pos[1])); - select_rect.h = this->text_height; - } - - text_size = ekg::utf_length(text); - for (it = 0; it < text.size(); it++) { - char8 = static_cast(text.at(it)); - it += ekg::utf_check_sequence(char8, char32, utf_string, text, it); - - switch (char32 < 256 || !f_renderer.font_face_emoji.font_face_loaded) { - case true: { - ft_face = f_renderer.font_face_text.ft_face; - break; - } - - default: { - ft_face = f_renderer.font_face_emoji.ft_face; - break; - } - } - - if (f_renderer.ft_bool_kerning && ft_uint_previous) { - FT_Get_Kerning(ft_face, ft_uint_previous, char32, 0, &ft_vector_previous_char); - x += static_cast(ft_vector_previous_char.x >> 6); - } - - ekg::draw::glyph_char_t &char_data {f_renderer.mapped_glyph_char_data[char32]}; - is_utf_char_last_index = utf_char_index + 1 == text_size; - - if (!char_data.was_sampled) { - f_renderer.loaded_sampler_generate_list.emplace_back(char32); - char_data.was_sampled = true; - } - - if ( - this->find_cursor( - cursor, - static_cast(utf_char_index), - static_cast(chunk_index), - is_utf_char_last_index - ) - && - !( - chunk_index > cursor.pos[0].chunk_index - && - chunk_index < cursor.pos[1].chunk_index - ) - ) { - - ekg::rect &select_rect {this->cursor_draw_data_list.emplace_back()}; - - // The end of line cursor drawing require check before to addition. - select_rect.x = ( - x + - ( - char_data.wsize - * - static_cast( - is_utf_char_last_index - && - cursor.pos[0] == cursor.pos[1] - && - cursor.pos[0].text_index == utf_char_index + 1 - ) - ) - ); - - select_rect.y = y; - - // Draw the offset signal for next line selected. - select_rect.w = ( - cursor.pos[0] != cursor.pos[1] ? // else true - ( - (char_data.wsize) - + - (this->rect_cursor.w + this->rect_cursor.w) - * - ( - is_utf_char_last_index - && - cursor.pos[1].chunk_index > chunk_index - && - cursor.pos[0].chunk_index == chunk_index - ) - ) : // else false - ( - this->rect_cursor.w - ) - ); - - select_rect.h = this->text_height; - } - - if (x + this->embedded_scroll.scroll.x < rect.w) { - vertices.x = x + char_data.left; - vertices.y = y + f_renderer.full_height - char_data.top; - - vertices.w = char_data.w; - vertices.h = char_data.h; - - coordinates.x = char_data.x; - coordinates.w = vertices.w / f_renderer.full_width; - coordinates.h = vertices.h / f_renderer.full_height; - - allocator.push_back_geometry( - vertices.x, - vertices.y, - coordinates.x, - coordinates.y - ); - - allocator.push_back_geometry( - vertices.x, - vertices.y + vertices.h, - coordinates.x, - coordinates.y + coordinates.h - ); - - allocator.push_back_geometry( - vertices.x + vertices.w, - vertices.y + vertices.h, - coordinates.x + coordinates.w, - coordinates.y + coordinates.h - ); - - allocator.push_back_geometry( - vertices.x + vertices.w, - vertices.y + vertices.h, - coordinates.x + coordinates.w, - coordinates.y + coordinates.h - ); - - allocator.push_back_geometry( - vertices.x + vertices.w, - vertices.y, - coordinates.x + coordinates.w, - coordinates.y - ); - - allocator.push_back_geometry( - vertices.x, - vertices.y, - coordinates.x, - coordinates.y - ); - - data.factor += ekg_generate_factor_hash( - x, char32, char_data.x - ); - } - - if (x + this->embedded_scroll.scroll.x > rect.w) { - break; - } - - ft_uint_previous = char32; - x += char_data.wsize; - utf_char_index++; - } - - if (!do_not_fill_line && chunk_index > cursor.pos[0].chunk_index && chunk_index < cursor.pos[1].chunk_index) { - ekg::rect &select_rect {this->cursor_draw_data_list.emplace_back()}; - select_rect.x = this->rect_cursor.w; - select_rect.y = y; - select_rect.w = x + this->rect_cursor.w + this->rect_cursor.w; - select_rect.h = this->text_height; - } - - y += this->text_height; - } - - f_renderer.flush(); - allocator.bind_texture(f_renderer.get_sampler_texture()); - allocator.dispatch(); - - bool draw_cursor {this->flag.focused && !ekg::reach(ekg::core->ui_timing, 500)}; - for (ekg::rect &cursor_rect : this->cursor_draw_data_list) { - cursor_rect.x = cursor_rect.x + rect.x + this->embedded_scroll.scroll.x; - cursor_rect.y = cursor_rect.y + f_renderer.offset_text_height + f_renderer.offset_text_height + rect.y; - - switch (static_cast(cursor_rect.w)) { - case 2: - if (draw_cursor) { - ekg::draw::rect( - cursor_rect, - theme_scheme.textbox_cursor, - ekg::draw_mode::filled - ); - } - - break; - default: - ekg::draw::rect( - cursor_rect, - theme_scheme.textbox_select, - ekg::draw_mode::filled, - ekg_layer(ekg::layer::highlight) - ); - break; - } - } - - this->rect_text.h += this->text_offset + ekg_pixel_div_2; - this->embedded_scroll.rect_child = this->rect_text; - this->embedded_scroll.scissor = this->scissor; - this->embedded_scroll.on_draw_refresh(); - - ekg::draw::rect( - rect, - theme_scheme.textbox_outline, - ekg::draw_mode::outline - ); -} diff --git a/src/util/geometry.cpp b/src/util/geometry.cpp deleted file mode 100644 index 53421211..00000000 --- a/src/util/geometry.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/core/runtime.hpp" - -bool ekg::operator==(const ekg::rect &l, const ekg::rect &r) { - return ekg_equals_float(l.x, r.x) && ekg_equals_float(l.y, r.y) && ekg_equals_float(l.w, r.w) && - ekg_equals_float(l.h, r.h); -} - -bool ekg::operator!=(const ekg::rect &l, const ekg::rect &r) { - return !(l == r); -} - -bool ekg::operator==(const ekg::rect &l, const ekg::vec4 &r) { - return ekg_equals_float(l.x, r.x) && ekg_equals_float(l.y, r.y) && ekg_equals_float(l.w, r.z) && - ekg_equals_float(l.h, r.w); -} - -bool ekg::operator!=(const ekg::rect &l, const ekg::vec4 &r) { - return !(l == r); -} - -ekg::rect ekg::operator-(const ekg::rect &l, const ekg::rect &r) { - return {l.x - r.x, l.y - r.y, l.w, l.h}; -} - -ekg::rect ekg::operator+(const ekg::rect &l, const ekg::rect &r) { - return {l.x + r.x, l.y + r.y, l.w, l.h}; -} - -ekg::rect ekg::operator-(const ekg::rect &l, const ekg::vec4 &r) { - return {l.x - r.x, l.y - r.y, l.w, l.h}; -} - -ekg::rect ekg::operator+(const ekg::rect &l, const ekg::vec4 &r) { - return {l.x + r.x, l.y + r.y, l.w, l.h}; -} - -ekg::vec4 ekg::operator-(const ekg::vec4 &l, const ekg::vec4 &r) { - return {l.x - r.x, l.y - r.y, l.z, l.w}; -} - -ekg::vec4 ekg::operator+(const ekg::vec4 &l, const ekg::vec4 &r) { - return {l.x + r.x, l.y + r.y, l.z, l.w}; -} - -ekg::vec4 ekg::operator-(const ekg::vec4 &l, const ekg::rect &r) { - return {l.x - r.x, l.y - r.y, l.z, l.w}; -} - -ekg::vec4 ekg::operator+(const ekg::vec4 &l, const ekg::rect &r) { - return {l.x + r.x, l.y + r.y, l.z, l.w}; -} - -ekg::vec2 ekg::operator/(const ekg::vec2 &l, float r) { - return {l.x / r, l.y / r}; -} - -ekg::vec4 ekg::operator/(const ekg::vec4 &l, float r) { - return {l.x / r, l.y / r, l.z / r, l.w / r}; -} - -bool ekg::rect_collide_rect(const ekg::rect &a, const ekg::rect &b) { - return a.x < b.x + b.w && a.x + a.w > b.x && - a.y < b.y + b.h && a.x + a.w > b.y; -} - -uint64_t ekg::get_index_by_scroll(float scroll, float dimension, uint64_t size) { - return scroll == 0.0f ? 0 : static_cast(((-scroll) / dimension) * size); -} - -uint64_t ekg::get_index_by_normalized_scroll(float normalized, uint64_t size) { - return static_cast(normalized * size); -} - -void ekg::ortho( - float *p_matrix, - float left, - float right, - float bottom, - float top -) { - const float depth_near = -1.0f; - const float depth_far = 1.0f; - const float depth_inv = 1.0f / (depth_far - depth_near); - const float y_inv = 1.0f / (top - bottom); - const float x_inv = 1.0f / (right - left); - - p_matrix[0] = 2.0f * x_inv; - p_matrix[1] = 0.0f; - p_matrix[2] = 0.0f; - p_matrix[3] = 0.0f; - - p_matrix[4] = 0.0f; - p_matrix[5] = 2.0f * y_inv; - p_matrix[6] = 0.0f; - p_matrix[7] = 0.0f; - - p_matrix[8] = 0.0f; - p_matrix[9] = 0.0f; - p_matrix[10] = -2.0f * depth_inv; - p_matrix[11] = 0.0f; - - p_matrix[12] = -(right + left) * x_inv; - p_matrix[13] = -(top + bottom) * y_inv; - p_matrix[14] = -(depth_far + depth_near) * depth_inv; - p_matrix[15] = 1.0f; -} - -bool ekg::rect_collide_vec_precisely(const ekg::rect &rect, const ekg::vec4 &vec) { - return vec.x >= rect.x && vec.x <= rect.x + rect.w && vec.y >= rect.y && vec.y <= rect.y + rect.h; -} - -bool ekg::rect_collide_vec(const ekg::rect &rect, const ekg::vec4 &vec) { - return vec.x > rect.x && vec.x < rect.x + rect.w && vec.y > rect.y && vec.y < rect.y + rect.h; -} - -void ekg::set_dock_scaled(const ekg::rect &rect, const ekg::vec2 &offset, ekg::docker &docker) { - docker.rect = rect; - - docker.left.x = rect.x; - docker.left.y = rect.y; - docker.left.w = offset.x; - docker.left.h = rect.h; - - docker.right.w = offset.x; - docker.right.h = rect.h; - docker.right.x = rect.x + rect.w - offset.x; - docker.right.y = rect.y; - - docker.top.x = rect.x; - docker.top.y = rect.y; - docker.top.w = rect.w; - docker.top.h = offset.y; - - docker.bottom.w = rect.w; - docker.bottom.h = offset.y; - docker.bottom.x = rect.x; - docker.bottom.y = rect.y + rect.h - offset.y; - - float dx = offset.x / 2; - float dy = offset.y / 2; - - docker.center.x = rect.x + (rect.w / 2) - dx; - docker.center.y = rect.y + (rect.h / 2) - dy; - docker.center.w = dx; - docker.center.h = dy; -} - -int32_t ekg::find_collide_dock(ekg::docker &docker, uint16_t flags, const ekg::vec4 &vec) { - if (ekg_bitwise_contains(flags, ekg::dock::full) && ekg::rect_collide_vec(docker.rect, vec)) { - return ekg::dock::full; - } - - uint16_t collided {ekg::dock::none}; - - collided = ( - ekg_bitwise_contains(flags, ekg::dock::left) && - ekg::rect_collide_vec(docker.left, vec) ? ekg_bitwise_add(collided, ekg::dock::left) : collided - ); - - collided = ( - ekg_bitwise_contains(flags, ekg::dock::right) && - ekg::rect_collide_vec(docker.right, vec) ? ekg_bitwise_add(collided, ekg::dock::right) : collided - ); - - collided = ( - ekg_bitwise_contains(flags, ekg::dock::top) && - ekg::rect_collide_vec(docker.top, vec) ? ekg_bitwise_add(collided, ekg::dock::top) : collided - ); - - collided = ( - ekg_bitwise_contains(flags, ekg::dock::bottom) && - ekg::rect_collide_vec(docker.bottom, vec) ? ekg_bitwise_add(collided, ekg::dock::bottom) : collided - ); - - collided = ( - ekg_bitwise_contains(flags, ekg::dock::center) && - ekg::rect_collide_vec(docker.center, vec) ? ekg_bitwise_add(collided, ekg::dock::center) : collided - ); - - return collided; -} - -void ekg::set_rect_clamped(ekg::rect &rect, float min_size) { - rect.x = ekg_min(rect.x, 0.0f); - rect.y = ekg_min(rect.y, 0.0f); - rect.w = ekg_min(rect.w, min_size); - rect.h = ekg_min(rect.h, min_size); -} - -float ekg::find_min_offset(float width, float offset) { - /** - * Use initial offset to get the min possible offset. - * Initial offset value may is font height divided by 2. - **/ - float full_rect {width + offset}; - return static_cast(static_cast((full_rect * 0.5f) - (width * 0.5f))); -} - -float ekg::smooth(float duration, uint64_t ticks) { - duration = static_cast(ticks) / duration; - return ekg_clamp(6.0f * powf(duration, 5) - (15 * powf(duration, 4)) + (10 * powf(duration, 3)), 0.0f, 1.0f); -} - -float ekg::lerp(float a, float b, float dt) { - return a + (b - a) * dt; -} - -ekg::vec4 ekg::color(int32_t r, int32_t g, int32_t b, int32_t a) { - return {static_cast(r) / 255, static_cast(g) / 255, static_cast(b) / 255, - static_cast(a) / 255}; -} diff --git a/src/util/gui.cpp b/src/util/gui.cpp deleted file mode 100644 index 2c59b25d..00000000 --- a/src/util/gui.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/core/runtime.hpp" -#include "ekg/ui/abstract/ui_abstract_widget.hpp" - -void ekg::reload(int32_t id) { - ekg::core->do_task_reload(ekg::core->get_fast_widget_by_id(id)); -} - -void ekg::reload(ekg::ui::abstract_widget *p_widget) { - ekg::core->do_task_reload(p_widget); -} - -void ekg::synclayout(int32_t id) { - ekg::core->do_task_synclayout(ekg::core->get_fast_widget_by_id(id)); -} - -void ekg::synclayout(ekg::ui::abstract_widget *p_widget) { - ekg::core->do_task_synclayout(p_widget); -} - -void ekg::push_back_stack(ekg::ui::abstract_widget *p_widget, ekg::stack &stack) { - if (p_widget == nullptr) { - return; - } - - if (stack.target_id == p_widget->p_data->get_id()) { - stack.target_id_found = true; - } - - stack.ordered_list.push_back(p_widget); - - for (int32_t &ids: p_widget->p_data->get_child_id_list()) { - ekg::ui::abstract_widget *p_widgets {ekg::core->get_fast_widget_by_id(ids)}; - if (p_widgets == nullptr) { - continue; - } - - ekg::push_back_stack(p_widgets, stack); - } -} - -ekg::stack::stack(std::string_view tag, bool do_not_trigger_stack_update) { - this->tag = tag; - - if (do_not_trigger_stack_update) { - return; - } - - this->push(); -} - -ekg::stack::~stack() { - this->destroy(); -} - -void ekg::stack::push() { - ekg::core->set_current_stack(this); -} - -void ekg::stack::pop() { - ekg::core->set_current_stack(nullptr); -} - -void ekg::stack::destroy() { - for (ekg::ui::abstract_widget *p_widget : this->ordered_list) { - p_widget->p_data->destroy(); - } - - this->ordered_list.clear(); -} - -void ekg::stack::clear() { - this->ordered_list.clear(); - this->target_id_found = false; -} - -ekg::ui::abstract_widget *ekg::find_absolute_parent_master(ekg::ui::abstract_widget *p_widget) { - if (p_widget == nullptr || p_widget->p_data->get_parent_id() == 0) { - return p_widget; - } - - auto widget {ekg::core->get_fast_widget_by_id(p_widget->p_data->get_parent_id())}; - return ekg::find_absolute_parent_master(widget); -} - -void ekg::refresh(int32_t id) { - ekg::core->do_task_refresh(ekg::core->get_fast_widget_by_id(id)); -} - -void ekg::refresh(ekg::ui::abstract_widget *widget) { - ekg::core->do_task_refresh(widget); -} - -void ekg::update_high_frequency(int32_t id) { - ekg::core->set_update_high_frequency(ekg::core->get_fast_widget_by_id(id)); -} - -void ekg::update_high_frequency(ekg::ui::abstract_widget *p_widget) { - ekg::core->set_update_high_frequency(p_widget); -} - -void ekg::dispatch(task *p_task) { - if (!p_task->is_dispatched) { - ekg::core->service_handler.dispatch(p_task); - } -} - -void ekg::dispatch(ekg::env env) { - switch (env) { - case ekg::env::redraw: - ekg::ui::redraw = true; - break; - default: - ekg::core->service_handler.dispatch_pre_allocated_task(static_cast(env)); - break; - } -} - -bool &ekg::set(bool &value, bool result) { - if (value != result) { - ekg::ui::redraw = true; - } - - return (value = result); -} - -std::string &ekg::set(std::string &value, std::string_view result) { - if (value != result) { - ekg::ui::redraw = true; - } - - return (value = result); -} - -int32_t &ekg::set(int32_t &value, int32_t result) { - if (value != result) { - ekg::dispatch(ekg::env::redraw); - } - - return (value = result); -} diff --git a/src/util/image.cpp b/src/util/image.cpp deleted file mode 100644 index d859345e..00000000 --- a/src/util/image.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "ekg/util/image.hpp" -#include - -ekg::format_convert_result ekg::image_src_r8_convert_to_r8g8b8a8( - FT_Vector size, - const unsigned char *p_src, - std::vector &dst -) { - if (size.x == 0 || size.y == 0 || p_src == nullptr || dst.empty()) { - return ekg::format_convert_result::failed; - } - - size_t index {}; - for (size_t it {}; it < (size.x * size.y); it++) { - const unsigned char &char8_red_color { - p_src[it] - }; - - index = it * 4; - - if (index == dst.size()) { - break; - } - - /** - * may I be wrong? but the format is ARGB and not RGBA, - * I do not know. - **/ - - dst.at(index + 0) = char8_red_color; - dst.at(index + 1) = 255; - dst.at(index + 2) = 255; - dst.at(index + 3) = 255; - } - - return ekg::format_convert_result::success; -} \ No newline at end of file diff --git a/src/util/io.cpp b/src/util/io.cpp deleted file mode 100644 index f0fcdc8a..00000000 --- a/src/util/io.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ekg/core/runtime.hpp" -#include "ekg/util/io.hpp" - -#include - -/* start of item */ - -ekg::item::item( - std::string_view insert_value, - ekg::flags insert_attr_bits -) { - this->placement.rect.w = 100.0f; - this->value = insert_value; - this->attr_bits = insert_attr_bits; - this->placement.text_dock_flags = ekg::dock::left; -} - -ekg::item::item( - std::string_view insert_value, - std::vector insert_item_list, - ekg::flags insert_attr_bits -) { - this->placement.rect.w = 100.0f; - this->value = insert_value; - this->insert(this->begin(), insert_item_list.begin(), insert_item_list.end()); - this->attr_bits = insert_attr_bits; - this->placement.text_dock_flags = ekg::dock::left; -} - -ekg::item::~item() { - if (this->p_semaphore != nullptr) { - *this->p_semaphore = true; - } -} - -void ekg::item::unsafe_set_addressed(ekg::item *p_set_addressed) { - this->p_addressed = p_set_addressed; -} - -ekg::item *ekg::item::unsafe_get_addressed() { - return this->p_addressed; -} - -ekg::placement &ekg::item::unsafe_get_placement() { - return this->placement; -} - -void ekg::item::unsafe_set_semaphore(bool *p_set_semaphore) { - this->p_semaphore = p_set_semaphore; -} - -void ekg::item::unsafe_set_visible_count(uint64_t count) { - this->visible_count = count; -} - -uint64_t ekg::item::get_visible_count() { - return this->visible_count; -} - -void ekg::item::set_value(std::string_view new_value) { - this->value = new_value; -} - -std::string ekg::item::get_value() { - return this->value; -} - -void ekg::item::set_attr(ekg::flags bits) { - this->attr_bits = bits; -} - -ekg::flags ekg::item::get_attr() { - return this->attr_bits; -} - -void ekg::item::set_text_align(ekg::flags bits) { - if (this->placement.text_dock_flags != bits) { - this->placement.text_dock_flags = bits; - } -} - -ekg::flags ekg::item::get_text_align() { - return this->placement.text_dock_flags; -} - -/* end of item */ - -std::ostringstream ekg::log::buffer {}; -bool ekg::log::buffered {}; -bool ekg::log::tracked {}; - -int64_t ekg::timing::second {}; -int64_t ekg::timing::ticks {}; - -bool ekg::reach(ekg::timing &timing, int64_t ms) { - timing.ticks_going_on = ekg::timing::ticks; - timing.current_ticks = timing.ticks_going_on - timing.elapsed_ticks; - return timing.current_ticks > ms; -} - -bool ekg::reset(ekg::timing &timing) { - timing.elapsed_ticks = timing.ticks_going_on; - return true; -} - -bool ekg::extend(ekg::timing &timing, int64_t ms) { - timing.elapsed_ticks = timing.ticks_going_on - ms; - return true; -} - -int64_t ekg::interval(ekg::timing &timing) { - timing.ticks_going_on = ekg::timing::ticks; - return ( - timing.current_ticks = timing.ticks_going_on - timing.elapsed_ticks - ); -} - -bool ekg::file_to_string(std::string &file_content, std::string_view path) { - std::ifstream ifs {path.data()}; - if (ifs.is_open()) { - std::string line_content {}; - while (std::getline(ifs, line_content)) { - file_content += line_content; - file_content += '\n'; - } - - ifs.close(); - return false; - } else { - ekg::log() << "Failed to read file '" << path << "\'!"; - } - - return true; -} - -bool ekg::input::action(std::string_view key_input) { - return ekg::core->service_input.pressed(key_input); -} - -void ekg::input::bind(std::string_view key_input, std::string_view value_input) { - return ekg::core->service_input.bind(key_input, value_input); -} - -void ekg::input::bind(std::string_view key_input, const std::vector &bind_list) { - for (const std::string_view &bind : bind_list) { - ekg::input::bind(key_input, bind); - } -} - -void ekg::input::fire(std::string_view key_input) { - ekg::core->service_input.fire(key_input); -} - -void ekg::input::clipboard(ekg::clipboard clipboard_op) { - // meow -} - -bool ekg::input::motion() { - return ekg::core->service_input.has_motion; -} - -bool ekg::input::released() { - return ekg::core->service_input.was_released; -} - -bool ekg::input::pressed() { - return ekg::core->service_input.was_pressed; -} - -bool ekg::input::wheel() { - return ekg::core->service_input.was_wheel; -} - -bool ekg::input::receive(std::string_view tag) { - return ekg::core->service_input.receive(tag); -} - -bool ekg::input::typed() { - return ekg::core->service_input.was_typed; -} - -ekg::vec4 &ekg::input::interact() { - return ekg::core->service_input.interact; -} diff --git a/src/util/text.cpp b/src/util/text.cpp deleted file mode 100644 index 98efd1a1..00000000 --- a/src/util/text.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022-2024 Rina Wilk / vokegpu@gmail.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -#include "ekg/util/text.hpp" -#include "ekg/util/geometry.hpp" -#include "ekg/util/io.hpp" -#include "ekg/ui/abstract/ui_abstract.hpp" - -uint64_t ekg::utf_check_sequence( - uint8_t &char8, - char32_t &char32, - std::string &utf_string, - std::string_view string, - uint64_t index -) { - if (char8 <= 0x7F) { - utf_string = char8; - char32 = static_cast(char8); - return 0; - } else if ((char8 & 0xE0) == 0xC0) { - utf_string = string.substr(index, 2); - char32 = ekg::utf_string_to_char32(utf_string); - return 1; - } else if ((char8 & 0xF0) == 0xE0) { - utf_string = string.substr(index, 3); - char32 = ekg::utf_string_to_char32(utf_string); - return 2; - } else if ((char8 & 0xF8) == 0xF0) { - utf_string = string.substr(index, 4); - char32 = ekg::utf_string_to_char32(utf_string); - return 3; - } - - return 0; -} - -std::string ekg::utf_char32_to_string(char32_t char32) { - std::string result {}; - - if (char32 < 0x80) { - result.reserve(1); - result[0] = static_cast(char32); - } else if (char32 < 0x800) { - result.reserve(2); - result[0] += static_cast(0xC0 | (char32 >> 6)); - result[1] += static_cast(0x80 | (char32 & 0x3F)); - } else if (char32 < 0x10000) { - result.reserve(3); - result[0] += static_cast(0xE0 | (char32 >> 12)); - result[1] += static_cast(0x80 | ((char32 >> 6) & 0x3F)); - result[2] += static_cast(0x80 | (char32 & 0x3F)); - } else if (char32 < 0x110000) { - result.reserve(4); - result[0] = static_cast(0xF0 | (char32 >> 18)); - result[1] = static_cast(0x80 | ((char32 >> 12) & 0x3F)); - result[2] = static_cast(0x80 | ((char32 >> 6) & 0x3F)); - result[3] = static_cast(0x80 | (char32 & 0x3F)); - } - - return result; -} - -char32_t ekg::utf_string_to_char32(std::string_view string) { - char32_t char32 {}; - - uint64_t it {}; - uint8_t char8 {static_cast(string.at(0))}; - - if (char8 <= 0x7F) { - char32 = char8; - } else if (char8 <= 0xDF) { - char32 = char8 & 0x1F; - char32 = (char32 << 6) | (string.at(++it) & 0x3F); - } else if (char8 <= 0xEF) { - char32 = char8 & 0x0F; - char32 = (char32 << 6) | (string.at(++it) & 0x3F); - char32 = (char32 << 6) | (string.at(++it) & 0x3F); - } else { - char32 = char8 & 0x07; - char32 = (char32 << 6) | (string.at(++it) & 0x3F); - char32 = (char32 << 6) | (string.at(++it) & 0x3F); - char32 = (char32 << 6) | (string.at(++it) & 0x3F); - } - - return char32; -} - -uint64_t ekg::utf_length(std::string_view utf_string) { - if (utf_string.empty()) { - return 0; - } - - uint64_t string_size {}; - uint8_t char8 {}; - - for (uint64_t it {}; it < utf_string.size(); it++) { - char8 = static_cast(utf_string.at(it)); - if (char8 == '\n' || char8 == '\r') { - continue; - } else if (char8 <= 0x7F) { - string_size++; - } else if ((char8 & 0xE0) == 0xC0) { - string_size++; - it++; - } else if ((char8 & 0xF0) == 0xE0) { - string_size++; - it += 2; - } else if ((char8 & 0xF8) == 0xF0) { - string_size++; - it += 3; - } - } - - return string_size; -} - -std::string ekg::utf_substr(std::string_view string, uint64_t offset, uint64_t size) { - if (string.empty() || size == 0) { - return ""; - } - - uint64_t string_size {string.size()}; - uint64_t utf_text_size {}; - - //offset = offset > string_size ? string_size : offset; - size += offset; - - uint64_t index {}; - uint64_t utf_sequence_size {}; - uint64_t begin {UINT64_MAX}; - uint8_t char8 {}; - bool at_last_index {}; - - /* - * This function implementation checks the amount of bytes per char for UTF-8. - * There is no support for UTF-16 or directly UTF-32. - */ - - while (index < string_size) { - char8 = static_cast(string.at(index)); - - utf_sequence_size = 1; - utf_sequence_size += ((char8 & 0xE0) == 0xC0); - utf_sequence_size += 2 * ((char8 & 0xF0) == 0xE0); - utf_sequence_size += 3 * ((char8 & 0xF8) == 0xF0); - - at_last_index = index + utf_sequence_size == string_size; - - if ((at_last_index || utf_text_size >= offset) && begin == UINT64_MAX) { - begin = ekg_max(index, string_size); - } - - index += utf_sequence_size; - utf_text_size++; - - if ( - /** - * OBS: - * If the `offset` parameter is equals to the last UTF-8 string size, - * then it continue without substring process. - **/ - (at_last_index && begin != UINT64_MAX && offset != utf_text_size) || - (utf_text_size >= size) - ) { - string = string.substr(begin, (index - begin)); - return std::string {string.begin(), string.end()}; - } - } - - return ""; -} - -void ekg::utf_decode(std::string_view string, std::vector &utf8_read) { - if (string.empty()) { - return; - } - - /* if the first element is empty, then the decode must start from it */ - if (utf8_read.size() == 1 && utf8_read.at(0).empty()) { - utf8_read.clear(); - } - - uint64_t index {}; - uint64_t start_index {}; - - while ((index = string.find('\n', start_index)) != std::string_view::npos) { - utf8_read.emplace_back( - string.substr( - start_index, - (index - start_index) - (index > 0 && string.at(index - 1) == '\r') - ) - ); - - start_index = index + 1; - } - - if (!string.empty() && - !( - string = string.substr(start_index, string.find('\0', start_index)) - ).empty() - ) { - // `meow\n\0`, so the decode wont emplace a empty string. - utf8_read.emplace_back(string); - } -} - -std::string ekg::string_float64_precision(double n, int32_t precision) { - std::string to_string_result {std::to_string(n)}; - return to_string_result.substr( - 0, - ekg_max( - static_cast(to_string_result.find('.') + precision + (1 * precision)), - static_cast(to_string_result.size()) - ) - ); -} - -std::string ekg::string_float_precision(float n, int32_t precision) { - std::string to_string_result {std::to_string(n)}; - return to_string_result.substr( - 0, - ekg_max( - static_cast(to_string_result.find('.') + precision + (1 * precision)), - static_cast(to_string_result.size()) - ) - ); -} - -uint8_t ekg::check_attribute_flags(std::string_view text, ekg::flags &flags) { - for (uint8_t it {}; it < text.size(); it++) { - switch (text.at(it)) { - case '\t': - flags |= ekg::attr::separator; - break; - default: - return it; - } - } - - return 0; -} - -bool ekg::split( - std::vector *p_string_split_list, - const std::string &string, - char find_char -) { - if (p_string_split_list == nullptr) { - return false; - } - - std::stringstream ss(string); - std::string find_string {}; - - bool found_flag {}; - - while (std::getline(ss, find_string, find_char)) { - p_string_split_list->push_back(find_string); - found_flag = true; - } - - return found_flag; -} \ No newline at end of file diff --git a/version/commit.txt b/version/commit.txt index 3f1848ed..aee5a89f 100644 --- a/version/commit.txt +++ b/version/commit.txt @@ -155,9 +155,9 @@ -- CMake new features and d-vars: `EKG_FORCE_WINDOWS`, `EKG_FORCE_ANDROID` and `EKG_FORCE_LINUX`: -- - An else-if/elif condition that first match windows, android, and linux; -- - or forced d-var sequentially. --- Little optmization in `ekg::utf_decode` (break-line string decode to a vector of strings). +-- Little optmization in `ekg::utf8_split_new_line` (break-line string decode to a vector of strings). -- Fixed small pixel-escape from rendering, any non-concave draw when reached zero was forcing generate one pixel fragment, so it was rendering a small pixel. --- Fixed break-line decode: `ekg::utf_decode` was not emplacing back at 0 pos. +-- Fixed break-line decode: `ekg::utf8_split_new_line` was not emplacing back at 0 pos. -- Added column header font size and align for listbox. -- GLFW3 interface support. -- New key cross-interface for IO serialized events. @@ -234,27 +234,42 @@ -- Added WASM build-type compatibility. -- Fixed GLES3 font format, GLES3 is stricly about image format, so now for GLES3 (applies: Android, Emscripten, and desktop GLES3), texture is swizzle by CPU-side and not in the driver (like desktop OpenGL core-profile does). +2.0.0-deprecated 25/01/25 + +-- ~~Added properties, stack, and new IO features.~~ +-- ~~Added bitwise op on `io/memory.hpp` template.~~ +-- ~~Added properties overload operator for casting widget and descriptor.~~ +-- ~~Refactored and optimized input-service with addressed input bind without useless hash queries.~~ +-- ~~New individual-theme-scheme system for service theme.~~ +-- ~~Refactored font-renderer and moved part of implementations to `io/` package.~~ +-- ~~Refactored layout docknization, extentinzation, and scale. Separated into individuals headers.~~ +-- ~~Rendering Hardware-Interface (RHI): OpenGL module refactored.~~ +-- ~~OS Platform-Interface (OPI): SDL2, GLFW modules refactored.~~ +-- ~~Added floating point features and algebra linear features.~~ +-- ~~Properties can be cast to reference and ptr, both with different reasons and complete utility.~~ +-- ~~Added new reload, layout docknization, and scale update runtime operation tasks. ~~ +-- ~~Properties has a visible absolute rect to user-programmer.~~ +-- ~~Unsafe properties linked to the descriptor also.~~ +-- ~~Dynamic widget-text rendering via `ekg::value` wrapper.~~ +-- ~~Frame widget impl. on new memory-model.~~ +-- ~~Button widget impl. on new memory-model.~~ +-- ~~Label widget impl. on new memory-model.~~ +-- ~~Scrollbar widget impl. on new memory-model.~~ +-- ~~Scrollbar scrolling speed fix.~~ +-- ~~Moved callback-actions to descriptors instead as before in properties, also actions are type-based named `ekg::actions`.~~ +-- ~~Fixed pixel align on container docknization, margin layout and offset layout both are separed now.~~ + 2.0.0 25/01/25 --- Added properties, stack, and new IO features. --- Added bitwise op on `io/memory.hpp` template. --- Added properties overload operator for casting widget and descriptor. --- Refactored and optimized input-service with addressed input bind without useless hash queries. --- New individual-theme-scheme system for service theme. --- Refactored font-renderer and moved part of implementations to `io/` package. --- Refactored layout docknization, extentinzation, and scale. Separated into individuals headers. --- Rendering Hardware-Interface (RHI): OpenGL module refactored. --- OS Platform-Interface (OPI): SDL2, GLFW modules refactored. --- Added floating point features and algebra linear features. --- Properties can be cast to reference and ptr, both with different reasons and complete utility. --- Added new reload, layout docknization, and scale update runtime operation tasks. --- Properties has a visible absolute rect to user-programmer. --- Unsafe properties linked to the descriptor also. --- Dynamic widget-text rendering via `ekg::value` wrapper. --- Frame widget impl. on new memory-model. --- Button widget impl. on new memory-model. --- Label widget impl. on new memory-model. --- Scrollbar widget impl. on new memory-model. --- Scrollbar scrolling speed fix. --- Moved callback-actions to descriptors instead as before in properties, also actions are type-based named `ekg::actions`. --- Fixed pixel align on container docknization, margin layout and offset layout both are separed now. +-- IO Memory. +-- Memory pools fundamentals: `ekg::pool` it self. +-- Query `ekg::query` and make `ekg::make` fundamentals functions. +-- Base platform and base rendering-hardware interface. +-- Platform `SDL2`. +-- API `OpenGL`. +-- Allocator. +-- Font rendering. +-- Layout docknization, scalenize, extentnize, and mask. +-- Theme system. +-- Basic descriptors: callback, property, sampler, stack, button, frame. +-- Internal features as dispatch for callbacks.