diff --git a/cmake/properties.cmake b/cmake/properties.cmake index e9437bc8..e08821e6 100644 --- a/cmake/properties.cmake +++ b/cmake/properties.cmake @@ -1,4 +1,4 @@ -set(EKG_VERSION 2.3.1) +set(EKG_VERSION 2.5.0) set(EKG_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/include") if(CMAKE_TOOLCHAIN_FILE) diff --git a/include/ekg/core/pools.hpp b/include/ekg/core/pools.hpp index 2351e891..4a0e4c5d 100644 --- a/include/ekg/core/pools.hpp +++ b/include/ekg/core/pools.hpp @@ -44,11 +44,14 @@ #include "ekg/ui/scrollbar/scrollbar.hpp" #include "ekg/ui/scrollbar/widget.hpp" +#include "ekg/ui/slider/slider.hpp" +#include "ekg/ui/slider/widget.hpp" + namespace ekg::core { void registry(ekg::property_t &property); } -#define ekg_core_declare_widget_case_todo(descriptor_t, widget_descriptor_at, ekg_core_widget_todo) \ +#define ekg_core_widget_call_impl(descriptor_t, widget_descriptor_at, todo) \ case descriptor_t::type: { \ descriptor_t &descriptor { \ ekg::query(widget_descriptor_at) \ @@ -56,16 +59,17 @@ namespace ekg::core { if (descriptor == descriptor_t::not_found) { \ break; \ } \ - ekg_core_widget_todo \ + todo \ break; \ } -#define ekg_core_abstract_todo(widget_descriptor_type, widget_descriptor_at, ekg_core_widget_todo) \ +#define ekg_core_widget_call(widget_descriptor_type, widget_descriptor_at, todo) \ switch (widget_descriptor_type) { \ - ekg_core_declare_widget_case_todo(ekg::frame_t, widget_descriptor_at, ekg_core_widget_todo); \ - ekg_core_declare_widget_case_todo(ekg::button_t, widget_descriptor_at, ekg_core_widget_todo); \ - ekg_core_declare_widget_case_todo(ekg::label_t, widget_descriptor_at, ekg_core_widget_todo); \ - ekg_core_declare_widget_case_todo(ekg::scrollbar_t, widget_descriptor_at, ekg_core_widget_todo); \ + ekg_core_widget_call_impl(ekg::frame_t, widget_descriptor_at, todo); \ + ekg_core_widget_call_impl(ekg::button_t, widget_descriptor_at, todo); \ + ekg_core_widget_call_impl(ekg::label_t, widget_descriptor_at, todo); \ + ekg_core_widget_call_impl(ekg::scrollbar_t, widget_descriptor_at, todo); \ + ekg_core_widget_call_impl(ekg::slider_t, widget_descriptor_at, todo); \ } #define ekg_registry_widget(widget_descriptor_t, register_widget_pool, register_property_pool, is_container, register_settings) \ @@ -130,6 +134,9 @@ namespace ekg { ekg::pool scrollbar_property {}; ekg::pool scrollbar {}; + + ekg::pool slider_property {}; + ekg::pool slider {}; } pools; template @@ -167,6 +174,10 @@ namespace ekg { return ekg::io::any_static_cast( &ekg::pools.scrollbar_property.query(at) ); + case ekg::type::slider: + return ekg::io::any_static_cast( + &ekg::pools.slider_property.query(at) + ); } case ekg::type::button: return ekg::io::any_static_cast( @@ -184,6 +195,10 @@ namespace ekg { return ekg::io::any_static_cast( &ekg::pools.scrollbar.query(at) ); + case ekg::type::slider: + return ekg::io::any_static_cast( + &ekg::pools.slider.query(at) + ); } return t::not_found; @@ -250,6 +265,20 @@ namespace ekg { ); } + case ekg::type::slider: { + ekg_registry_widget( + ekg::slider_t, + ekg::pools.slider, + ekg::pools.slider_property, + false, + { + property.is_childnizate = false; + property.is_children_docknizable = false; + widget.color_scheme = global_theme.slider_color_scheme; + } + ); + } + case ekg::type::stack: { ekg::stack_t &stack { ekg::pools.stack.push_back( diff --git a/include/ekg/draw/allocator.hpp b/include/ekg/draw/allocator.hpp index c916b527..dcb2c85b 100644 --- a/include/ekg/draw/allocator.hpp +++ b/include/ekg/draw/allocator.hpp @@ -53,6 +53,10 @@ ekg_draw_allocator_pass(); \ }; +namespace ekg { + constexpr bool always_parented {true}; +} + namespace ekg::draw { class allocator { public: diff --git a/include/ekg/handler/theme.hpp b/include/ekg/handler/theme.hpp index 8bd49f3f..179cd9ec 100644 --- a/include/ekg/handler/theme.hpp +++ b/include/ekg/handler/theme.hpp @@ -28,6 +28,7 @@ #include "ekg/ui/frame/frame.hpp" #include "ekg/ui/label/label.hpp" #include "ekg/ui/scrollbar/scrollbar.hpp" +#include "ekg/ui/slider/slider.hpp" namespace ekg { struct theme_t { @@ -42,6 +43,7 @@ namespace ekg { ekg::frame_color_scheme_t frame_color_scheme {}; ekg::label_color_scheme_t label_color_scheme {}; ekg::scrollbar_color_scheme_t scrollbar_color_scheme {}; + ekg::slider_color_scheme_t slider_color_scheme {}; }; ekg::theme_t &theme(std::string tag = ""); diff --git a/include/ekg/io/descriptor.hpp b/include/ekg/io/descriptor.hpp index 66c05841..f9f43c15 100644 --- a/include/ekg/io/descriptor.hpp +++ b/include/ekg/io/descriptor.hpp @@ -36,7 +36,8 @@ namespace ekg { button = 5, scrollbar = 6, frame = 7, - label = 8 + slider = 8, + label = 9, }; } diff --git a/include/ekg/io/memory.hpp b/include/ekg/io/memory.hpp index ff0b80ee..d8e32fd3 100644 --- a/include/ekg/io/memory.hpp +++ b/include/ekg/io/memory.hpp @@ -71,6 +71,12 @@ namespace ekg { constexpr ekg::flags_t &put(ekg::flags_t &bits, t bit) { return (bits |= bit); } + + constexpr void assert(bool state, const char *p_msg = "") { + if (state) return; + if (p_msg) std::cout << "[EKG] assert failed: " << p_msg; + std::abort(); + } } /** @@ -158,6 +164,21 @@ namespace ekg { }; } +namespace ekg::io { + /** + * @TODO: add a complete docs here please. + **/ + template + constexpr t &any_static_cast(void *p_any) { + return *static_cast(p_any); + } + + template + constexpr t *any_static_cast_as_ptr(void *p_any) { + return static_cast(p_any); + } +} + /** * Value system. **/ @@ -169,6 +190,7 @@ namespace ekg { t *p {nullptr}; t previous {}; bool changed {}; + size_t type_info_hash {}; public: value() { this->ownership(nullptr); @@ -178,20 +200,30 @@ namespace ekg { this->ownership(p_address); this->changed = true; } + + template + value(s val) { + this->as() = ekg::io::any_static_cast(&val); + this->changed = true; + this->type_info_hash = typeid(s).hash_code(); + } value(t val) { this->get() = val; this->changed = true; + this->type_info_hash = typeid(t).hash_code(); } value(const char *p_char) { this->get() = p_char; this->changed = true; + this->type_info_hash = typeid(t).hash_code(); } bool set(const t &val) { this->get() = val; this->changed = true; + this->type_info_hash = typeid(t).hash_code(); return true; } @@ -199,8 +231,15 @@ namespace ekg { return this->p ? *this->p : this->val; } + template + void ownership(s *p_address) { + ownership(ekg::io::any_static_cast_as_ptr(p_address)); + this->type_info_hash = typeid(s).hash_code(); + } + void ownership(t *p_address) { if (p_address == nullptr) { + this->p = &val; return; } @@ -221,10 +260,39 @@ namespace ekg { return false; } + + template + bool was_changed_as() { + if (this->changed) { + this->changed = false; + return true; + } + + s &get {ekg::io::any_static_cast(this->get())}; + s &previous {ekg::io::any_static_cast(this->previous)}; + + if (previous != get) { + previous = get; + return true; + } + + return false; + } + + template + s &as() { + this->type_info_hash = typeid(s).hash_code(); + return ekg::io::any_static_cast(this->get()); + } + + size_t &get_type_info_hash() { + return this->type_info_hash; + } public: template - ekg::value &operator = (const s &val) { - this->get() = val; + ekg::value &operator = (s val) { + this->type_info_hash = typeid(s).hash_code(); + this->get() = ekg::io::any_static_cast(&val); this->changed = true; return *this; } @@ -246,14 +314,4 @@ namespace ekg { void unmap(void *pv_address); } -namespace ekg::io { - /** - * @TODO: add a complete docs here please. - **/ - template - constexpr t &any_static_cast(void *p_any) { - return *static_cast(p_any); - } -} - #endif diff --git a/include/ekg/ui/button/button.hpp b/include/ekg/ui/button/button.hpp index d0193e05..9101087a 100644 --- a/include/ekg/ui/button/button.hpp +++ b/include/ekg/ui/button/button.hpp @@ -48,20 +48,20 @@ namespace ekg { struct button_t { public: - struct check_t { + struct widget_t { public: - struct widget_t { - public: - ekg::rect_t rect_text {}; - ekg::rect_t rect_box {}; - }; + ekg::rect_t rect_text {}; + ekg::rect_t rect_box {}; + }; + + struct check_t { public: ekg::value text {}; ekg::value value {}; ekg::font font_size {ekg::font::medium}; ekg::flags_t box {ekg::dock::none}; ekg::flags_t dock {ekg::dock::left}; - ekg::button_t::check_t::widget_t widget {}; + ekg::button_t::widget_t widget {}; ekg::property_t::states_t states {}; ekg::at_array_t layers {}; ekg::at_array_t actions {}; diff --git a/include/ekg/ui/slider/slider.hpp b/include/ekg/ui/slider/slider.hpp new file mode 100644 index 00000000..85fe94c5 --- /dev/null +++ b/include/ekg/ui/slider/slider.hpp @@ -0,0 +1,91 @@ +/** + * 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_SLIDER_HPP +#define EKG_UI_SLIDER_HPP + +#include "ekg/io/descriptor.hpp" +#include "ekg/math/geometry.hpp" +#include "ekg/io/font.hpp" +#include "ekg/io/event.hpp" +#include "ekg/ui/property.hpp" + +namespace ekg { + struct slider_color_scheme_t { + public: + ekg::rgba_t background {}; + ekg::rgba_t outline {}; + ekg::rgba_t bar_background {}; + ekg::rgba_t bar_highlight {}; + ekg::rgba_t bar_active {}; + ekg::rgba_t bar_outline {}; + ekg::rgba_t text_foreground {}; + ekg::pixel_thickness_t bar_thickness {25}; + ekg::pixel_thickness_t bar_target_thickness {25}; + }; + + struct slider_t { + public: + struct widget_t { + public: + ekg::rect_t rect_bar {}; + ekg::rect_t rect_bar_progress {}; + ekg::rect_t rect_target {}; + ekg::rect_t rect_text {}; + std::string text {}; + ekg::property_t::states_t states {}; + char bytes[8] {}; + }; + + struct range_t { + public: + ekg::value value {}; + size_t precision {2}; + ekg::value min {}; + ekg::value max {}; + ekg::flags_t dock {ekg::dock::left}; + ekg::flags_t dock_text {ekg::dock::right}; + ekg::font font_size {ekg::font::medium}; + ekg::at_array_t layers {}; + ekg::at_array_t actions {}; + ekg::slider_t::widget_t widget {}; + }; + public: + static constexpr ekg::type type {ekg::type::slider}; + static ekg::slider_t not_found; + public: + ekg::at_t property_at {}; + public: + std::string tag {}; + ekg::rect_t rect {0.0f, 0.0f, 89.0f, 0.0f}; + ekg::flags_t dock {}; + std::vector ranges {}; + ekg::at_array_t layers {}; + ekg::at_array_t actions {}; + ekg::slider_color_scheme_t color_scheme {}; + public: + ekg_descriptor(ekg::slider_t); + }; +} + +#endif diff --git a/include/ekg/ui/slider/widget.hpp b/include/ekg/ui/slider/widget.hpp new file mode 100644 index 00000000..e982020f --- /dev/null +++ b/include/ekg/ui/slider/widget.hpp @@ -0,0 +1,151 @@ +/** + * 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_SLIDER_WIDGET_HPP +#define EKG_UI_SLIDER_WIDGET_HPP + +#include "slider.hpp" +#include "ekg/ui/property.hpp" +#include "ekg/io/event.hpp" + +namespace ekg::ui { + static const size_t f64 {typeid(double).hash_code()}; + static const size_t f32 {typeid(float).hash_code()}; + static const size_t i64 {typeid(int64_t).hash_code()}; + static const size_t u64 {typeid(uint64_t).hash_code()}; + static const size_t i32 {typeid(int32_t).hash_code()}; + static const size_t u32 {typeid(uint32_t).hash_code()}; + static const size_t i16 {typeid(int16_t).hash_code()}; + static const size_t u16 {typeid(uint16_t).hash_code()}; + static const size_t i8 {typeid(int8_t).hash_code()}; + static const size_t u8 {typeid(uint8_t).hash_code()}; +} + +/** + * This make easily the part of adding tasks like: drag, draw, etc. + **/ +#define ekg_ui_slider_range_task_impl(ekg_ui_slider_range, format_t_hash, format_t, todo) \ + if (hash == format_t_hash) { \ + format_t &value {ekg_ui_slider_range.value.as()}; \ + format_t &min {ekg_ui_slider_range.min.as()}; \ + format_t &max {ekg_ui_slider_range.max.as()}; \ + using number_t = format_t; \ + todo; \ + } + +/** + * This provides a map for most all number-types. + **/ +#define ekg_ui_slider_range_task(ekg_ui_slider_range, todo) \ + const size_t hash {ekg_ui_slider_range.value.get_type_info_hash()}; \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::f64, double, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::f32, float, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::i64, int64_t, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::u64, uint64_t, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::i32, int32_t, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::u32, uint32_t, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::i16, int16_t, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::u16, uint16_t, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::i8, int8_t, todo); \ + ekg_ui_slider_range_task_impl(ekg_ui_slider_range, ekg::ui::u8, uint8_t, todo); + +namespace ekg::ui { + template + void calculate_value_by_factor( + ekg::slider_t::range_t &range, + ekg::vec2_t &factor, + const ekg::axis &axis, + t &min, t &max, t &value + ) { + value = ekg::clamp(value, min, max); + switch (axis) { + case ekg::axis::horizontal: + if (static_cast(factor.x) == 0) { + value = min; + break; + } + + value = (factor.x / range.widget.rect_bar.w) * (max - min) + min; + break; + case ekg::axis::vertical: + if (static_cast(factor.y) == 0) { + value = min; + break; + } + + value = (factor.y / range.widget.rect_bar.h) * (max - min) + min; + break; + } + } + + template + void calculate_bar_progress_by_value( + ekg::slider_t::range_t &range, + const ekg::axis &axis, + t &min, t &max, t &value + ) { + value = ekg::clamp(value, min, max); + switch (axis) { + case ekg::axis::horizontal: + range.widget.rect_bar_progress.w = range.widget.rect_bar.w * (value - min) / (max - min); + range.widget.rect_target.x = range.widget.rect_bar_progress.w - (range.widget.rect_target.w * 0.5f); + break; + case ekg::axis::vertical: + range.widget.rect_bar_progress.h = range.widget.rect_bar.h * (value - min) / (max - min); + range.widget.rect_target.y = range.widget.rect_bar_progress.h - (range.widget.rect_target.h * 0.5f); + break; + } + } + + void reload( + ekg::property_t &property, + ekg::slider_t &slider + ); + + void event( + ekg::property_t &property, + ekg::slider_t &slider, + const ekg::io::stage &stage + ); + + void high_frequency( + ekg::property_t &property, + ekg::slider_t &slider + ); + + void pass( + ekg::property_t &property, + ekg::slider_t &slider + ); + + void buffering( + ekg::property_t &property, + ekg::slider_t &slider + ); + + void unmap( + ekg::slider_t &slider + ); +} + +#endif diff --git a/src/core/runtime.cpp b/src/core/runtime.cpp index 01218791..038a22c9 100644 --- a/src/core/runtime.cpp +++ b/src/core/runtime.cpp @@ -88,12 +88,14 @@ void ekg::core::swap(ekg::info_t &info) { ekg::p_core->collector.clear(); ekg::core::swap_collector(was_found, at); - if (was_found) { + if (ekg::p_core->top_level_stack.empty() && 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() ); + + ekg::io::dispatch(ekg::io::operation::docknize, property.at); } else { ekg::p_core->stack.insert( ekg::p_core->stack.begin(), @@ -125,7 +127,7 @@ void ekg::core::reload(ekg::info_t &info) { continue; } - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, ekg::ui::reload(property, descriptor); @@ -214,7 +216,7 @@ void ekg::core::scalenize(ekg::info_t &info) { continue; } - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, @@ -254,7 +256,7 @@ void ekg::core::poll_event() { (abs_widget.states.is_absolute || is_on_scrolling_timeout) ) { - ekg_core_abstract_todo( + ekg_core_widget_call( abs_widget.descriptor_at.flags, abs_widget.descriptor_at, @@ -294,7 +296,7 @@ void ekg::core::poll_event() { continue; } - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, @@ -347,7 +349,7 @@ void ekg::core::poll_event() { }; if (focused_property != ekg::property_t::not_found) { - ekg_core_abstract_todo( + ekg_core_widget_call( focused_at.flags, focused_at, diff --git a/src/draw/typography/font.cpp b/src/draw/typography/font.cpp index 90e79120..fc8ee67d 100644 --- a/src/draw/typography/font.cpp +++ b/src/draw/typography/font.cpp @@ -402,7 +402,7 @@ void ekg::draw::font::reload() { } this->text_height = static_cast(this->font_size); - this->offset_text_height = static_cast(this->text_height / 6) / 2; + this->offset_text_height = this->text_height / 6; ekg::p_core->p_gpu_api->gen_font_atlas_and_map_glyph( ekg::query(this->atlas_texture_sampler_at), diff --git a/src/ekg.cpp b/src/ekg.cpp index 3a8300df..ca1622f7 100644 --- a/src/ekg.cpp +++ b/src/ekg.cpp @@ -102,7 +102,7 @@ void ekg::update() { ekg::query(ekg::p_core->high_frequency.at(it)) }; - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, ekg::ui::high_frequency(property, descriptor); @@ -147,7 +147,7 @@ void ekg::render() { continue; } - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, diff --git a/src/handler/theme/handler.cpp b/src/handler/theme/handler.cpp index 4f0d3355..9b328012 100644 --- a/src/handler/theme/handler.cpp +++ b/src/handler/theme/handler.cpp @@ -65,6 +65,13 @@ void ekg::handler::theme::init() { light_pinky_theme.scrollbar_color_scheme.bar_highlight = {245, 169, 184, 50}; light_pinky_theme.scrollbar_color_scheme.bar_active = {245, 169, 184, 100}; + light_pinky_theme.slider_color_scheme.outline = {204, 204, 204, 0}; + light_pinky_theme.slider_color_scheme.background = {204, 204, 204, 30}; + light_pinky_theme.slider_color_scheme.bar_background = {204, 204, 204, 50}; + light_pinky_theme.slider_color_scheme.bar_highlight = {245, 169, 184, 50}; + light_pinky_theme.slider_color_scheme.bar_active = {245, 169, 184, 100}; + light_pinky_theme.slider_color_scheme.text_foreground = {141, 141, 141, 255}; + this->registry(light_pinky_theme.tag) = light_pinky_theme; this->set_current_theme(light_pinky_theme.tag); @@ -106,6 +113,13 @@ void ekg::handler::theme::init() { black_light_pinky_theme.scrollbar_color_scheme.bar_highlight = {245, 169, 184, 50}; black_light_pinky_theme.scrollbar_color_scheme.bar_active = {245, 169, 184, 100}; + black_light_pinky_theme.slider_color_scheme.background = {204, 204, 204, 50}; + black_light_pinky_theme.slider_color_scheme.outline = {204, 204, 204, 0}; + black_light_pinky_theme.slider_color_scheme.bar_background = {204, 204, 204, 50}; + black_light_pinky_theme.slider_color_scheme.bar_highlight = {245, 169, 184, 50}; + black_light_pinky_theme.slider_color_scheme.bar_active = {245, 169, 184, 100}; + black_light_pinky_theme.slider_color_scheme.text_foreground = {141, 141, 141, 255}; + this->registry(black_light_pinky_theme.tag) = black_light_pinky_theme; //this->set_current_theme(black_light_pinky_theme.tag); } diff --git a/src/io/memory.cpp b/src/io/memory.cpp index 442852d7..50ee3503 100644 --- a/src/io/memory.cpp +++ b/src/io/memory.cpp @@ -66,7 +66,7 @@ void ekg::unmap(void *pv_address) { 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_core_abstract_todo( + ekg_core_widget_call( at.flags, at, ekg::ui::unmap(descriptor); diff --git a/src/layout/docknize.cpp b/src/layout/docknize.cpp index b923c135..a63f2625 100644 --- a/src/layout/docknize.cpp +++ b/src/layout/docknize.cpp @@ -275,7 +275,7 @@ void ekg::layout::docknize_widget( } if (parent_property.widget.should_refresh_size) { - ekg_core_abstract_todo( + ekg_core_widget_call( parent_property.descriptor_at.flags, parent_property.descriptor_at, ekg::ui::reload(parent_property, descriptor); @@ -401,7 +401,7 @@ void ekg::layout::docknize_widget( property.widget.should_refresh_size = true; } - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, ekg::ui::reload(property, descriptor); @@ -570,7 +570,7 @@ void ekg::layout::docknize_widget( } max_previous_height = rect.h > max_previous_height ? rect.h : max_previous_height; - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, @@ -619,7 +619,7 @@ float ekg::layout::get_widget_height_by_children( continue; } - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, flags = descriptor.dock; diff --git a/src/layout/extentnize.cpp b/src/layout/extentnize.cpp index bd435090..77180497 100644 --- a/src/layout/extentnize.cpp +++ b/src/layout/extentnize.cpp @@ -184,7 +184,7 @@ void ekg::layout::extentnize_widget( is_scrollbar = property.descriptor_at.flags == ekg::type::scrollbar; is_last_index = it == latest_index; - ekg_core_abstract_todo( + ekg_core_widget_call( property.descriptor_at.flags, property.descriptor_at, dock = descriptor.dock; diff --git a/src/ui/abstract.cpp b/src/ui/abstract.cpp index 01b7309a..113468b9 100644 --- a/src/ui/abstract.cpp +++ b/src/ui/abstract.cpp @@ -41,7 +41,6 @@ ekg::rect_t &ekg::ui::get_abs_rect( ); } - void ekg::ui::pre_event( ekg::property_t &property, ekg::rect_t &descriptor_rect, diff --git a/src/ui/button/widget.cpp b/src/ui/button/widget.cpp index ceee91a5..7b54d572 100644 --- a/src/ui/button/widget.cpp +++ b/src/ui/button/widget.cpp @@ -75,7 +75,7 @@ void ekg::ui::reload( if (check.box == ekg::dock::none) { check.widget.rect_text.x = (check.widget.rect_text.w / 2) - (check.widget.rect_box.w / 2); - check.widget.rect_box.h = aligned_dimension.h; + //check.widget.rect_box.h = aligned_dimension.h; } } @@ -327,7 +327,7 @@ void ekg::ui::buffering( property.widget.rect_scissor, rect_abs, ekg::query(property.parent_at).widget.rect_scissor, - true + ekg::always_parented ); ekg::draw::rect( diff --git a/src/ui/label/widget.cpp b/src/ui/label/widget.cpp index 88ed144b..759af289 100644 --- a/src/ui/label/widget.cpp +++ b/src/ui/label/widget.cpp @@ -143,7 +143,7 @@ void ekg::ui::buffering( property.widget.rect_scissor, rect_abs, ekg::query(property.parent_at).widget.rect_scissor, - true + ekg::always_parented ); ekg::draw::rect( diff --git a/src/ui/scrollbar/widget.cpp b/src/ui/scrollbar/widget.cpp index 08f39d31..9a1497e3 100644 --- a/src/ui/scrollbar/widget.cpp +++ b/src/ui/scrollbar/widget.cpp @@ -141,7 +141,7 @@ void ekg::ui::reload( float place {}; for (ekg::at_t &at : children) { - ekg_core_abstract_todo( + ekg_core_widget_call( at.flags, at, @@ -640,7 +640,7 @@ void ekg::ui::buffering( property.widget.rect_scissor, rect_parent, rect_parent, - true + ekg::always_parented ); scrollbar.widget.rect_horizontal.w = 0.0f; diff --git a/src/ui/slider/slider.cpp b/src/ui/slider/slider.cpp new file mode 100644 index 00000000..76da2cf5 --- /dev/null +++ b/src/ui/slider/slider.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/slider/slider.hpp" + +ekg::slider_t ekg::slider_t::not_found { + .at = ekg::at_t::not_found +}; diff --git a/src/ui/slider/widget.cpp b/src/ui/slider/widget.cpp new file mode 100644 index 00000000..d1558028 --- /dev/null +++ b/src/ui/slider/widget.cpp @@ -0,0 +1,408 @@ +/** + * 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/slider/widget.hpp" +#include "ekg/draw/typography/font.hpp" +#include "ekg/draw/allocator.hpp" +#include "ekg/core/runtime.hpp" +#include "ekg/io/utf.hpp" +#include "ekg/core/context.hpp" +#include "ekg/layout/scalenize.hpp" +#include "ekg/ui/abstract.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/draw/shape/shape.hpp" + +void ekg::ui::reload( + ekg::property_t &property, + ekg::slider_t &slider +) { + constexpr ekg::pixel_t min_bar_width_factor {ekg::one_pixel * 4}; + ekg::pixel_t min_bar_width {slider.rect.w / min_bar_width_factor}; + + ekg::theme_t &global_theme {ekg::p_core->handler_theme.get_current_theme()}; + ekg::aligned_t aligned_dimension {}; + ekg::axis pick_axis {ekg::axis::horizontal}; + + slider.rect.scaled_height = ekg::max(slider.rect.scaled_height, ekg::one_pixel); + for (ekg::slider_t::range_t &range : slider.ranges) { + ekg::draw::font &draw_font {ekg::draw::get_font_renderer(range.font_size)}; + + ekg_ui_slider_range_task( + range, + { + { + ekg::utf8_number_precision( + range.widget.text, + min, + range.precision + ); + + range.widget.rect_text.w = ekg::max( + range.widget.rect_text.w, + draw_font.get_text_width(range.widget.text) + ); + } + + { + ekg::utf8_number_precision( + range.widget.text, + max, + range.precision + ); + + range.widget.rect_text.w = ekg::max( + range.widget.rect_text.w, + draw_font.get_text_width(range.widget.text) + ); + } + + ekg::utf8_number_precision( + range.widget.text, + value, + range.precision + ); + + range.widget.rect_text.h = draw_font.get_text_height(); + } + ); + + aligned_dimension = {}; + ekg::align_rect_dimension( + pick_axis, + range.widget.rect_text, + ekg::dpi.min_sizes, + aligned_dimension + ); + + aligned_dimension.h *= slider.rect.scaled_height; + slider.rect.h = ekg::max(slider.rect.h, aligned_dimension.h); + + range.widget.rect_bar.h = ( + aligned_dimension.h * (static_cast(slider.color_scheme.bar_thickness) / 100.0f) + ); + + range.widget.rect_target.h = ( + aligned_dimension.h * (static_cast(slider.color_scheme.bar_target_thickness) / 100.0f) + ); + + range.widget.rect_target.w = range.widget.rect_text.h; + range.widget.rect_bar.w = ekg::max(range.widget.rect_bar.w, min_bar_width); + } + + if (slider.ranges.empty()) { + ekg::rect_t empty {}; + empty.w = ekg::draw::get_font_renderer(ekg::font::medium).get_text_height(); + empty.h = empty.w; + + aligned_dimension = {}; + ekg::align_rect_dimension( + pick_axis, + empty, + ekg::dpi.min_sizes, + aligned_dimension + ); + + aligned_dimension.h *= slider.rect.scaled_height; + slider.rect.h = ekg::max(slider.rect.h, aligned_dimension.h); + } + + ekg::layout::mask mask {}; + mask.preset( + { + aligned_dimension.offset, + aligned_dimension.offset, + slider.rect.h + }, + pick_axis, + slider.rect.w + ); + + for (ekg::slider_t::range_t &range : slider.ranges) { + /** + * @TODO: a specialized issue for mask-reordering may with a new dock value. + * I will explicit set the order here, a temp solution while no dock-priority is done. + **/ + if (ekg::has(range.dock_text, ekg::dock::right)) { + mask.insert( + {.p_rect = &range.widget.rect_bar, .dock = range.dock} + ); + + mask.insert( + {.p_rect = &range.widget.rect_text, .dock = range.dock_text} + ); + } else if (ekg::has(range.dock_text, ekg::dock::left) || range.dock_text != ekg::dock::none) { + mask.insert( + {.p_rect = &range.widget.rect_text, .dock = range.dock_text} + ); + + mask.insert( + {.p_rect = &range.widget.rect_bar, .dock = range.dock} + ); + } else { + mask.insert( + {.p_rect = &range.widget.rect_bar, .dock = range.dock} + ); + } + } + + mask.docknize(); + + if (property.widget.should_refresh_size) { + slider.rect.w = ekg::min(slider.rect.w, mask.get_rect().w); + property.widget.should_refresh_size = false; + } + + property.widget.should_buffering = true; +} + +void ekg::ui::event( + ekg::property_t &property, + ekg::slider_t &slider, + const ekg::io::stage &stage +) { + switch (stage) { + case ekg::io::stage::process: { + ekg::input_info_t &input {ekg::p_core->handler_input.input}; + if (!input.was_pressed && !input.was_released && !input.has_motion) { + break; + } + + ekg::vec2_t interact {static_cast>(input.interact)}; + ekg::rect_t &rect_abs {ekg::ui::get_abs_rect(property, slider.rect)}; + + if (property.states.is_hovering || property.states.is_active) { + property.states.is_active = false; + ekg::rect_t bar {}; + + for (ekg::slider_t::range_t &range : slider.ranges) { + bar = range.widget.rect_bar + rect_abs; + range.widget.states.is_hovering = ekg::rect_collide_vec2(bar, interact); + + if (input.was_pressed && range.widget.states.is_hovering) { + ekg_action( + range.actions, + ekg::action::press, + (range.widget.states.is_active = true) + ); + } + + if (input.was_released && range.widget.states.is_active) { + ekg_action( + range.actions, + ekg::action::release, + (range.widget.states.is_active = false) + ); + } + + if (range.widget.states.is_active) { + ekg::vec2_t factor { + ekg::max(interact.x - bar.x, 0.0f), + ekg::max(interact.y - bar.y, 0.0f) + }; + + ekg_ui_slider_range_task( + range, + { + ekg::ui::calculate_value_by_factor( + range, + factor, + ekg::axis::horizontal, + min, max, value + ); + } + ); + + property.widget.should_buffering = true; + ekg::gui.ui.redraw = true; + property.states.is_active = true; + } + } + } + + break; + } + + case ekg::io::stage::pre: + ekg::ui::pre_event(property, slider.rect, false); + break; + case ekg::io::stage::post: + ekg::ui::post_event(property); + break; + } +} + +void ekg::ui::high_frequency( + ekg::property_t &property, + ekg::slider_t &slider +) { + +} + +void ekg::ui::pass( + ekg::property_t &property, + ekg::slider_t &slider +) { + ekg_draw_allocator_bind_local( + &property.widget.geometry_buffer, + &property.widget.gpu_data_buffer + ); + + for (ekg::slider_t::range_t &range : slider.ranges) { + ekg_ui_slider_range_task( + range, + { + if (range.value.was_changed_as()) { + property.widget.should_buffering = true; + break; + } + } + ); + + if (property.widget.should_buffering) { + return; + } + } + + if (property.widget.should_buffering) { + return; + } + + ekg_draw_allocator_pass(); +} + +void ekg::ui::buffering( + ekg::property_t &property, + ekg::slider_t &slider +) { + ekg::rect_t &rect_abs { + ekg::ui::get_abs_rect( + property, slider.rect + ) + }; + + ekg_draw_allocator_assert_scissor( + property.widget.rect_scissor, + rect_abs, + ekg::query(property.parent_at).widget.rect_scissor, + ekg::always_parented + ); + + ekg::draw::rect( + rect_abs, + slider.color_scheme.background, + ekg::draw::mode::fill, + slider.layers[ekg::layer::bg] + ); + + ekg::rect_t bar {}; + ekg::rect_t bar_progress {}; + ekg::vec2_t text {}; + float text_geometry_size {}; + + for (ekg::slider_t::range_t &range : slider.ranges) { + ekg::draw::font &draw_font {ekg::draw::get_font_renderer(range.font_size)}; + range.widget.rect_bar_progress = range.widget.rect_bar; + + ekg_ui_slider_range_task( + range, + { + ekg::ui::calculate_bar_progress_by_value( + range, + ekg::axis::horizontal, + min, max, value + ); + + ekg::utf8_number_precision( + range.widget.text, + value, + range.precision + ); + + text_geometry_size = draw_font.get_text_width(range.widget.text); + } + ); + + bar = range.widget.rect_bar + rect_abs; + bar_progress = range.widget.rect_bar_progress + rect_abs; + + ekg::draw::rect( + bar, + slider.color_scheme.bar_background, + ekg::draw::mode::fill, + range.layers[ekg::layer::bg] + ); + + if (range.widget.states.is_highlight) { + ekg::draw::rect( + bar, + slider.color_scheme.bar_highlight, + ekg::draw::mode::fill, + range.layers[ekg::layer::highlight_bg] + ); + } + + ekg::draw::rect( + bar_progress, + slider.color_scheme.bar_active, + ekg::draw::mode::fill, + range.layers[ekg::layer::active_bg] + ); + + if (range.dock_text != ekg::dock::none) { + text.x = ( + range.widget.rect_text.x + rect_abs.x + + + (range.widget.rect_text.w * 0.5f) - (text_geometry_size * 0.5f) + ); + + text.y = range.widget.rect_text.y + rect_abs.y; + + draw_font + .blit( + range.widget.text, + text.x, + text.y, + slider.color_scheme.text_foreground + ); + } + } + + ekg::draw::rect( + rect_abs, + slider.color_scheme.background, + ekg::draw::mode::outline, + slider.layers[ekg::layer::outline] + ); + + ekg_draw_allocator_pass(); +} + +void ekg::ui::unmap( + ekg::slider_t &slider +) { + for (ekg::slider_t::range_t &range : slider.ranges) { + range.value.ownership(nullptr); + range.min.ownership(nullptr); + range.max.ownership(nullptr); + } +}