diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e730ddd..2d267196 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. -# +# 1 # 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 diff --git a/cmake/properties.cmake b/cmake/properties.cmake index e08821e6..c228367e 100644 --- a/cmake/properties.cmake +++ b/cmake/properties.cmake @@ -1,4 +1,4 @@ -set(EKG_VERSION 2.5.0) +set(EKG_VERSION 2.6.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 4a0e4c5d..84a2c7c7 100644 --- a/include/ekg/core/pools.hpp +++ b/include/ekg/core/pools.hpp @@ -47,6 +47,9 @@ #include "ekg/ui/slider/slider.hpp" #include "ekg/ui/slider/widget.hpp" +#include "ekg/ui/popup/popup.hpp" +#include "ekg/ui/popup/widget.hpp" + namespace ekg::core { void registry(ekg::property_t &property); } @@ -59,20 +62,32 @@ namespace ekg::core { if (descriptor == descriptor_t::not_found) { \ break; \ } \ + ekg::core::widget_call_result = true; \ todo \ break; \ } +namespace ekg::core { + extern bool widget_call_result; +} + #define ekg_core_widget_call(widget_descriptor_type, widget_descriptor_at, todo) \ + ekg::core::widget_call_result = false; \ switch (widget_descriptor_type) { \ 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); \ + ekg_core_widget_call_impl(ekg::popup_t, widget_descriptor_at, todo); \ } -#define ekg_registry_widget(widget_descriptor_t, register_widget_pool, register_property_pool, is_container, register_settings) \ +#define ekg_core_unique_widget_call(descriptor_t, widget_descriptor_type, widget_descriptor_at, todo) \ + switch (widget_descriptor_type) { \ + ekg_core_widget_call_impl(descriptor_t, widget_descriptor_at, todo); \ + } + +#define ekg_registry_widget_impl(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) \ @@ -100,13 +115,13 @@ namespace ekg::core { ekg::property_t &parent {ekg::query(ekg::gui.bind.parent_at)}; \ if (is_container) { \ if (parent != ekg::property_t::not_found && widget.dock != ekg::dock::none) { \ + property.parent_at = parent.at; \ parent.children.push_back(widget.at); \ - property.parent_at = ekg::gui.bind.parent_at; \ property.abs_parent_at = parent.abs_parent_at; \ - } else { \ - ekg::gui.bind.parent_at = property.at; \ + } else if (parent == ekg::property_t::not_found) { \ property.abs_parent_at = property.at; \ } \ + ekg::gui.bind.parent_at = property.at; \ } else if (parent != ekg::property_t::not_found) { \ property.parent_at = ekg::gui.bind.parent_at; \ property.abs_parent_at = parent.abs_parent_at; \ @@ -137,6 +152,9 @@ namespace ekg { ekg::pool slider_property {}; ekg::pool slider {}; + + ekg::pool popup_property {}; + ekg::pool popup {}; } pools; template @@ -178,6 +196,10 @@ namespace ekg { return ekg::io::any_static_cast( &ekg::pools.slider_property.query(at) ); + case ekg::type::popup: + return ekg::io::any_static_cast( + &ekg::pools.popup_property.query(at) + ); } case ekg::type::button: return ekg::io::any_static_cast( @@ -199,6 +221,10 @@ namespace ekg { return ekg::io::any_static_cast( &ekg::pools.slider.query(at) ); + case ekg::type::popup: + return ekg::io::any_static_cast( + &ekg::pools.popup.query(at) + ); } return t::not_found; @@ -206,11 +232,11 @@ namespace ekg { template t &make( - t descriptor + t descriptor = {} ) { switch (t::type) { case ekg::type::frame: { - ekg_registry_widget( + ekg_registry_widget_impl( ekg::frame_t, ekg::pools.frame, ekg::pools.frame_property, @@ -224,7 +250,7 @@ namespace ekg { } case ekg::type::button: { - ekg_registry_widget( + ekg_registry_widget_impl( ekg::button_t, ekg::pools.button, ekg::pools.button_property, @@ -238,7 +264,7 @@ namespace ekg { } case ekg::type::label: { - ekg_registry_widget( + ekg_registry_widget_impl( ekg::label_t, ekg::pools.label, ekg::pools.label_property, @@ -252,7 +278,7 @@ namespace ekg { } case ekg::type::scrollbar: { - ekg_registry_widget( + ekg_registry_widget_impl( ekg::scrollbar_t, ekg::pools.scrollbar, ekg::pools.scrollbar_property, @@ -266,7 +292,7 @@ namespace ekg { } case ekg::type::slider: { - ekg_registry_widget( + ekg_registry_widget_impl( ekg::slider_t, ekg::pools.slider, ekg::pools.slider_property, @@ -279,6 +305,23 @@ namespace ekg { ); } + case ekg::type::popup: { + ekg_registry_widget_impl( + ekg::popup_t, + ekg::pools.popup, + ekg::pools.popup_property, + true, + { + property.is_childnizate = true; + property.is_stack_top_level = true; + property.is_children_docknizable = true; + property.states.is_visible = false; + + widget.color_scheme = global_theme.popup_color_scheme; + } + ); + } + case ekg::type::stack: { ekg::stack_t &stack { ekg::pools.stack.push_back( @@ -308,6 +351,24 @@ namespace ekg { return t::not_found; } + + template + void pop() { + switch (t::type) { + case ekg::type::stack: + ekg::gui.bind.stack_at = ekg::at_t::not_found; + break; + case ekg::type::property: + ekg::gui.bind.parent_at = ekg::query(ekg::gui.bind.parent_at).parent_at; + break; + case ekg::type::frame: + ekg::gui.bind.parent_at = ekg::query(ekg::gui.bind.parent_at).parent_at; + break; + case ekg::type::popup: + ekg::gui.bind.parent_at = ekg::query(ekg::gui.bind.parent_at).parent_at; + break; + } + } } #endif diff --git a/include/ekg/draw/allocator.hpp b/include/ekg/draw/allocator.hpp index dcb2c85b..985ad054 100644 --- a/include/ekg/draw/allocator.hpp +++ b/include/ekg/draw/allocator.hpp @@ -60,7 +60,7 @@ namespace ekg { namespace ekg::draw { class allocator { public: - static bool enable_high_priority; + static bool is_scissor_sync_allowed; static bool is_simple_shape; protected: size_t global_data_instance {}; diff --git a/include/ekg/handler/theme.hpp b/include/ekg/handler/theme.hpp index 179cd9ec..8756af0a 100644 --- a/include/ekg/handler/theme.hpp +++ b/include/ekg/handler/theme.hpp @@ -29,6 +29,7 @@ #include "ekg/ui/label/label.hpp" #include "ekg/ui/scrollbar/scrollbar.hpp" #include "ekg/ui/slider/slider.hpp" +#include "ekg/ui/popup/popup.hpp" namespace ekg { struct theme_t { @@ -44,6 +45,7 @@ namespace ekg { 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::popup_color_scheme_t popup_color_scheme {}; }; ekg::theme_t &theme(std::string tag = ""); diff --git a/include/ekg/io/descriptor.hpp b/include/ekg/io/descriptor.hpp index f9f43c15..32fbe07d 100644 --- a/include/ekg/io/descriptor.hpp +++ b/include/ekg/io/descriptor.hpp @@ -38,6 +38,7 @@ namespace ekg { frame = 7, slider = 8, label = 9, + popup = 10, }; } diff --git a/include/ekg/io/memory.hpp b/include/ekg/io/memory.hpp index d8e32fd3..4fff11c6 100644 --- a/include/ekg/io/memory.hpp +++ b/include/ekg/io/memory.hpp @@ -165,20 +165,70 @@ namespace ekg { } namespace ekg::io { + + constexpr bool strictly {true}; + constexpr bool non_strictly {false}; + /** * @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); } + + template + constexpr t &any_static_cast(void *p_any) { + return *ekg::io::any_static_cast_as_ptr(p_any); + } } +#define ekg_io_memory_ownership_impl(cast_type_t, p) \ + this->p = p; \ + this->changed = true; \ + this->type_info_hash = typeid(cast_type_t).hash_code(); + +#define ekg_io_memory_strictly_set_impl(cast_type_t, val) \ + this->get() = val; \ + this->changed = true; \ + this->type_info_hash = typeid(cast_type_t).hash_code(); + +#define ekg_io_memory_strictly_format_impl(type_t, cast_type_t) \ + value(cast_type_t val) { \ + ekg_io_memory_strictly_set_impl(cast_type_t, val); \ + } \ + cast_type_t &set(cast_type_t val) { \ + return (this->get() = val); \ + } \ + ekg::value &operator = (cast_type_t val) { \ + ekg_io_memory_strictly_set_impl(cast_type_t, val); \ + return *this; \ + } \ + operator cast_type_t () { \ + return this->get(); \ + } + +#define ekg_io_memory_unstrictly_set_impl(cast_type_t, val) \ + ekg::io::any_static_cast(&this->get()) = val; \ + this->changed = true; \ + this->type_info_hash = typeid(cast_type_t).hash_code(); + +#define ekg_io_memory_unstrictly_format_impl(type_t, cast_type_t) \ + value(cast_type_t val) { \ + ekg_io_memory_unstrictly_set_impl(cast_type_t, val); \ + } \ + cast_type_t &set(cast_type_t val) { \ + return (this->get() = val); \ + } \ + ekg::value &operator = (cast_type_t val) { \ + ekg_io_memory_unstrictly_set_impl(cast_type_t, val); \ + return *this; \ + } \ + operator cast_type_t () { \ + return this->get(); \ + } + /** * Value system. **/ @@ -192,60 +242,35 @@ namespace ekg { bool changed {}; size_t type_info_hash {}; public: - value() { - this->ownership(nullptr); - }; + value() {}; - value(t *p_address) { - this->ownership(p_address); - this->changed = true; + void ownership(t *p) { + ekg_io_memory_ownership_impl(t, p); } 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(); + void ownership(s *p) { + ekg_io_memory_ownership_impl(s, p); } - - bool set(const t &val) { - this->get() = val; - this->changed = true; - this->type_info_hash = typeid(t).hash_code(); - return true; - } - + + ekg_io_memory_strictly_format_impl(t, std::string); + ekg_io_memory_strictly_format_impl(t, const char*); + ekg_io_memory_strictly_format_impl(t, bool); + ekg_io_memory_unstrictly_format_impl(t, double); + ekg_io_memory_unstrictly_format_impl(t, float); + ekg_io_memory_unstrictly_format_impl(t, uint64_t); + ekg_io_memory_unstrictly_format_impl(t, int64_t); + ekg_io_memory_unstrictly_format_impl(t, uint32_t); + ekg_io_memory_unstrictly_format_impl(t, int32_t); + ekg_io_memory_unstrictly_format_impl(t, uint16_t); + ekg_io_memory_unstrictly_format_impl(t, int16_t); + ekg_io_memory_unstrictly_format_impl(t, uint8_t); + ekg_io_memory_unstrictly_format_impl(t, int8_t); + public: t &get() { 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; - } - - this->p = p_address; - } - bool was_changed() { if (this->changed) { this->changed = false; @@ -267,7 +292,7 @@ namespace ekg { this->changed = false; return true; } - + s &get {ekg::io::any_static_cast(this->get())}; s &previous {ekg::io::any_static_cast(this->previous)}; @@ -288,13 +313,10 @@ namespace ekg { size_t &get_type_info_hash() { return this->type_info_hash; } - public: - template - 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; + + void debug() { + ekg_log_low_level(this->p); + ekg_log_low_level(&this->val); } }; diff --git a/include/ekg/ui/frame/frame.hpp b/include/ekg/ui/frame/frame.hpp index d293c3bf..1e3ff1e7 100644 --- a/include/ekg/ui/frame/frame.hpp +++ b/include/ekg/ui/frame/frame.hpp @@ -27,11 +27,15 @@ #include "ekg/io/descriptor.hpp" #include "ekg/math/geometry.hpp" #include "ekg/io/event.hpp" +#include "ekg/core/context.hpp" namespace ekg { struct frame_color_scheme_t { public: ekg::pixel_thickness_t actions_margin_pixel_thickness {5}; + ekg::pixel_t popup_offset {2.0f}; + ekg::pixel_t max_popup_height {400.0f}; + bool popup_mode {}; ekg::pixel_t margin {2.0f}; ekg::rgba_t background {}; ekg::rgba_t highlight {}; @@ -61,9 +65,9 @@ namespace ekg { ekg::at_t property_at {}; public: std::string tag {}; - ekg::dock dock {ekg::dock::none}; - ekg::rect_t rect {}; - ekg::flags_t drag {}; + ekg::flags_t dock {ekg::dock::none}; + ekg::rect_t rect {20.0f, 20.0f, 200.0f, 200.0f}; + ekg::flags_t drag {ekg::dock::top}; ekg::flags_t resize {}; bool set_top_level {}; ekg::frame_color_scheme_t color_scheme {}; diff --git a/include/ekg/ui/label/label.hpp b/include/ekg/ui/label/label.hpp index f20e8369..3145fa8c 100644 --- a/include/ekg/ui/label/label.hpp +++ b/include/ekg/ui/label/label.hpp @@ -35,6 +35,7 @@ namespace ekg { ekg::rgba_t background {}; ekg::rgba_t outline {}; ekg::rgba_t text_foreground {}; + ekg::pixel_thickness_t separator_thickness {1}; }; struct label_t { @@ -43,6 +44,11 @@ namespace ekg { public: ekg::rect_t rect_text {}; }; + + enum class mode { + text, + separator + }; public: static constexpr ekg::type type {ekg::type::label}; static ekg::label_t not_found; @@ -55,6 +61,7 @@ namespace ekg { ekg::flags_t dock {}; ekg::flags_t dock_text {}; ekg::font font_size {ekg::font::medium}; + ekg::label_t::mode mode {ekg::label_t::mode::text}; ekg::at_array_t actions {}; ekg::at_array_t layers {}; ekg::label_t::widget_t widget {}; diff --git a/include/ekg/ui/popup/popup.hpp b/include/ekg/ui/popup/popup.hpp new file mode 100644 index 00000000..44de9ae3 --- /dev/null +++ b/include/ekg/ui/popup/popup.hpp @@ -0,0 +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_UI_POPUP_HPP +#define EKG_UI_POPUP_HPP + +#include "ekg/ui/frame/frame.hpp" +#include "ekg/ui/label/label.hpp" + +namespace ekg { + typedef ekg::frame_color_scheme_t popup_color_scheme_t; + + struct popup_t { + public: + struct widget_t { + public: + ekg::frame_t frame {}; + ekg::at_t popup_opened_at {ekg::at_t::not_found}; + bool was_visible {}; + bool just_opened {}; + bool should_self_recursive_destroy {}; + }; + + struct link_t { + public: + std::string tag {}; + ekg::at_t popup_at {}; + ekg::at_t focused_widget_at {}; + std::vector widget_ats {}; + }; + public: + static ekg::popup_t not_found; + static constexpr ekg::type type {ekg::type::popup}; + static constexpr ekg::flags_t auto_kill {2 << 2}; + static ekg::label_t separator; + public: + ekg::at_t parent_popup_at {}; + ekg::at_t property_at {}; + public: + std::string tag {}; + ekg::flags_t mode {}; + std::vector links {}; + ekg::rect_t rect {.w = 200.0f}; + ekg::flags_t dock {}; + ekg::popup_color_scheme_t color_scheme {}; + ekg::popup_t::widget_t widget {}; + public: + ekg_descriptor(ekg::popup_t); + }; + + void show( + ekg::at_t &popup_at, + const ekg::vec2_t &pos, + bool should_if = true + ); +} + +#endif diff --git a/include/ekg/ui/popup/widget.hpp b/include/ekg/ui/popup/widget.hpp new file mode 100644 index 00000000..9c10ebde --- /dev/null +++ b/include/ekg/ui/popup/widget.hpp @@ -0,0 +1,95 @@ +/** + * 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/popup/popup.hpp" +#include "ekg/ui/property.hpp" + +namespace ekg::ui { + void recursive_self_destroy_abs_popup( + ekg::popup_t &popup + ); + + void recursive_assert_set_current_open( + ekg::property_t &property, + ekg::at_t &opened, + ekg::at_t &to_open + ); + + void set_visible( + ekg::property_t &property, + bool visible + ); + + void recursive_children_set_visible( + ekg::popup_t &popup, + bool visible + ); + + void splash_popup_just_opened( + ekg::popup_t &popup, + const ekg::vec2_t &pos + ); + + void splash_popup_but_bounding( + float &popup_offset, + ekg::rect_t &rect_widget, + ekg::rect_t &rect_parent, + ekg::rect_t &rect_child + ); + + void recursive_sync_ats_by_finding_tag( + ekg::property_t &property, + std::vector &ats_to_sync, + std::string &tag + ); + + void reload( + ekg::property_t &property, + ekg::popup_t &popup + ); + + void event( + ekg::property_t &property, + ekg::popup_t &popup, + const ekg::io::stage &stage + ); + + void high_frequency( + ekg::property_t &property, + ekg::popup_t &popup + ); + + void pass( + ekg::property_t &property, + ekg::popup_t &popup + ); + + void buffering( + ekg::property_t &property, + ekg::popup_t &popup + ); + + void unmap( + ekg::popup_t &popup + ); +} diff --git a/include/ekg/ui/property.hpp b/include/ekg/ui/property.hpp index f7c0f3e3..e488bc60 100644 --- a/include/ekg/ui/property.hpp +++ b/include/ekg/ui/property.hpp @@ -41,6 +41,7 @@ namespace ekg { bool is_highlight {}; bool is_focused {}; bool is_warning {}; + bool is_sensitive {}; }; struct operation_t { @@ -81,6 +82,8 @@ namespace ekg { bool is_childnizate {}; bool is_children_docknizable {}; + bool is_stack_top_level {}; + std::vector children {}; public: ekg::property_t::states_t states {}; diff --git a/include/ekg/ui/scrollbar/widget.hpp b/include/ekg/ui/scrollbar/widget.hpp index 9a806748..d0c833a9 100644 --- a/include/ekg/ui/scrollbar/widget.hpp +++ b/include/ekg/ui/scrollbar/widget.hpp @@ -104,7 +104,8 @@ namespace ekg::ui { void buffering( ekg::property_t &property, ekg::scrollbar_t &scrollbar, - ekg::rect_t &rect_parent + ekg::rect_t &rect_parent, + ekg::rect_t &rect_parent_scissor ); void buffering( diff --git a/src/core/pools.cpp b/src/core/pools.cpp index 52147054..b5c1aa35 100644 --- a/src/core/pools.cpp +++ b/src/core/pools.cpp @@ -24,6 +24,8 @@ #include "ekg/core/pools.hpp" #include "ekg/core/runtime.hpp" +bool ekg::core::widget_call_result {}; + void ekg::core::registry(ekg::property_t &property) { ekg::p_core->registry.push_back(property.at); diff --git a/src/core/runtime.cpp b/src/core/runtime.cpp index 038a22c9..6917921c 100644 --- a/src/core/runtime.cpp +++ b/src/core/runtime.cpp @@ -42,6 +42,8 @@ void ekg::core::swap_collector( if ( parent_property.at == ekg::gui.bind.swap_at + || + parent_property.is_stack_top_level ) { was_found = true; } @@ -88,7 +90,7 @@ void ekg::core::swap(ekg::info_t &info) { ekg::p_core->collector.clear(); ekg::core::swap_collector(was_found, at); - if (ekg::p_core->top_level_stack.empty() && was_found) { + if (was_found) { ekg::p_core->top_level_stack.insert( ekg::p_core->top_level_stack.begin(), ekg::p_core->collector.begin(), @@ -96,13 +98,14 @@ void ekg::core::swap(ekg::info_t &info) { ); ekg::io::dispatch(ekg::io::operation::docknize, property.at); - } else { - ekg::p_core->stack.insert( - ekg::p_core->stack.begin(), - ekg::p_core->collector.begin(), - ekg::p_core->collector.end() - ); + continue; } + + 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( @@ -244,11 +247,6 @@ void ekg::core::poll_event() { }; 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 ( abs_widget != ekg::property_t::not_found @@ -343,6 +341,7 @@ void ekg::core::poll_event() { } ekg::gui.ui.hovered_type = ekg::type::unknown; + ekg::gui.ui.hovered_at = ekg::at_t::not_found; ekg::property_t &focused_property { ekg::query(focused_at) diff --git a/src/draw/allocator.cpp b/src/draw/allocator.cpp index 06dc01e5..5305b559 100644 --- a/src/draw/allocator.cpp +++ b/src/draw/allocator.cpp @@ -27,7 +27,7 @@ #include "ekg/io/log.hpp" #include "ekg/core/context.hpp" -bool ekg::draw::allocator::enable_high_priority {}; +bool ekg::draw::allocator::is_scissor_sync_allowed {true}; bool ekg::draw::allocator::is_simple_shape {}; void ekg::draw::allocator::init() { @@ -193,6 +193,10 @@ bool ekg::draw::allocator::sync_scissor( ekg::rect_t &rect_parent_scissor, bool is_parented ) { + if (!ekg::draw::allocator::is_scissor_sync_allowed) { + return true; + } + rect_scissor.x = rect_child.x; rect_scissor.y = rect_child.y; rect_scissor.w = rect_child.w; diff --git a/src/ekg.cpp b/src/ekg.cpp index ca1622f7..03b9ea72 100644 --- a/src/ekg.cpp +++ b/src/ekg.cpp @@ -81,7 +81,6 @@ ekg::flags_t ekg::init( ekg::core::scalenize(info); ekg::log() << "Successfully initialized"; - return ekg::result::success; } @@ -136,6 +135,8 @@ void ekg::render() { } if (ekg::gui.ui.redraw) { + ekg::gui.ui.redraw = false; + ekg::p_core->draw_allocator.invoke(); for (ekg::at_t &at : ekg::p_core->stack) { @@ -151,6 +152,14 @@ void ekg::render() { property.descriptor_at.flags, property.descriptor_at, + /** + * Sync the widget states and position by getting the abs. + **/ + ekg::ui::get_abs_rect(property, descriptor.rect); + if (!property.states.is_visible) { + break; + } + /** * @TODO: Fix this stupid glitch where scrolling is jittering because of something * @@ -168,8 +177,9 @@ void ekg::render() { /* */ ekg::ui::pass(property, descriptor); + if (!property.widget.should_buffering) { - continue; + break; } ekg::ui::buffering(property, descriptor); @@ -179,6 +189,5 @@ void ekg::render() { ekg::p_core->draw_allocator.revoke(); } - ekg::gui.ui.redraw = false; ekg::p_core->draw_allocator.to_gpu(); } diff --git a/src/handler/input/handler.cpp b/src/handler/input/handler.cpp index 656f618e..4b102a3f 100644 --- a/src/handler/input/handler.cpp +++ b/src/handler/input/handler.cpp @@ -194,6 +194,51 @@ void ekg::handler::input::quit() { } void ekg::handler::input::poll_event() { + if (this->input.was_wheel) { + this->set_input_state("mouse-wheel", false); + this->set_input_state("mouse-wheel-up", false); + this->set_input_state("mouse-wheel-down", false); + this->input.was_wheel = false; + } + + if (this->finger_swipe_event) { + this->set_input_state("finger-swipe", false); + this->set_input_state("finger-swipe-up", false); + this->set_input_state("finger-swipe-down", false); + this->finger_swipe_event = false; + } + + this->finger_hold_event = false; + + if (this->is_special_keys_released) { + for (std::string &units : this->special_keys_unit_pressed) { + this->set_input_state(units, false); + } + + this->special_keys_unit_pressed.clear(); + this->is_special_keys_released = false; + } + + if (!this->input_released_list.empty()) { + for (std::string &inputs: this->input_released_list) { + this->set_input_state(inputs, false); + } + + this->input_released_list.clear(); + } + + if (!this->just_fired_input_bind.empty()) { + for (bool *p_input_bind_state_address : this->just_fired_input_bind) { + if (!p_input_bind_state_address) { + continue; + } + + *p_input_bind_state_address = false; + } + + this->just_fired_input_bind.clear(); + } + this->input.was_pressed = false; this->input.was_released = false; this->input.has_motion = false; @@ -477,51 +522,6 @@ void ekg::handler::input::update() { ekg::reset_if_reach(this->input.ui_timing, 1000); ekg::timing_t::second = this->input.ui_timing.elapsed_ticks; - - if (this->input.was_wheel) { - this->set_input_state("mouse-wheel", false); - this->set_input_state("mouse-wheel-up", false); - this->set_input_state("mouse-wheel-down", false); - this->input.was_wheel = false; - } - - if (this->finger_swipe_event) { - this->set_input_state("finger-swipe", false); - this->set_input_state("finger-swipe-up", false); - this->set_input_state("finger-swipe-down", false); - this->finger_swipe_event = false; - } - - this->finger_hold_event = false; - - if (this->is_special_keys_released) { - for (std::string &units : this->special_keys_unit_pressed) { - this->set_input_state(units, false); - } - - this->special_keys_unit_pressed.clear(); - this->is_special_keys_released = false; - } - - if (!this->input_released_list.empty()) { - for (std::string &inputs: this->input_released_list) { - this->set_input_state(inputs, false); - } - - this->input_released_list.clear(); - } - - if (!this->just_fired_input_bind.empty()) { - for (bool *p_input_bind_state_address : this->just_fired_input_bind) { - if (!p_input_bind_state_address) { - continue; - } - - *p_input_bind_state_address = false; - } - - this->just_fired_input_bind.clear(); - } } void ekg::handler::input::insert_input_bind( diff --git a/src/handler/theme/handler.cpp b/src/handler/theme/handler.cpp index 9b328012..26c6deeb 100644 --- a/src/handler/theme/handler.cpp +++ b/src/handler/theme/handler.cpp @@ -30,7 +30,7 @@ void ekg::handler::theme::init() { ekg::theme_t light_pinky_theme { .tag = "light-pinky", .author = "Rina Wilk", - .description = "dim again, dim again, not even a light can undim my life, wasted love, wasted love...", + .description = "wasted love of my life, no love no more love no more" }; light_pinky_theme.layout_offset = 2.0f; @@ -72,20 +72,24 @@ void ekg::handler::theme::init() { light_pinky_theme.slider_color_scheme.bar_active = {245, 169, 184, 100}; light_pinky_theme.slider_color_scheme.text_foreground = {141, 141, 141, 255}; + light_pinky_theme.popup_color_scheme = light_pinky_theme.frame_color_scheme; + light_pinky_theme.popup_color_scheme.outline = {50, 50, 50, 100}; + light_pinky_theme.popup_color_scheme.popup_mode = true; + this->registry(light_pinky_theme.tag) = light_pinky_theme; this->set_current_theme(light_pinky_theme.tag); ekg::theme_t black_light_pinky_theme { .tag = "black-light-pinky", .author = "Rina Wilk", - .description = "yea i loved you alot, but you broken my heart, I can not do nothing, this is obscure, I miss you 2666, but I do not know how to live anymore", + .description = "loved a shitty person God save me" }; black_light_pinky_theme.layout_offset = 2.0f; black_light_pinky_theme.layout_margin_thickness = 2; black_light_pinky_theme.frame_color_scheme.background = {40, 40, 40, 255}; black_light_pinky_theme.frame_color_scheme.highlight = {242, 242, 242, 0}; - black_light_pinky_theme.frame_color_scheme.outline = {190, 190, 190, 0}; + black_light_pinky_theme.frame_color_scheme.outline = {190, 190, 190, 255}; black_light_pinky_theme.frame_color_scheme.active = {242, 242, 242, 0}; black_light_pinky_theme.frame_color_scheme.focused_background = {242, 242, 242, 0}; black_light_pinky_theme.frame_color_scheme.focused_outline = {242, 242, 242, 0}; @@ -120,6 +124,9 @@ void ekg::handler::theme::init() { 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}; + black_light_pinky_theme.popup_color_scheme = black_light_pinky_theme.frame_color_scheme; + black_light_pinky_theme.popup_color_scheme.popup_mode = true; + this->registry(black_light_pinky_theme.tag) = black_light_pinky_theme; //this->set_current_theme(black_light_pinky_theme.tag); } diff --git a/src/layout/docknize.cpp b/src/layout/docknize.cpp index a63f2625..0b43826f 100644 --- a/src/layout/docknize.cpp +++ b/src/layout/docknize.cpp @@ -444,12 +444,11 @@ void ekg::layout::docknize_widget( property.widget.min_size.x ); + align = ((dimensional_extent + current_global_theme.layout_offset) * count) + (extent); if (is_bottom) { - align = ((dimensional_extent + current_global_theme.layout_offset) * count) + (extent); align = (align - pixel_perfect_projection.w) * (align > pixel_perfect_projection.w) * (extent > 0.0f); align > 0.0f && (align = (align / count)); } else { - align = ((dimensional_extent + current_global_theme.layout_offset) * count) + (extent); align = (pixel_perfect_projection.w - align) * (align < pixel_perfect_projection.w) * (extent > 0.0f); align > 0.0f && (align = -(align / count)); } diff --git a/src/ui/abstract.cpp b/src/ui/abstract.cpp index 113468b9..4e82d2ca 100644 --- a/src/ui/abstract.cpp +++ b/src/ui/abstract.cpp @@ -34,6 +34,10 @@ ekg::rect_t &ekg::ui::get_abs_rect( ekg::query(property.parent_at) }; + if (parent != ekg::property_t::not_found) { + property.states.is_visible = parent.states.is_visible; + } + return ( property.widget.rect = ( descriptor_rect + parent.widget.rect + parent.scroll.position diff --git a/src/ui/button/widget.cpp b/src/ui/button/widget.cpp index 7b54d572..298c624f 100644 --- a/src/ui/button/widget.cpp +++ b/src/ui/button/widget.cpp @@ -201,6 +201,7 @@ void ekg::ui::event( ); if (!is_checkbox) { + property.states.is_sensitive = true; check.value.set(true); } } @@ -219,11 +220,18 @@ void ekg::ui::event( ekg_action( check.actions, ekg::action::active, - (is_checkbox ? (check.value.set(!check.value.get())) : (check.value.set(false))) + check.states.is_highlight + && + (is_checkbox ? (check.value.set(!check.value.get())) : (check.value.set(true))) && (is_active_any = true) ); + if (!is_checkbox) { + property.states.is_sensitive = false; + check.value.set(false); + } + ekg_action( check.actions, ekg::action::release, @@ -235,6 +243,8 @@ void ekg::ui::event( ); } } + + property.states.is_highlight = is_hovering_any; ekg_action( button.actions, diff --git a/src/ui/frame/widget.cpp b/src/ui/frame/widget.cpp index a607deb7..741b8b99 100644 --- a/src/ui/frame/widget.cpp +++ b/src/ui/frame/widget.cpp @@ -38,10 +38,24 @@ void ekg::ui::reload( ) { ekg::ui::get_abs_rect(property, frame.rect); - if (property.widget.should_refresh_size && static_cast(frame.rect.h) == 0) { + if ( + ( + property.widget.should_refresh_size + && + static_cast(frame.rect.h) == 0 + ) + || + frame.color_scheme.popup_mode + ) { frame.rect.h = ekg::layout::get_widget_height_by_children( property ); + + property.widget.should_buffering = true; + } + + if (frame.color_scheme.popup_mode) { + frame.rect.h = ekg::min(frame.rect.h, frame.color_scheme.max_popup_height); } property.widget.should_refresh_size = false; @@ -141,7 +155,7 @@ void ekg::ui::event( ); } else if (input.has_motion && property.states.is_active) { ekg::rect_t new_rect {rect}; - ekg::vec2_t interact {static_cast>(input.interact)}; + ekg::vec2_t interact {static_cast>(input.interact)}; resize_over_dock = frame.widget.target_dock_resize; @@ -214,8 +228,6 @@ void ekg::ui::event( ekg::gui.ui.redraw = true; } } else if ( - property.states.is_hovering - && frame.resize != ekg::dock::none && !property.states.is_active @@ -268,6 +280,7 @@ void ekg::ui::event( frame.widget.target_dock_resize = ekg::dock::none; frame.widget.target_dock_drag = ekg::dock::none; property.states.is_active = false; + property.states.is_absolute = false; } break; diff --git a/src/ui/label/widget.cpp b/src/ui/label/widget.cpp index 759af289..ed07ec5c 100644 --- a/src/ui/label/widget.cpp +++ b/src/ui/label/widget.cpp @@ -40,51 +40,62 @@ void ekg::ui::reload( label.rect ); - ekg::axis pick_axis { - ekg::axis::horizontal - }; - - ekg::draw::font &draw_font { - ekg::draw::get_font_renderer(label.font_size) - }; - - label.widget.rect_text.w = draw_font.get_text_width(label.text.get()); - label.widget.rect_text.h = draw_font.get_text_height(); - - ekg::aligned_t aligned_dimension {}; - ekg::align_rect_dimension( - pick_axis, - label.widget.rect_text, - ekg::dpi.min_sizes, - aligned_dimension - ); - - label.rect.scaled_height = ekg::max(1, label.rect.scaled_height); - label.rect.h = aligned_dimension.h * label.rect.scaled_height; - - ekg::layout::mask mask {}; - mask.preset( - { - aligned_dimension.offset, - aligned_dimension.offset, - label.rect.h - }, - pick_axis, - label.rect.w - ); - - mask.insert( - { - .p_rect = &label.widget.rect_text, - .dock = label.dock_text + switch (label.mode) { + case ekg::label_t::mode::text: { + ekg::axis pick_axis { + ekg::axis::horizontal + }; + + ekg::draw::font &draw_font { + ekg::draw::get_font_renderer(label.font_size) + }; + + label.widget.rect_text.w = draw_font.get_text_width(label.text.get()); + label.widget.rect_text.h = draw_font.get_text_height(); + + ekg::aligned_t aligned_dimension {}; + ekg::align_rect_dimension( + pick_axis, + label.widget.rect_text, + ekg::dpi.min_sizes, + aligned_dimension + ); + + label.rect.scaled_height = ekg::max(1, label.rect.scaled_height); + label.rect.h = aligned_dimension.h * label.rect.scaled_height; + + ekg::layout::mask mask {}; + mask.preset( + { + aligned_dimension.offset, + aligned_dimension.offset, + label.rect.h + }, + pick_axis, + label.rect.w + ); + + mask.insert( + { + .p_rect = &label.widget.rect_text, + .dock = label.dock_text + } + ); + + mask.docknize(); + + if (property.widget.should_refresh_size) { + label.rect.w = ekg::max(ekg::dpi.min_sizes, mask.get_rect().w); + property.widget.should_refresh_size = false; + } + + break; } - ); - mask.docknize(); - - if (property.widget.should_refresh_size) { - label.rect.w = ekg::max(ekg::dpi.min_sizes, mask.get_rect().w); - property.widget.should_refresh_size = false; + case ekg::label_t::mode::separator: { + label.rect.h = static_cast(label.color_scheme.separator_thickness); + break; + } } } @@ -146,26 +157,38 @@ void ekg::ui::buffering( ekg::always_parented ); - ekg::draw::rect( - rect_abs, - label.color_scheme.background, - ekg::draw::mode::fill, - label.layers[ekg::layer::bg] - ); - - ekg::draw::get_font_renderer(label.font_size) - .blit( - label.text.get(), - rect_abs.x + label.widget.rect_text.x, rect_abs.y + label.widget.rect_text.y, - label.color_scheme.text_foreground + switch (label.mode) { + case ekg::label_t::mode::text: + ekg::draw::rect( + rect_abs, + label.color_scheme.background, + ekg::draw::mode::fill, + label.layers[ekg::layer::bg] ); - ekg::draw::rect( - rect_abs, - label.color_scheme.outline, - ekg::draw::mode::outline, - label.layers[ekg::layer::outline] - ); + ekg::draw::get_font_renderer(label.font_size) + .blit( + label.text.get(), + rect_abs.x + label.widget.rect_text.x, rect_abs.y + label.widget.rect_text.y, + label.color_scheme.text_foreground + ); + + ekg::draw::rect( + rect_abs, + label.color_scheme.outline, + ekg::draw::mode::outline, + label.layers[ekg::layer::outline] + ); + break; + case ekg::label_t::mode::separator: + ekg::draw::rect( + rect_abs, + label.color_scheme.text_foreground, + ekg::draw::mode::fill, + ekg::at_t::not_found + ); + break; + } ekg_draw_allocator_pass(); } diff --git a/src/ui/popup/popup.cpp b/src/ui/popup/popup.cpp new file mode 100644 index 00000000..3d51da05 --- /dev/null +++ b/src/ui/popup/popup.cpp @@ -0,0 +1,95 @@ +/** + * 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/popup/popup.hpp" +#include "ekg/ui/popup/widget.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/core/context.hpp" + +ekg::popup_t ekg::popup_t::not_found { + .at = ekg::at_t::not_found +}; + +ekg::label_t ekg::popup_t::separator { + .dock = ekg::dock::fill | ekg::dock::next, + .mode = ekg::label_t::mode::separator +}; + +void ekg::show( + ekg::at_t &popup_at, + const ekg::vec2_t &pos, + bool should_if +) { + ekg::popup_t &popup { + ekg::query(popup_at) + }; + + bool is_hovering_a_popup {false}; + ekg_core_widget_call( + ekg::gui.ui.hovered_type, + ekg::gui.ui.hovered_at, + { + /** + * Check if the hovered property is a popup. + **/ + ekg::property_t &wproperty {ekg::query(descriptor.property_at)}; + if ( + wproperty != ekg::property_t::not_found + && + wproperty.parent_at == ekg::at_t::not_found + && + wproperty.at.flags == ekg::type::popup + ) { + is_hovering_a_popup = true; + } + + /** + * If not, then, check if the abs parent from hovered is a popup. + **/ + ekg::property_t &wproperty_abs {ekg::query(wproperty.abs_parent_at)}; + if ( + !is_hovering_a_popup + && + wproperty_abs != ekg::property_t::not_found + && + wproperty_abs.at.flags == ekg::type::popup + ) { + is_hovering_a_popup = true; + } + } + ); + + if (!should_if || popup == ekg::popup_t::not_found || is_hovering_a_popup) { + return; + } + + ekg::ui::splash_popup_just_opened( + popup, + pos + ); + + ekg::io::dispatch( + ekg::io::operation::swap, + popup.property_at + ); +} diff --git a/src/ui/popup/widget.cpp b/src/ui/popup/widget.cpp new file mode 100644 index 00000000..8726b1ca --- /dev/null +++ b/src/ui/popup/widget.cpp @@ -0,0 +1,487 @@ +/** + * 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/popup/widget.hpp" +#include "ekg/ui/frame/widget.hpp" +#include "ekg/core/runtime.hpp" +#include "ekg/core/pools.hpp" +#include "ekg/core/context.hpp" + +void ekg::ui::recursive_self_destroy_abs_popup( + ekg::popup_t &popup +) { + if (popup == ekg::popup_t::not_found) { + return; + } + + ekg::property_t &property {ekg::query(popup.property_at)}; + ekg::ui::set_visible( + property, + false + ); + + property.widget.should_buffering = false; + + ekg::ui::recursive_self_destroy_abs_popup( + ekg::query(popup.parent_popup_at) + ); +} + +void ekg::ui::recursive_assert_set_current_open( + ekg::property_t &property, + ekg::at_t &opened, + ekg::at_t &to_open +) { + if ( + opened != ekg::at_t::not_found + && + opened != to_open + ) { + ekg::ui::recursive_children_set_visible( + ekg::query(opened), + false + ); + } + + opened = to_open; + ekg::ui::set_visible( + property, + true + ); +} + +void ekg::ui::set_visible( + ekg::property_t &property, + bool visible +) { + property.states.is_visible = visible; + property.widget.should_buffering = true; + ekg::gui.ui.redraw = true; +} + +void ekg::ui::recursive_children_set_visible( + ekg::popup_t &popup, + bool visible +) { + if (popup == ekg::popup_t::not_found) { + return; + } + + ekg::property_t &property {ekg::query(popup.property_at)}; + ekg::ui::set_visible(property, visible); + + for (ekg::popup_t::link_t &link : popup.links) { + ekg::ui::recursive_children_set_visible( + ekg::query(link.popup_at), + visible + ); + } +} + +void ekg::ui::splash_popup_just_opened( + ekg::popup_t &popup, + const ekg::vec2_t &pos +) { + ekg::property_t &property {ekg::query(popup.property_at)}; + if ( + popup == ekg::popup_t::not_found + || + property == ekg::property_t::not_found + ) { + return; + } + + ekg::ui::recursive_children_set_visible( + popup, + false + ); + + property.states.is_visible = true; + popup.widget.was_visible = true; + ekg::gui.ui.redraw = true; + property.widget.should_buffering = true; + popup.widget.just_opened = true; + property.states.is_absolute = true; + + ekg::vec2_t preview {}; + ekg::rect_t &rect {popup.widget.frame.rect}; + + preview.x = pos.x + rect.w; + rect.x = pos.x; + + if (preview.x > ekg::dpi.viewport.w) { + rect.x = pos.x - rect.w; + } + + preview.y = pos.y + rect.h; + rect.y = pos.y; + + if (preview.y > ekg::dpi.viewport.h) { + rect.y = ekg::dpi.viewport.h - rect.h; + } +} + +void ekg::ui::splash_popup_but_bounding( + float &popup_offset, + ekg::rect_t &rect_widget, + ekg::rect_t &rect_parent, + ekg::rect_t &rect_child +) { + ekg::rect_t preview {}; + + preview.w = rect_parent.x + rect_parent.w + popup_offset; + rect_child.x = preview.w; + + if (preview.w + rect_child.w > ekg::dpi.viewport.w) { + rect_child.x = rect_parent.x - rect_parent.w - popup_offset; + } + + preview.h = rect_widget.y; + rect_child.y = preview.h; + + if (preview.h + rect_child.h > ekg::dpi.viewport.h) { + preview.h += rect_child.h; + rect_child.y = rect_widget.y - (preview.h - ekg::dpi.viewport.h); + } +} + +void ekg::ui::recursive_sync_ats_by_finding_tag( + ekg::property_t &property, + std::vector &ats_to_sync, + std::string &tag +) { + if (property == ekg::property_t::not_found) { + return; + } + + for (ekg::at_t &at : property.children) { + ekg_core_widget_call( + at.flags, + at, + { + if (descriptor.tag == tag) { + ats_to_sync.push_back(at); + } + } + ); + + if (property.is_childnizate && property.is_children_docknizable) { + ekg::ui::recursive_sync_ats_by_finding_tag( + ekg::query(at), + ats_to_sync, + tag + ); + } + } +} + +void ekg::ui::reload( + ekg::property_t &property, + ekg::popup_t &popup +) { + popup.widget.frame.color_scheme = popup.color_scheme; + ekg::ui::reload(property, popup.widget.frame); +} + +void ekg::ui::event( + ekg::property_t &property, + ekg::popup_t &popup, + const ekg::io::stage &stage +) { + ekg::ui::event(property, popup.widget.frame, stage); + popup.rect = popup.widget.frame.rect; + popup.widget.frame.drag = ekg::dock::none; + popup.widget.frame.resize = ekg::dock::none; + + switch (stage) { + case ekg::io::stage::pre: { + ekg::input_info_t &input {ekg::p_core->handler_input.input}; + ekg::vec2_t interact {static_cast>(input.interact)}; + + bool skip_this_tick_self_destruction {}; + if ( + popup.widget.just_opened + && + input.was_released + ) { + skip_this_tick_self_destruction = true; + popup.widget.just_opened = false; + } + + if ( + ( + !input.has_motion + && + !input.was_pressed + && + !input.was_released + ) + || + !property.states.is_visible + || + popup.widget.just_opened + ) { + break; + } + + bool exists {}; + bool should_set_visibility {}; + bool should_unset_visibility {}; + bool is_linked_hovering {}; + bool is_this_popup_being_hovered {}; + bool is_hovering_any_linked_widget {}; + bool is_hovering_a_popup_sensitive_widget {}; + bool should_force_update_popup_position {}; + + ekg::rect_t rect_position {}; + bool is_hovering_a_popup {}; + ekg::at_t hovering_popup {}; + + ekg_core_widget_call( + ekg::gui.ui.hovered_type, + ekg::gui.ui.hovered_at, + { + is_hovering_a_popup = false; + hovering_popup = ekg::at_t::not_found; + + /** + * Check if the hovered property is a popup. + **/ + ekg::property_t &wproperty {ekg::query(descriptor.property_at)}; + is_hovering_a_popup_sensitive_widget = wproperty.states.is_sensitive; + + if ( + wproperty != ekg::property_t::not_found + && + wproperty.parent_at == ekg::at_t::not_found + && + wproperty.at.flags == ekg::type::popup + ) { + is_hovering_a_popup = true; + hovering_popup = wproperty.descriptor_at; + } + + /** + * If not, then, check if the abs parent from hovered is a popup. + **/ + ekg::property_t &wproperty_abs {ekg::query(wproperty.abs_parent_at)}; + if ( + !is_hovering_a_popup + && + wproperty_abs != ekg::property_t::not_found + && + wproperty_abs.at.flags == ekg::type::popup + ) { + is_hovering_a_popup = true; + hovering_popup = wproperty_abs.descriptor_at; + } + } + ); + + is_this_popup_being_hovered = hovering_popup == popup.at; + + for (ekg::popup_t::link_t &link : popup.links) { + exists = false; + is_linked_hovering = false; + + for (ekg::at_t &widget_at : link.widget_ats) { + ekg_core_widget_call( + widget_at.flags, + widget_at, + { + exists = true; + + ekg::property_t &wproperty {ekg::query(descriptor.property_at)}; + if (wproperty.states.is_highlight) { + should_set_visibility = true; + is_linked_hovering = true; + is_hovering_any_linked_widget = true; + rect_position = wproperty.widget.rect; + should_force_update_popup_position = link.focused_widget_at != descriptor.at; + link.focused_widget_at = descriptor.at; + break; + } + } + ); + + if (!ekg::core::widget_call_result) { + exists = false; + break; + } + } + + if (!exists) { + link.widget_ats.clear(); + ekg::ui::recursive_sync_ats_by_finding_tag( + property, + link.widget_ats, + link.tag + ); + continue; + } + + if ( + /** + * If the actual popup is being hovered + * and linked widget is not being hovered + * visibility can be unset! + **/ + ( + is_this_popup_being_hovered + && + !is_linked_hovering + && + ekg::gui.ui.hovered_type != ekg::type::popup + && + ekg::gui.ui.hovered_type != ekg::type::frame + && + ekg::gui.ui.hovered_type != ekg::type::scrollbar + ) + ){ + should_unset_visibility = true; + } + + ekg_core_unique_widget_call( + ekg::popup_t, + link.popup_at.flags, + link.popup_at, + { + ekg::property_t &wproperty {ekg::query(descriptor.property_at)}; + descriptor.parent_popup_at = popup.at; + + if (should_set_visibility) { + ekg::ui::recursive_assert_set_current_open( + property, + popup.widget.popup_opened_at, + descriptor.at + ); + + wproperty.states.is_visible = true; + should_set_visibility = false; + } + + if (should_unset_visibility) { + ekg::ui::recursive_children_set_visible( + descriptor, + false + ); + + popup.widget.popup_opened_at = ekg::at_t::not_found; + should_unset_visibility = false; + } + + if ( + descriptor.widget.was_visible != wproperty.states.is_visible + || + should_force_update_popup_position + ) { + ekg::ui::reload(wproperty, descriptor.widget.frame); + + ekg::ui::splash_popup_but_bounding( + popup.color_scheme.popup_offset, + rect_position, + popup.rect, + descriptor.widget.frame.rect + ); + + descriptor.widget.was_visible = wproperty.states.is_visible; + ekg::io::dispatch(ekg::io::operation::swap, wproperty.at); + } + } + ); + } + + if ( + ( + ( + input.was_released + && + ekg::gui.ui.hovered_type != ekg::type::popup + && + is_hovering_a_popup_sensitive_widget + && + is_this_popup_being_hovered + && + !is_hovering_any_linked_widget + ) + || + ( + (input.was_pressed || input.was_released) + && + !is_hovering_a_popup + ) + ) + && + !skip_this_tick_self_destruction + ) { + popup.widget.should_self_recursive_destroy = true; + ekg::io::dispatch(ekg::io::operation::high_frequency, property.at); + } + + property.states.is_absolute = false; + + break; + } + + default: + break; + } +} + +void ekg::ui::high_frequency( + ekg::property_t &property, + ekg::popup_t &popup +) { + if (popup.widget.should_self_recursive_destroy) { + ekg::ui::recursive_self_destroy_abs_popup( + popup + ); + + popup.widget.should_self_recursive_destroy = false; + } + + property.widget.is_high_frequency = false; +} + +void ekg::ui::pass( + ekg::property_t &property, + ekg::popup_t &popup +) { + ekg::ui::pass(property, popup.widget.frame); +} + +void ekg::ui::buffering( + ekg::property_t &property, + ekg::popup_t &popup +) { + // ekg::draw::allocator::is_scissor_sync_allowed = false; + popup.widget.frame.color_scheme = popup.color_scheme; + ekg::ui::buffering(property, popup.widget.frame); + // ekg::draw::allocator::is_scissor_sync_allowed = true; +} + +void ekg::ui::unmap( + ekg::popup_t &popup +) { + +} diff --git a/src/ui/scrollbar/widget.cpp b/src/ui/scrollbar/widget.cpp index 9a1497e3..0c261932 100644 --- a/src/ui/scrollbar/widget.cpp +++ b/src/ui/scrollbar/widget.cpp @@ -157,6 +157,7 @@ void ekg::ui::reload( ); } + scrollbar.rect.h += ekg::p_core->handler_theme.get_current_theme().layout_offset; scrollbar.acceleration.y += scrollbar.acceleration.x + (scrollbar.acceleration.x * 0.5f); ekg::ui::check_scrollbar(property, scrollbar, rect_parent); ekg::ui::clamp_scrollbar(property, scrollbar, rect_parent); @@ -634,12 +635,13 @@ void ekg::ui::pass( void ekg::ui::buffering( ekg::property_t &property, ekg::scrollbar_t &scrollbar, - ekg::rect_t &rect_parent + ekg::rect_t &rect_parent, + ekg::rect_t &rect_parent_scissor ) { ekg_draw_allocator_assert_scissor( property.widget.rect_scissor, rect_parent, - rect_parent, + rect_parent_scissor, ekg::always_parented ); @@ -841,10 +843,15 @@ void ekg::ui::buffering( ekg::property_t &property, ekg::scrollbar_t &scrollbar ) { + ekg::property_t &property_parent { + ekg::query(property.parent_at) + }; + ekg::ui::buffering( property, scrollbar, - ekg::query(property.parent_at).widget.rect_scissor + property_parent.widget.rect, + property_parent.widget.rect_scissor ); } diff --git a/src/ui/slider/widget.cpp b/src/ui/slider/widget.cpp index d1558028..afb73739 100644 --- a/src/ui/slider/widget.cpp +++ b/src/ui/slider/widget.cpp @@ -187,6 +187,8 @@ void ekg::ui::event( break; } + property.states.is_highlight = property.states.is_hovering; + ekg::vec2_t interact {static_cast>(input.interact)}; ekg::rect_t &rect_abs {ekg::ui::get_abs_rect(property, slider.rect)};