From d6736abbfa5db357fca9336a9cc0aea89c761f17 Mon Sep 17 00:00:00 2001 From: life4gal Date: Wed, 21 Aug 2024 14:08:55 +0800 Subject: [PATCH 001/258] `build`: Rename `gui` => `draw`. --- CMakeLists.txt | 30 ++--- src/{gui/gui.ixx => draw/draw.ixx} | 6 +- src/{gui => draw}/draw_list.ixx | 110 ++++++++-------- src/{gui => draw}/font.impl.ixx | 8 +- src/{gui => draw}/font.ixx | 4 +- src/prometheus.ixx | 4 +- unit_test/CMakeLists.txt | 117 +++++++++--------- unit_test/src/{gui => draw}/backend_dx11.cpp | 18 +-- unit_test/src/{gui => draw}/backend_dx12.cpp | 14 +-- unit_test/src/{gui => draw}/def.hpp | 6 +- .../src/{gui => draw}/dx_error_handler.hpp | 0 .../{gui => draw}/glfw_callback_handler.cpp | 0 unit_test/src/{gui => draw}/main_dx11.cpp | 8 +- unit_test/src/{gui => draw}/main_dx12.cpp | 8 +- unit_test/src/{gui => draw}/main_vulkan.cpp | 0 15 files changed, 168 insertions(+), 165 deletions(-) rename src/{gui/gui.ixx => draw/draw.ixx} (78%) rename src/{gui => draw}/draw_list.ixx (97%) rename src/{gui => draw}/font.impl.ixx (99%) rename src/{gui => draw}/font.ixx (96%) rename unit_test/src/{gui => draw}/backend_dx11.cpp (97%) rename unit_test/src/{gui => draw}/backend_dx12.cpp (98%) rename unit_test/src/{gui => draw}/def.hpp (69%) rename unit_test/src/{gui => draw}/dx_error_handler.hpp (100%) rename unit_test/src/{gui => draw}/glfw_callback_handler.cpp (100%) rename unit_test/src/{gui => draw}/main_dx11.cpp (96%) rename unit_test/src/{gui => draw}/main_dx12.cpp (98%) rename unit_test/src/{gui => draw}/main_vulkan.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f8b717a..d1b2e7f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -388,6 +388,15 @@ set( ${PROJECT_SOURCE_DIR}/src/coroutine/coroutine.ixx + # ========================= + # DRAW + # ========================= + + ${PROJECT_SOURCE_DIR}/src/draw/draw_list.ixx + ${PROJECT_SOURCE_DIR}/src/draw/font.ixx + + ${PROJECT_SOURCE_DIR}/src/draw/draw.ixx + # ========================= # ERROR # ========================= @@ -415,15 +424,6 @@ set( ${PROJECT_SOURCE_DIR}/src/functional/functional.ixx - # ========================= - # GUI - # ========================= - - ${PROJECT_SOURCE_DIR}/src/gui/draw_list.ixx - ${PROJECT_SOURCE_DIR}/src/gui/font.ixx - - ${PROJECT_SOURCE_DIR}/src/gui/gui.ixx - # ========================= # MEMORY # ========================= @@ -512,6 +512,12 @@ set( ${PROJECT_SOURCE_DIR}/src/concurrency/thread.impl.ixx ${PROJECT_SOURCE_DIR}/src/concurrency/unfair_mutex.impl.ixx + # ========================= + # DRAW + # ========================= + + ${PROJECT_SOURCE_DIR}/src/draw/font.impl.ixx + # ========================= # ERROR # ========================= @@ -520,12 +526,6 @@ set( ${PROJECT_SOURCE_DIR}/src/error/platform.impl.ixx ${PROJECT_SOURCE_DIR}/src/error/command_line.impl.ixx ${PROJECT_SOURCE_DIR}/src/error/instruction_set.impl.ixx - - # ========================= - # GUI - # ========================= - - ${PROJECT_SOURCE_DIR}/src/gui/font.impl.ixx ) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/src/gui/gui.ixx b/src/draw/draw.ixx similarity index 78% rename from src/gui/gui.ixx rename to src/draw/draw.ixx index 1d85b8ee..cfbb5e28 100644 --- a/src/gui/gui.ixx +++ b/src/draw/draw.ixx @@ -4,7 +4,7 @@ // found in the top-level directory of this distribution. #if GAL_PROMETHEUS_USE_MODULE -export module gal.prometheus.gui; +export module gal.prometheus.draw; export import :font; export import :draw_list; @@ -12,7 +12,7 @@ export import :draw_list; #else #pragma once -#include -#include +#include +#include #endif diff --git a/src/gui/draw_list.ixx b/src/draw/draw_list.ixx similarity index 97% rename from src/gui/draw_list.ixx rename to src/draw/draw_list.ixx index e8a065e1..4ff8e14b 100644 --- a/src/gui/draw_list.ixx +++ b/src/draw/draw_list.ixx @@ -3,11 +3,11 @@ // This file is subject to the license terms in the LICENSE file // found in the top-level directory of this distribution. -#if not defined(GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG) +#if not defined(GAL_PROMETHEUS_DRAW_LIST_DEBUG) #if defined(DEBUG) or defined(_DEBUG) -#define GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG 1 +#define GAL_PROMETHEUS_DRAW_LIST_DEBUG 1 #else -#define GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG 0 +#define GAL_PROMETHEUS_DRAW_LIST_DEBUG 0 #endif #endif @@ -48,7 +48,7 @@ import :font; #include #include -#if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG +#if GAL_PROMETHEUS_DRAW_LIST_DEBUG #include #include #include @@ -58,12 +58,12 @@ import :font; #include #include #include -#include +#include #include GAL_PROMETHEUS_ERROR_DEBUG_MODULE #endif -namespace gal::prometheus::gui +namespace gal::prometheus::draw { GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_BEGIN @@ -486,7 +486,7 @@ namespace gal::prometheus::gui using path_list_type = list_type; private: - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG using debug_vertex_range_type = std::pair; using debug_index_range_type = std::pair; @@ -521,13 +521,13 @@ namespace gal::prometheus::gui // 1: .clip_rect = {max(rect0.left, rect1.left), max(rect0.top, rect1.top), min(rect0.right, rect1.right), min(rect0.bottom, rect1.bottom)}, .index_offset = root_window_element_count + 6, .element_count = 6 (two triangles => 4/5/6-4/6/7) // 2: .clip_rect = {...}, .index_offset = root_window_element_count + 12, .element_count = 3 (one triangle => 8/9/10) command_list_type command_list_; - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG list_type command_message_; #endif vertex_list_type vertex_list_; index_list_type index_list_; - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_type debug_range_info_list_; // If you want to get the exact data (and not just a [begin, end] of range), // you have to `explicitly` call `bind_debug_info` after `all` operations are finished, @@ -542,7 +542,7 @@ namespace gal::prometheus::gui path_list_type path_list_; constexpr auto push_command( - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG std::string&& message #endif ) noexcept -> void @@ -560,7 +560,7 @@ namespace gal::prometheus::gui .element_count = 0 } ); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG command_message_.emplace_back(std::move(message)); #endif } @@ -584,7 +584,7 @@ namespace gal::prometheus::gui if (current_clip_rect != this_command_clip_rect_) { push_command( - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG std::format("[PUSH CLIP_RECT] {} -> {}", current_clip_rect, this_command_clip_rect_) #endif ); @@ -597,7 +597,7 @@ namespace gal::prometheus::gui if (current_texture_id != this_command_texture_id_) { push_command( - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG std::format("[PUSH TEXTURE_ID] [{}] -> [{}]", current_texture_id, this_command_texture_id_) #endif ); @@ -627,7 +627,7 @@ namespace gal::prometheus::gui ) { command_list_.pop_back(); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG command_message_.pop_back(); #endif return; @@ -1745,13 +1745,13 @@ namespace gal::prometheus::gui constexpr auto reset() noexcept -> void { command_list_.resize(0); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG command_message_.resize(0); #endif vertex_list_.resize(0); index_list_.resize(0); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.resize(0); debug_list_info_list_.resize(0); #endif @@ -1776,7 +1776,7 @@ namespace gal::prometheus::gui ); } - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG constexpr auto bind_debug_info() noexcept -> void { GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(debug_range_info_list_.size() == debug_list_info_list_.size()); @@ -1861,14 +1861,14 @@ namespace gal::prometheus::gui path_pin(from + point_type{.5f, .5f}); path_pin(to + point_type{.5f, .5f}); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::NONE, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -1896,14 +1896,14 @@ namespace gal::prometheus::gui path_pin(b); path_pin(c); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::CLOSED, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -1931,14 +1931,14 @@ namespace gal::prometheus::gui path_pin(b); path_pin(c); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -1964,14 +1964,14 @@ namespace gal::prometheus::gui path_rect(rect_type{rect.left_top() + point_type{.5f, .5f}, rect.right_bottom() - point_type{.5f, .5f}}, rounding, flag); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::CLOSED, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2007,7 +2007,7 @@ namespace gal::prometheus::gui return; } - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif @@ -2023,7 +2023,7 @@ namespace gal::prometheus::gui path_stroke(color); } - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2064,14 +2064,14 @@ namespace gal::prometheus::gui return; } - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif draw_rect_filled(rect, color_left_top, color_right_top, color_left_bottom, color_right_bottom); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2109,14 +2109,14 @@ namespace gal::prometheus::gui path_quadrilateral(p1, p2, p3, p4); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::CLOSED, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2142,14 +2142,14 @@ namespace gal::prometheus::gui path_quadrilateral(p1, p2, p3, p4); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2175,14 +2175,14 @@ namespace gal::prometheus::gui path_arc_n(circle, 0, std::numbers::pi_v * 2, segments); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::CLOSED, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2213,14 +2213,14 @@ namespace gal::prometheus::gui path_arc_elliptical_n(ellipse, 0, std::numbers::pi_v * 2, segments); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::CLOSED, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2258,14 +2258,14 @@ namespace gal::prometheus::gui path_arc_n(circle, 0, std::numbers::pi_v * 2, segments); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2296,14 +2296,14 @@ namespace gal::prometheus::gui path_arc_elliptical_n(ellipse, 0, std::numbers::pi_v * 2, segments); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2342,14 +2342,14 @@ namespace gal::prometheus::gui { path_arc_fast(circle, 0, DrawListSharedData::vertex_sample_points_count - 1); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::CLOSED, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2389,14 +2389,14 @@ namespace gal::prometheus::gui { path_arc_fast(circle, 0, DrawListSharedData::vertex_sample_points_count - 1); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2501,14 +2501,14 @@ namespace gal::prometheus::gui path_bezier_curve(p1, p2, p3, p4, segments); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::NONE, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2541,14 +2541,14 @@ namespace gal::prometheus::gui path_bezier_quadratic_curve(p1, p2, p3, segments); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif path_stroke(color, DrawFlag::NONE, thickness); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2576,14 +2576,14 @@ namespace gal::prometheus::gui if (color.alpha == 0) { return; } - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif draw_text(font, font_size, p, color, text, wrap_width); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2630,14 +2630,14 @@ namespace gal::prometheus::gui if (color.alpha == 0) { return; } - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif draw_image(texture_id, display_p1, display_p2, display_p3, display_p4, uv_p1, uv_p2, uv_p3, uv_p4, color); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} @@ -2701,14 +2701,14 @@ namespace gal::prometheus::gui return; } - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG const auto current_vertex_size = vertex_list_.size(); const auto current_index_size = index_list_.size(); #endif draw_image_rounded(texture_id, display_rect, uv_rect, color, rounding, flag); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG debug_range_info_list_.emplace_back( debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} diff --git a/src/gui/font.impl.ixx b/src/draw/font.impl.ixx similarity index 99% rename from src/gui/font.impl.ixx rename to src/draw/font.impl.ixx index 21acb9da..ed68458f 100644 --- a/src/gui/font.impl.ixx +++ b/src/draw/font.impl.ixx @@ -17,7 +17,7 @@ module; #define STB_RECT_PACK_IMPLEMENTATION #include -export module gal.prometheus.gui:font.impl; +export module gal.prometheus.draw:font.impl; import std; @@ -38,13 +38,13 @@ import :font; #include #include -#include +#include #endif namespace { - using namespace gal::prometheus::gui; + using namespace gal::prometheus::draw; // ReSharper disable once CppInconsistentNaming constexpr glyph_range_value_type simplified_chinese_common_accumulative_offsets_from_0x4e00[] @@ -112,7 +112,7 @@ namespace }; } -namespace gal::prometheus::gui +namespace gal::prometheus::draw { [[nodiscard]] auto glyph_range_latin() noexcept -> glyph_ranges_view_type { diff --git a/src/gui/font.ixx b/src/draw/font.ixx similarity index 96% rename from src/gui/font.ixx rename to src/draw/font.ixx index 07ddea98..93bfe79c 100644 --- a/src/gui/font.ixx +++ b/src/draw/font.ixx @@ -8,7 +8,7 @@ module; #include -export module gal.prometheus.gui:font; +export module gal.prometheus.draw:font; import std; import gal.prometheus.primitive; @@ -25,7 +25,7 @@ import gal.prometheus.primitive; #endif -namespace gal::prometheus::gui +namespace gal::prometheus::draw { GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_BEGIN diff --git a/src/prometheus.ixx b/src/prometheus.ixx index d4d3a0fe..8cd38a04 100644 --- a/src/prometheus.ixx +++ b/src/prometheus.ixx @@ -5,9 +5,9 @@ export import gal.prometheus.chars; export import gal.prometheus.command_line_parser; export import gal.prometheus.concurrency; export import gal.prometheus.coroutine; +export import gal.prometheus.draw; export import gal.prometheus.error; export import gal.prometheus.functional; -export import gal.prometheus.gui; export import gal.prometheus.memory; export import gal.prometheus.meta; export import gal.prometheus.numeric; @@ -24,9 +24,9 @@ export import gal.prometheus.wildcard; #include #include #include +#include #include #include -#include #include #include #include diff --git a/unit_test/CMakeLists.txt b/unit_test/CMakeLists.txt index 1a638437..03dc7dd9 100644 --- a/unit_test/CMakeLists.txt +++ b/unit_test/CMakeLists.txt @@ -45,6 +45,7 @@ target_link_libraries( add_test( NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) # assets path @@ -91,60 +92,62 @@ set(ASSETS_PATH_PIC ${CMAKE_SOURCE_DIR}/assets/pic.jpg) # ) #endif (Vulkan_FOUND) -add_executable( - ${PROJECT_NAME}-gui-dx12 - - ${PROJECT_SOURCE_DIR}/src/gui/glfw_callback_handler.cpp - ${PROJECT_SOURCE_DIR}/src/gui/backend_dx12.cpp - ${PROJECT_SOURCE_DIR}/src/gui/main_dx12.cpp -) - -target_compile_features( - ${PROJECT_NAME}-gui-dx12 - PUBLIC - cxx_std_23 -) - -target_link_libraries( - ${PROJECT_NAME}-gui-dx12 - PUBLIC - prometheus -) -link_3rd_library_stb(${PROJECT_NAME}-gui-dx12) -link_3rd_library_glfw(${PROJECT_NAME}-gui-dx12) - -target_compile_definitions( - ${PROJECT_NAME}-gui-dx12 - PUBLIC - - ASSETS_PATH_PIC="${ASSETS_PATH_PIC}" -) - -add_executable( - ${PROJECT_NAME}-gui-dx11 - - ${PROJECT_SOURCE_DIR}/src/gui/glfw_callback_handler.cpp - ${PROJECT_SOURCE_DIR}/src/gui/backend_dx11.cpp - ${PROJECT_SOURCE_DIR}/src/gui/main_dx11.cpp -) - -target_compile_features( - ${PROJECT_NAME}-gui-dx11 - PUBLIC - cxx_std_23 -) - -target_link_libraries( - ${PROJECT_NAME}-gui-dx11 - PUBLIC - prometheus -) -link_3rd_library_stb(${PROJECT_NAME}-gui-dx11) -link_3rd_library_glfw(${PROJECT_NAME}-gui-dx11) - -target_compile_definitions( - ${PROJECT_NAME}-gui-dx11 - PUBLIC - - ASSETS_PATH_PIC="${ASSETS_PATH_PIC}" -) +if (${PROJECT_NAME_PREFIX}PLATFORM_WINDOWS) + add_executable( + ${PROJECT_NAME}-dx12 + + ${PROJECT_SOURCE_DIR}/src/draw/glfw_callback_handler.cpp + ${PROJECT_SOURCE_DIR}/src/draw/backend_dx12.cpp + ${PROJECT_SOURCE_DIR}/src/draw/main_dx12.cpp + ) + + target_compile_features( + ${PROJECT_NAME}-dx12 + PUBLIC + cxx_std_23 + ) + + target_link_libraries( + ${PROJECT_NAME}-dx12 + PUBLIC + prometheus + ) + link_3rd_library_stb(${PROJECT_NAME}-dx12) + link_3rd_library_glfw(${PROJECT_NAME}-dx12) + + target_compile_definitions( + ${PROJECT_NAME}-dx12 + PUBLIC + + ASSETS_PATH_PIC="${ASSETS_PATH_PIC}" + ) + + add_executable( + ${PROJECT_NAME}-dx11 + + ${PROJECT_SOURCE_DIR}/src/draw/glfw_callback_handler.cpp + ${PROJECT_SOURCE_DIR}/src/draw/backend_dx11.cpp + ${PROJECT_SOURCE_DIR}/src/draw/main_dx11.cpp + ) + + target_compile_features( + ${PROJECT_NAME}-dx11 + PUBLIC + cxx_std_23 + ) + + target_link_libraries( + ${PROJECT_NAME}-dx11 + PUBLIC + prometheus + ) + link_3rd_library_stb(${PROJECT_NAME}-dx11) + link_3rd_library_glfw(${PROJECT_NAME}-dx11) + + target_compile_definitions( + ${PROJECT_NAME}-dx11 + PUBLIC + + ASSETS_PATH_PIC="${ASSETS_PATH_PIC}" + ) +endif (${PROJECT_NAME_PREFIX}PLATFORM_WINDOWS) diff --git a/unit_test/src/gui/backend_dx11.cpp b/unit_test/src/draw/backend_dx11.cpp similarity index 97% rename from unit_test/src/gui/backend_dx11.cpp rename to unit_test/src/draw/backend_dx11.cpp index 06a6e501..40ddfe45 100644 --- a/unit_test/src/gui/backend_dx11.cpp +++ b/unit_test/src/draw/backend_dx11.cpp @@ -26,8 +26,8 @@ extern double g_last_time; extern std::uint64_t g_frame_count; extern float g_fps; -extern std::shared_ptr g_draw_list_shared_data; -extern gui::DrawList g_draw_list; +extern std::shared_ptr g_draw_list_shared_data; +extern draw::DrawList g_draw_list; namespace { @@ -106,7 +106,7 @@ auto prometheus_init() -> void // print_time(); using functional::operators::operator|; - g_draw_list.draw_list_flag(gui::DrawListFlag::ANTI_ALIASED_LINE | gui::DrawListFlag::ANTI_ALIASED_FILL); + g_draw_list.draw_list_flag(draw::DrawListFlag::ANTI_ALIASED_LINE | draw::DrawListFlag::ANTI_ALIASED_FILL); g_draw_list.shared_data(g_draw_list_shared_data); // Create the blending setup @@ -375,7 +375,7 @@ auto prometheus_init() -> void // const auto load_font_texture_result = load_texture(reinterpret_cast(font_data), font_width, font_height, g_font_texture); assert(load_font_texture_result); - g_draw_list_shared_data->get_default_font().texture_id = reinterpret_cast(g_font_texture.Get()); + g_draw_list_shared_data->get_default_font().texture_id = reinterpret_cast(g_font_texture.Get()); } // Load additional picture texture @@ -472,9 +472,9 @@ auto prometheus_render() -> void // font texture g_draw_list.image(g_draw_list_shared_data->get_default_font().texture_id, {900, 20, 1200, 320}); - g_draw_list.image_rounded(reinterpret_cast(g_additional_picture_texture.Get()), {900, 350, 1200, 650}, 10); + g_draw_list.image_rounded(reinterpret_cast(g_additional_picture_texture.Get()), {900, 350, 1200, 650}, 10); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG g_draw_list.bind_debug_info(); #endif } @@ -493,7 +493,7 @@ auto prometheus_draw() -> void this_frame_vertex_count = static_cast(vertex_list.size()) + 5000; const D3D11_BUFFER_DESC buffer_desc{ - .ByteWidth = static_cast(this_frame_vertex_count * sizeof(gui::DrawList::vertex_type)), + .ByteWidth = static_cast(this_frame_vertex_count * sizeof(draw::DrawList::vertex_type)), .Usage = D3D11_USAGE_DYNAMIC, .BindFlags = D3D11_BIND_VERTEX_BUFFER, .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE, @@ -508,7 +508,7 @@ auto prometheus_draw() -> void this_frame_index_count = static_cast(index_list.size()) + 10000; const D3D11_BUFFER_DESC buffer_desc{ - .ByteWidth = static_cast(this_frame_index_count * sizeof(gui::DrawList::index_type)), + .ByteWidth = static_cast(this_frame_index_count * sizeof(draw::DrawList::index_type)), .Usage = D3D11_USAGE_DYNAMIC, .BindFlags = D3D11_BIND_INDEX_BUFFER, .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE, @@ -531,7 +531,7 @@ auto prometheus_draw() -> void std::ranges::transform( vertex_list, mapped_vertex, - [](const gui::DrawList::vertex_type& vertex) -> d3d_vertex_type + [](const draw::DrawList::vertex_type& vertex) -> d3d_vertex_type { // return { // .position = {vertex.position.x, vertex.position.y}, diff --git a/unit_test/src/gui/backend_dx12.cpp b/unit_test/src/draw/backend_dx12.cpp similarity index 98% rename from unit_test/src/gui/backend_dx12.cpp rename to unit_test/src/draw/backend_dx12.cpp index 2878bd1b..4e2f8cfb 100644 --- a/unit_test/src/gui/backend_dx12.cpp +++ b/unit_test/src/draw/backend_dx12.cpp @@ -28,8 +28,8 @@ extern double g_last_time; extern std::uint64_t g_frame_count; extern float g_fps; -extern std::shared_ptr g_draw_list_shared_data; -extern gui::DrawList g_draw_list; +extern std::shared_ptr g_draw_list_shared_data; +extern draw::DrawList g_draw_list; namespace { @@ -279,7 +279,7 @@ auto prometheus_init() -> void print_time(); using functional::operators::operator|; - g_draw_list.draw_list_flag(gui::DrawListFlag::ANTI_ALIASED_LINE | gui::DrawListFlag::ANTI_ALIASED_FILL); + g_draw_list.draw_list_flag(draw::DrawListFlag::ANTI_ALIASED_LINE | draw::DrawListFlag::ANTI_ALIASED_FILL); g_draw_list.shared_data(g_draw_list_shared_data); // Create the root signature @@ -578,7 +578,7 @@ auto prometheus_init() -> void ); assert(load_font_texture_result); - g_draw_list_shared_data->get_default_font().texture_id = static_cast(g_font_handle.ptr); + g_draw_list_shared_data->get_default_font().texture_id = static_cast(g_font_handle.ptr); } // Load additional picture texture @@ -683,9 +683,9 @@ auto prometheus_render() -> void // font texture g_draw_list.image(g_draw_list_shared_data->get_default_font().texture_id, {900, 20, 1200, 320}); - g_draw_list.image_rounded(static_cast(g_additional_picture_handle.ptr), {900, 350, 1200, 650}, 10); + g_draw_list.image_rounded(static_cast(g_additional_picture_handle.ptr), {900, 350, 1200, 650}, 10); - #if GAL_PROMETHEUS_GUI_DRAW_LIST_DEBUG + #if GAL_PROMETHEUS_DRAW_LIST_DEBUG g_draw_list.bind_debug_info(); #endif } @@ -781,7 +781,7 @@ auto prometheus_draw() -> void std::ranges::transform( vertex_list, mapped_vertex, - [](const gui::DrawList::vertex_type& vertex) -> d3d_vertex_type + [](const draw::DrawList::vertex_type& vertex) -> d3d_vertex_type { // return { // .position = {vertex.position.x, vertex.position.y}, diff --git a/unit_test/src/gui/def.hpp b/unit_test/src/draw/def.hpp similarity index 69% rename from unit_test/src/gui/def.hpp rename to unit_test/src/draw/def.hpp index 5400fe94..a1921e10 100644 --- a/unit_test/src/gui/def.hpp +++ b/unit_test/src/draw/def.hpp @@ -18,11 +18,11 @@ struct d3d_vertex_type std::uint32_t color; }; -using d3d_index_type = gal::prometheus::gui::DrawList::index_type; +using d3d_index_type = gal::prometheus::draw::DrawList::index_type; using d3d_projection_matrix_type = float[4][4]; -static_assert(sizeof(gal::prometheus::gui::DrawList::vertex_type) == sizeof(d3d_vertex_type)); -static_assert(sizeof(gal::prometheus::gui::DrawList::index_type) == sizeof(d3d_index_type)); +static_assert(sizeof(gal::prometheus::draw::DrawList::vertex_type) == sizeof(d3d_vertex_type)); +static_assert(sizeof(gal::prometheus::draw::DrawList::index_type) == sizeof(d3d_index_type)); inline auto print_time(const std::source_location& location = std::source_location::current()) noexcept -> void { diff --git a/unit_test/src/gui/dx_error_handler.hpp b/unit_test/src/draw/dx_error_handler.hpp similarity index 100% rename from unit_test/src/gui/dx_error_handler.hpp rename to unit_test/src/draw/dx_error_handler.hpp diff --git a/unit_test/src/gui/glfw_callback_handler.cpp b/unit_test/src/draw/glfw_callback_handler.cpp similarity index 100% rename from unit_test/src/gui/glfw_callback_handler.cpp rename to unit_test/src/draw/glfw_callback_handler.cpp diff --git a/unit_test/src/gui/main_dx11.cpp b/unit_test/src/draw/main_dx11.cpp similarity index 96% rename from unit_test/src/gui/main_dx11.cpp rename to unit_test/src/draw/main_dx11.cpp index 343d3e53..22e4ce62 100644 --- a/unit_test/src/gui/main_dx11.cpp +++ b/unit_test/src/draw/main_dx11.cpp @@ -32,8 +32,8 @@ double g_last_time = 0; std::uint64_t g_frame_count = 0; float g_fps = 0; -auto g_draw_list_shared_data = std::make_shared(); -gui::DrawList g_draw_list; +auto g_draw_list_shared_data = std::make_shared(); +draw::DrawList g_draw_list; extern auto glfw_callback_setup(GLFWwindow& w) -> void; @@ -101,8 +101,8 @@ int main(int, char**) return -1; } - const auto range = gui::glyph_range_simplified_chinese_common(); - g_draw_list_shared_data->set_default_font(gui::load_font(R"(C:\Windows\Fonts\msyh.ttc)", 18, range)); + const auto range = draw::glyph_range_simplified_chinese_common(); + g_draw_list_shared_data->set_default_font(draw::load_font(R"(C:\Windows\Fonts\msyh.ttc)", 18, range)); // Setup Platform/Renderer backends win32_init(*window); diff --git a/unit_test/src/gui/main_dx12.cpp b/unit_test/src/draw/main_dx12.cpp similarity index 98% rename from unit_test/src/gui/main_dx12.cpp rename to unit_test/src/draw/main_dx12.cpp index 3fb9d807..6e8a7189 100644 --- a/unit_test/src/gui/main_dx12.cpp +++ b/unit_test/src/draw/main_dx12.cpp @@ -43,8 +43,8 @@ double g_last_time = 0; std::uint64_t g_frame_count = 0; float g_fps = 0; -auto g_draw_list_shared_data = std::make_shared(); -gui::DrawList g_draw_list; +auto g_draw_list_shared_data = std::make_shared(); +draw::DrawList g_draw_list; extern auto glfw_callback_setup(GLFWwindow& w) -> void; @@ -136,8 +136,8 @@ int main(int, char**) return 1; } - const auto range = gui::glyph_range_simplified_chinese_common(); - g_draw_list_shared_data->set_default_font(gui::load_font(R"(C:\Windows\Fonts\msyh.ttc)", 18, range)); + const auto range = draw::glyph_range_simplified_chinese_common(); + g_draw_list_shared_data->set_default_font(draw::load_font(R"(C:\Windows\Fonts\msyh.ttc)", 18, range)); // Setup Platform/Renderer backends win32_init(*window); diff --git a/unit_test/src/gui/main_vulkan.cpp b/unit_test/src/draw/main_vulkan.cpp similarity index 100% rename from unit_test/src/gui/main_vulkan.cpp rename to unit_test/src/draw/main_vulkan.cpp From 563ddc30f703fabdd7d4ed1f7db35930652390b4 Mon Sep 17 00:00:00 2001 From: life4gal Date: Wed, 28 Aug 2024 17:54:41 +0800 Subject: [PATCH 002/258] `feat`: Add io.device (WIP). --- src/io/device.ixx | 357 ++++++++++++++++++++++++++++++++++++++++++++++ src/io/io.ixx | 16 +++ 2 files changed, 373 insertions(+) create mode 100644 src/io/device.ixx create mode 100644 src/io/io.ixx diff --git a/src/io/device.ixx b/src/io/device.ixx new file mode 100644 index 00000000..7bc8a853 --- /dev/null +++ b/src/io/device.ixx @@ -0,0 +1,357 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.io:device; + +import std; +GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE + +#else +#pragma once + +#include +#include +#include + +#include GAL_PROMETHEUS_ERROR_DEBUG_MODULE + +#endif + +namespace gal::prometheus::io +{ + GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_BEGIN + + enum class DeviceKey : std::uint16_t + { + NONE, + + // ======================================== + // keyboard <------------------------------------------------------- + // from left to right, top to bottom + // ======================================== + + KB_ESCAPE, + + // ================== + // function keys + + KB_F1, + KB_F2, + KB_F3, + KB_F4, + KB_F5, + KB_F6, + KB_F7, + KB_F8, + KB_F9, + KB_F10, + KB_F11, + FK_F12, + + // ================== + // main area + + // ` + KB_GRAVE_ACCENT, + KB_1, + KB_2, + KB_3, + KB_4, + KB_5, + KB_6, + KB_7, + KB_8, + KB_9, + KB_0, + // - OR _ + KB_MINUS, + // = OR + + KB_PLUS, + KB_BACKSPACE, + + KB_TAB, + KB_Q, + KB_W, + KB_E, + KB_R, + KB_T, + KB_Y, + KB_U, + KB_I, + KB_O, + KB_P, + // [ OR { + KB_LEFT_BRACKET, + // ] OR } + KB_RIGHT_BRACKET, + // \ OR | + KB_BACKSLASH, + + KB_CAPS_LOCK, + KB_A, + KB_S, + KB_D, + KB_F, + KB_G, + KB_H, + KB_J, + KB_K, + KB_L, + // ; OR : + KB_SEMICOLON, + // ' OR " + KB_QUOTATION, + KB_ENTER, + + KB_LEFT_SHIFT, + KB_Z, + KB_X, + KB_C, + KB_V, + KB_B, + KB_N, + KB_M, + // , OR < + KB_COMMA, + // . OR > + KB_PERIOD, + // / OR ? + KB_SLASH, + KB_RIGHT_SHIFT, + + KB_LEFT_CTRL, + KB_LEFT_SUPER, + KB_LEFT_ALT, + KB_SPACE, + KB_RIGHT_ALT, + KB_RIGHT_SUPER, + KB_MENU, + KB_RIGHT_CTRL, + + // ================== + // navigation keys + + KB_PAUSE, + KB_SCROLL_LOCK, + KB_PRINT_SCREEN, + + KB_INSERT, + KB_HOME, + KB_PAGE_UP, + KB_DELETE, + KB_END, + KB_PAGE_DOWN, + + KB_ARROW_UP, + KB_ARROW_LEFT, + KB_ARROW_DOWN, + KB_ARROW_RIGHT, + + // ================== + // Numeric Keypad + + KB_KEYPAD_NUM_LOCK, + // / + KB_KEYPAD_DIVIDE, + // * + KB_KEYPAD_MULTIPLY, + // - + KB_KEYPAD_MINUS, + + KB_KEYPAD_7, + KB_KEYPAD_8, + KB_KEYPAD_9, + KB_KEYPAD_PLUS, + + KB_KEYPAD_4, + KB_KEYPAD_5, + KB_KEYPAD_6, + + KB_KEYPAD_1, + KB_KEYPAD_2, + KB_KEYPAD_3, + KB_KEYPAD_ENTER, + + KB_KEYPAD_0, + KB_KEYPAD_DECIMAL, + + // ======================================== + // -------------------------------------------------------> keyboard + // ======================================== + + // incoming + }; + + enum class MouseButton : std::uint32_t + { + LEFT = 0, + MIDDLE = 1, + RIGHT = 2, + X1 = 3, + X2 = 4 + }; + + enum class MouseButtonStatus : std::uint32_t + { + PRESS, + RELEASE, + }; + + enum class DeviceActionType : std::uint8_t + { + MOUSE_MOV = 0, + MOUSE_BUTTON = 1, + MOUSE_WHEEL = 2, + + TEXT = 3, + KEY = 4, + }; + + class [[nodiscard]] DeviceEvent + { + public: + struct mouse_mov_type + { + float x; + float y; + }; + + struct mouse_button_type + { + MouseButton button; + MouseButtonStatus status; + }; + + struct mouse_wheel_type + { + float x; + float y; + }; + + struct text_type + { + std::uint32_t c; + std::uint32_t pad; + }; + + struct key_type + { + DeviceKey key; + bool down; + float analog_value; + + static_assert(sizeof(DeviceKey) == 2); + std::uint8_t pad; + }; + + using underlying_type = std::variant; + static_assert(std::is_same_v(DeviceActionType::MOUSE_MOV), underlying_type>, mouse_mov_type>); + static_assert(std::is_same_v(DeviceActionType::MOUSE_BUTTON), underlying_type>, mouse_button_type>); + static_assert(std::is_same_v(DeviceActionType::MOUSE_WHEEL), underlying_type>, mouse_wheel_type>); + static_assert(std::is_same_v(DeviceActionType::TEXT), underlying_type>, text_type>); + static_assert(std::is_same_v(DeviceActionType::KEY), underlying_type>, key_type>); + + private: + underlying_type underlying_; + + public: + template + requires std::is_constructible_v, Args...> + constexpr explicit DeviceEvent(std::in_place_type_t type, Args&&... args) noexcept + : underlying_{type, std::forward(args)...} {} + + template + requires std::is_constructible_v + constexpr explicit DeviceEvent(Args&&... args) noexcept + : underlying_{std::forward(args)...} {} + + [[nodiscard]] constexpr auto type() const noexcept -> DeviceActionType + { + return static_cast(underlying_.index()); + } + + // note: unchecked + template + [[nodiscard]] constexpr auto value() const noexcept -> const auto& + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(underlying_.index() == static_cast(Type)); + return std::get(Type)>(underlying_); + } + + template + constexpr auto visit(Callable&& callable) const noexcept -> void + { + std::visit(std::forward(callable), underlying_); + } + }; + + class DeviceEventQueue + { + public: + template + using list_type = std::vector; + + using queue_type = list_type; + using size_type = queue_type::size_type; + + private: + queue_type queue_; + + public: + [[nodiscard]] constexpr auto size() const noexcept -> size_type + { + return queue_.size(); + } + + [[nodiscard]] constexpr auto consume() noexcept -> DeviceEvent + { + const auto e = queue_.back(); + queue_.pop_back(); + return e; + } + + constexpr auto mouse_move(const float x, const float y) noexcept -> void + { + // queue_.emplace_back(std::in_place_type, x, y); + queue_.emplace_back(DeviceEvent::mouse_mov_type{.x = x, .y = y}); + } + + constexpr auto mouse_button(const MouseButton button, const MouseButtonStatus status) noexcept -> void + { + // queue_.emplace_back(std::in_place_type, button, status); + queue_.emplace_back(DeviceEvent::mouse_button_type{.button = button, .status = status}); + } + + constexpr auto mouse_wheel(const float x, const float y) noexcept -> void + { + // queue_.emplace_back(std::in_place_type, x, y); + queue_.emplace_back(DeviceEvent::mouse_wheel_type{.x = x, .y = y}); + } + + constexpr auto text(const std::uint32_t c) noexcept -> void + { + // queue_.emplace_back(std::in_place_type, c); + queue_.emplace_back(DeviceEvent::text_type{.c = c, .pad = 0}); + } + + constexpr auto key(const DeviceKey key, const bool down, const float analog_value) noexcept -> void + { + // queue_.emplace_back(std::in_place_type, key, down, analog_value); + queue_.emplace_back(DeviceEvent::key_type{.key = key, .down = down, .analog_value = analog_value, .pad = 0}); + } + + constexpr auto key(const DeviceKey key, const bool down) noexcept -> void + { + return this->key(key, down, down ? 1.f : 0.f); + } + }; + + GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_END +} diff --git a/src/io/io.ixx b/src/io/io.ixx new file mode 100644 index 00000000..514e2cf1 --- /dev/null +++ b/src/io/io.ixx @@ -0,0 +1,16 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +export module gal.prometheus.io; + +export import :device; + +#else +#pragma once + +#include + +#endif From 431d26a91d2c6b93b853305f26bb8e6d7af21274 Mon Sep 17 00:00:00 2001 From: life4gal Date: Wed, 28 Aug 2024 17:55:43 +0800 Subject: [PATCH 003/258] `feat`: Add draw.node (WIP). --- src/draw/node.impl.ixx | 26 +++++++++++ src/draw/node.ixx | 102 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 src/draw/node.impl.ixx create mode 100644 src/draw/node.ixx diff --git a/src/draw/node.impl.ixx b/src/draw/node.impl.ixx new file mode 100644 index 00000000..94d7b2f3 --- /dev/null +++ b/src/draw/node.impl.ixx @@ -0,0 +1,26 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:node.impl; + +import std; + +import :node; + +#else +#include +#include + +#endif + +namespace gal::prometheus::draw::impl +{ + Node::~Node() noexcept = default; +} diff --git a/src/draw/node.ixx b/src/draw/node.ixx new file mode 100644 index 00000000..bf2fda7a --- /dev/null +++ b/src/draw/node.ixx @@ -0,0 +1,102 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:node; + +import std; +import gal.prometheus.primitive; + +#else +#pragma once + +#include +#include +#include + +#include +#include + +#endif + +GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) +{ + namespace impl + { + class Node; + class Surface; + } + + using element_type = std::shared_ptr; + using elements_type = std::vector; + + namespace impl + { + struct requirement_type + { + float min_width{0}; + float min_height{0}; + + float flex_grow_width{0}; + float flex_grow_height{0}; + float flex_shrink_width{0}; + float flex_shrink_height{0}; + }; + + class Node + { + public: + using rect_type = primitive::basic_rect_2d; + + protected: + elements_type children_; + requirement_type requirement_; + rect_type rect_; + + public: + Node(const Node&) noexcept = default; + auto operator=(const Node&) noexcept -> Node& = default; + Node(Node&&) noexcept = default; + auto operator=(Node&&) noexcept -> Node& = default; + + explicit Node() noexcept = default; + + explicit Node(elements_type children) noexcept + : children_{std::move(children)} {} + + virtual ~Node() noexcept = 0; + + [[nodiscard]] auto requirement() const noexcept -> requirement_type + { + return requirement_; + } + + virtual auto calculate_requirement() noexcept -> void + { + std::ranges::for_each( + children_, + [](auto& node) noexcept -> void { node->calculate_requirement(); } + ); + } + + virtual auto set_rect(const rect_type& rect) noexcept -> void + { + rect_ = rect; + } + + virtual auto render(Surface& surface) noexcept -> void + { + std::ranges::for_each( + children_, + [&surface](auto& node) noexcept -> void { node->render(surface); } + ); + } + }; + } +} From ff391492e38481ffe52c9f45e69422e2b78babc9 Mon Sep 17 00:00:00 2001 From: life4gal Date: Wed, 28 Aug 2024 17:57:06 +0800 Subject: [PATCH 004/258] `feat`: Add draw.dynamic_node (WIP). --- src/draw/dynamic_node.impl.ixx | 40 ++++++ src/draw/dynamic_node.ixx | 225 +++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 src/draw/dynamic_node.impl.ixx create mode 100644 src/draw/dynamic_node.ixx diff --git a/src/draw/dynamic_node.impl.ixx b/src/draw/dynamic_node.impl.ixx new file mode 100644 index 00000000..20ce670b --- /dev/null +++ b/src/draw/dynamic_node.impl.ixx @@ -0,0 +1,40 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:dynamic_node.impl; + +import std; + +import :dynamic_node; + +#else +#include +#include + +#endif + +namespace +{ + using namespace gal::prometheus::draw; + + class TrivialMouse final : public impl::Mouse {}; +} + +namespace gal::prometheus::draw::impl +{ + Mouse::~Mouse() noexcept = default; + + auto DynamicNode::try_capture_mouse(const event_type event) noexcept -> mouse_type + { + // todo + (void)event; + return std::make_unique(); + } +} diff --git a/src/draw/dynamic_node.ixx b/src/draw/dynamic_node.ixx new file mode 100644 index 00000000..51ea38db --- /dev/null +++ b/src/draw/dynamic_node.ixx @@ -0,0 +1,225 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:dynamic_node; + +import std; +import gal.prometheus.io; +GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE + +import :node; +import :animation; + +#else +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include GAL_PROMETHEUS_ERROR_DEBUG_MODULE + +#endif + +GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) +{ + namespace impl + { + class Mouse + { + public: + Mouse(const Mouse&) noexcept = default; + auto operator=(const Mouse&) noexcept -> Mouse& = default; + Mouse(Mouse&&) noexcept = default; + auto operator=(Mouse&&) noexcept -> Mouse& = default; + + Mouse() noexcept = default; + + virtual ~Mouse() noexcept = 0; + }; + + class DynamicNode; + } + + using mouse_type = std::unique_ptr; + + using component_type = std::shared_ptr; + using components_type = std::vector; + + namespace impl + { + class DynamicNode + { + public: + using size_type = components_type::size_type; + using event_type = io::DeviceEvent; + + protected: + components_type children_; + + private: + DynamicNode* parent_; + + public: + DynamicNode(const DynamicNode&) noexcept = delete; + auto operator=(const DynamicNode&) noexcept -> DynamicNode& = delete; + DynamicNode(DynamicNode&&) noexcept = delete; + auto operator=(DynamicNode&&) noexcept -> DynamicNode& = delete; + + explicit DynamicNode() noexcept + : parent_{nullptr} {} + + explicit DynamicNode(components_type children) noexcept + : children_{std::move(children)}, + parent_{nullptr} {} + + virtual ~DynamicNode() noexcept = 0; + + // ================================= + + [[nodiscard]] auto parent(this auto&& self) noexcept -> auto* + { + return std::forward(self).parent_; + } + + [[nodiscard]] auto children_size() const noexcept -> size_type + { + return children_.size(); + } + + [[nodiscard]] auto child_at(this auto&& self, const size_type index) noexcept -> component_type + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(index < children_size()); + return std::forward(self).children_[index]; + } + + auto detach_from_parent(DynamicNode* new_parent = nullptr) noexcept -> void + { + if (auto* old_parent = std::exchange(parent_, new_parent); + old_parent) + { + const auto it = std::ranges::find(old_parent->children_, this, &component_type::get); + old_parent->children_.erase(it); + } + } + + auto detach_children() noexcept -> void + { + std::ranges::for_each(children_, [](auto& child) noexcept -> void { child->parent_ = nullptr; }); + children_.clear(); + } + + auto attach_child(component_type child) noexcept -> void + { + child->detach_from_parent(this); + children_.push_back(std::move(child)); + } + + // ================================= + + [[nodiscard]] virtual auto render() noexcept -> element_type + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(children_.size() == 1); + + return children_.front()->render(); + } + + // ================================= + + [[nodiscard]] virtual auto on_event(const event_type event) noexcept -> bool + { + return std::ranges::any_of( + children_, + [event](auto* child) noexcept -> bool { return child->on_event(event); }, + &component_type::get + ); + } + + virtual auto on_animation(const animation_parameter_type parameter) noexcept -> void + { + std::ranges::for_each( + children_, + [parameter](auto* child) noexcept -> void { child->on_animation(parameter); }, + &component_type::get + ); + } + + // ================================= + + [[nodiscard]] virtual auto focusable() const noexcept -> bool + { + return std::ranges::any_of( + children_, + [](auto* child) noexcept -> bool { return child->focusable(); }, + &component_type::get + ); + } + + [[nodiscard]] auto focused() const noexcept -> bool + { + const auto* current = this; + while (current and current->activated()) + { + current = current->parent_; + } + + return current == nullptr and focusable(); + } + + auto take_focus() noexcept -> void + { + auto* child = this; + while (auto* parent = child->parent_) + { + parent->active_child(*child); + child = parent; + } + } + + [[nodiscard]] auto activated() const noexcept -> bool + { + return parent_ == nullptr or parent_->activated_child().get() == this; + } + + [[nodiscard]] virtual auto activated_child() noexcept -> component_type + { + if (const auto it = std::ranges::find_if( + children_, + [](auto* child) noexcept -> bool { return child->focusable(); }, + &component_type::get + ); + it != std::ranges::end(children_)) + { + return it.operator*(); + } + + return nullptr; + } + + virtual auto active_child(DynamicNode& child) noexcept -> void + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(std::ranges::contains(children_, &child, &component_type::get)); + } + + auto active_child(const component_type& child) noexcept -> void + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(child != nullptr); + active_child(*child); + } + + protected: + [[nodiscard]] auto try_capture_mouse(event_type event) noexcept -> mouse_type; + }; + } +} From fc19bed33653d1f17771eb1c60050c0691f7715a Mon Sep 17 00:00:00 2001 From: life4gal Date: Wed, 28 Aug 2024 17:58:27 +0800 Subject: [PATCH 005/258] `feat`: Add draw.animation (WIP). --- src/draw/animation.ixx | 335 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 src/draw/animation.ixx diff --git a/src/draw/animation.ixx b/src/draw/animation.ixx new file mode 100644 index 00000000..11fd255a --- /dev/null +++ b/src/draw/animation.ixx @@ -0,0 +1,335 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:animation; + +import std; +import gal.prometheus.primitive; +import gal.prometheus.functional; + +#else +#pragma once + +#include +#include +#include + +#include +#include +#include + +#endif + +GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw::impl) +{ + using clock_type = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point; + using duration_type = std::chrono::duration; + + using animation_parameter_type = duration_type; + + auto request_play_animation() noexcept -> void; + + namespace easing + { + using function_type = auto(*)(float) noexcept -> float; + + // y = x + constexpr auto linear = [](const float p) noexcept -> float + { + return p; + }; + + // y = x^2 + constexpr auto quadratic_in = [](const float p) noexcept -> float + { + return functional::pow(p, 2); + }; + + // y = -x^2+2x + constexpr auto quadratic_out = [](const float p) noexcept -> float + { + return -(p * (p - 2.f)); + }; + + // y = (1/2)((2x)^2) <- [0, 0.5) + // y = -(1/2)((2x-1)(2x-3)-1) <- [0.5, 1] + constexpr auto quadratic_in_out = [](const float p) noexcept -> float + { + if (p < .5f) + { + // 2 * x^2 + return 2.f * quadratic_in(p); + } + + // -2x^2 + 4x - 1 + return -2.f * quadratic_out(p) - 1.f; + }; + + // y = x^3 + constexpr auto cubic_in = [](const float p) noexcept -> float + { + return functional::pow(p, 3); + }; + + // y = (x-1)^3+1 + constexpr auto cubic_out = [](const float p) noexcept -> float + { + const auto t = p - 1.f; + return functional::pow(t, 3) + 1; + }; + + // y = (1/2)((2x)^3) <- [0, 0.5) + // y = (1/2)((2x-2)^3+2) <- [0.5, 1) + constexpr auto cubic_in_out = [](const float p) noexcept -> float + { + if (p < .5f) + { + // 4x^3 + return 4.f * cubic_in(p); + } + + // 4x^3-12x^2+12x-3 + // 4(x-1)^3+1 + return -3.f + 4 * cubic_out(p); + }; + + // y = x^4 + constexpr auto quartic_in = [](const float p) noexcept -> float + { + return functional::pow(p, 4); + }; + + // y = 1-(x-1)^4 + constexpr auto quartic_out = [](const float p) noexcept -> float + { + const auto t = p - 1.f; + return 1.f - functional::pow(t, 4); + }; + + // y = (1/2)((2x)^4) <- [0, 0.5) + // y = -(1/2)((2x-2)^4-2) <- [0.5, 1) + constexpr auto quartic_in_out = [](const float p) noexcept -> float + { + if (p < .5f) + { + // 8x^4 + return 8 * quartic_in(p); + } + + // -8x^4+32x^3-48x^2+32x-7 + // -8(x-1)^4+1 + return -7.f + 8 * quartic_out(p); + }; + + // y = x^5 + constexpr auto quintic_in = [](const float p) noexcept -> float + { + return functional::pow(p, 5); + }; + + // y = (x-1)^5+1 + constexpr auto quintic_out = [](const float p) noexcept -> float + { + const auto t = p - 1.f; + return functional::pow(t, 5) + 1.f; + }; + + // y = (1/2)((2x)^5) <- [0, 0.5) + // y = (1/2)((2x-2)^5+2) <- [0.5, 1) + constexpr auto quintic_in_out = [](const float p) noexcept -> float + { + if (p < .5f) + { + // 16x^5 + return 16 * quintic_in(p); + } + + // 16x^5 - 80x^4 + 160x^3 - 160x^2 + 80x - 15 + // 16(x-1)^5+1 + return 16.f * quintic_out(p) - 15.f; + }; + + // y = sin((x-1)*(PI/2))+1 + constexpr auto sine_in = [](const float p) noexcept -> float + { + return functional::sin((p - 1.f) * (std::numbers::pi_v / 2)) + 1.f; + }; + + // y = sin(x*(PI/2)) + constexpr auto sine_out = [](const float p) noexcept -> float + { + return functional::sin(p * (std::numbers::pi_v / 2)); + }; + + // y = (1/2)(1-cos(x*PI)) + constexpr auto sine_in_out = [](const float p) noexcept -> float + { + return .5f * (1 - functional::cos(p * std::numbers::pi_v)); + }; + + // y = 1-sqrt(1-x^2) + constexpr auto circular_in = [](const float p) noexcept -> float + { + return 1.f - functional::sqrt(1.f - functional::pow(p, 2)); + }; + + // y = sqrt((2-x)*x) + constexpr auto circular_out = [](const float p) noexcept -> float + { + return functional::sqrt((2.f - p) * p); + }; + + // y = (1/2)(1-sqrt(1-4x^2)) <- [0, 0.5) + // y = (1/2)(sqrt(-(2x-3)*(2x-1))+1) <- [0.5, 1) + constexpr auto circular_in_out = [](const float p) noexcept -> float + { + if (p < .5f) + { + return .5f * (1.f - functional::sqrt(1.f - 4.f * functional::pow(p, 2))); + } + + return .5f * (functional::sqrt(-(2.f * p - 3.f) * (2.f * p - 1.f)) + 1.f); + }; + } + + class Animator + { + public: + using function_type = easing::function_type; + + private: + std::reference_wrapper ref_to_value_; + float from_; + float to_; + duration_type duration_; + function_type function_; + duration_type current_; + + public: + explicit Animator( + float& from, + const float to = .0f, + const duration_type duration = std::chrono::milliseconds{250}, + const function_type easing_function = easing::linear, + const duration_type delay = std::chrono::milliseconds{0} + ) noexcept + : ref_to_value_{from}, + from_{from}, + to_{to}, + duration_{duration}, + function_{easing_function}, + current_{-delay} + { + request_play_animation(); + } + + auto play(const animation_parameter_type parameter) noexcept -> void + { + current_ += parameter; + + if (current_ >= duration_) + { + ref_to_value_.get() = to_; + return; + } + + if (current_ <= duration_) + { + ref_to_value_.get() = from_; + } + else + { + ref_to_value_.get() = from_ + (to_ - from_) * function_(current_ / duration_); + } + + request_play_animation(); + } + }; + + class AnimatedColorOption + { + public: + using function_type = easing::function_type; + using color_type = primitive::basic_color; + + private: + color_type inactive_; + color_type active_; + duration_type duration_; + function_type function_; + + public: + AnimatedColorOption() noexcept + : duration_{}, + function_{nullptr} {} + + AnimatedColorOption( + const color_type inactive, + const color_type active, + const duration_type duration = std::chrono::milliseconds{250}, + const function_type function = easing::quadratic_in_out + ) noexcept + : inactive_{inactive}, + active_{active}, + duration_{duration}, + function_{function} {} + + [[nodiscard]] constexpr auto enabled() const noexcept -> bool + { + return function_ != nullptr; + } + + [[nodiscard]] constexpr auto inactive() const noexcept -> color_type + { + return inactive_; + } + + [[nodiscard]] constexpr auto active() const noexcept -> color_type + { + return active_; + } + + [[nodiscard]] constexpr auto duration() const noexcept -> duration_type + { + return duration_; + } + + [[nodiscard]] constexpr auto function() const noexcept -> function_type + { + return function_; + } + }; + + class AnimatedColorsOption + { + public: + using option_type = AnimatedColorOption; + + private: + option_type background_; + option_type foreground_; + + public: + AnimatedColorsOption(const option_type& background, const option_type& foreground) noexcept + : background_{background}, + foreground_{foreground} {} + + [[nodiscard]] constexpr auto background() const noexcept -> const option_type& + { + return background_; + } + + [[nodiscard]] constexpr auto foreground() const noexcept -> const option_type& + { + return foreground_; + } + }; +} From ce5cba53711cea04c8a67aff6c31cdc78268dfd2 Mon Sep 17 00:00:00 2001 From: life4gal Date: Thu, 29 Aug 2024 17:55:17 +0800 Subject: [PATCH 006/258] `feat`: Add draw.surface. --- src/draw/surface.ixx | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/draw/surface.ixx diff --git a/src/draw/surface.ixx b/src/draw/surface.ixx new file mode 100644 index 00000000..327f3dbf --- /dev/null +++ b/src/draw/surface.ixx @@ -0,0 +1,79 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:surface; + +import std; +GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE + +export import :draw_list; +export import :element; + +#else +#pragma once + +#include +#include +#include + +#include +#include +#include +#include GAL_PROMETHEUS_ERROR_DEBUG_MODULE + +#endif + +GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) +{ + class Surface + { + public: + using draw_list_type = DrawList; + using rect_type = draw_list_type::rect_type; + using point_type = draw_list_type::point_type; + using extent_type = draw_list_type::extent_type; + + private: + draw_list_type draw_list_; + rect_type rect_; + + public: + explicit Surface() noexcept = default; + + explicit Surface(const rect_type& rect) noexcept : rect_{rect} {} + + [[nodiscard]] auto draw_list(this auto&& self) noexcept -> auto&& { return std::forward(self).draw_list_; } + + [[nodiscard]] auto rect() const noexcept -> const rect_type& { return rect_; } + + auto set_rect(const rect_type& rect) noexcept -> void { rect_ = rect; } + + auto set_rect(const point_type& left_top, const point_type& right_bottom) noexcept -> void + { + set_rect({left_top, right_bottom}); + } + + auto new_frame() noexcept -> void + { + draw_list_.reset(); + draw_list_.push_clip_rect(rect_, false); + } + + auto render(impl::Element& element) noexcept -> void; + + auto render(const element_type& element) noexcept -> void + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(element != nullptr); + render(*element); + } + + // todo + }; +} From 2b3b148d82cc7e9ca600fb7342305535a87492c1 Mon Sep 17 00:00:00 2001 From: life4gal Date: Thu, 29 Aug 2024 17:56:03 +0800 Subject: [PATCH 007/258] `fix`: Rename module (primitive:draw_list => draw:draw_list). --- src/draw/draw_list.ixx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/draw/draw_list.ixx b/src/draw/draw_list.ixx index 4ff8e14b..54e016e4 100644 --- a/src/draw/draw_list.ixx +++ b/src/draw/draw_list.ixx @@ -22,7 +22,7 @@ module; #include #endif -export module gal.prometheus.primitive:draw_list; +export module gal.prometheus.draw:draw_list; import std; import gal.prometheus.functional; @@ -1742,6 +1742,11 @@ namespace gal::prometheus::draw shared_data_ = shared_data; } + [[nodiscard]] auto shared_data() const noexcept -> std::shared_ptr + { + return shared_data_; + } + constexpr auto reset() noexcept -> void { command_list_.resize(0); From 14f0949983f39ac1cfdf03978668c6f278802724 Mon Sep 17 00:00:00 2001 From: life4gal Date: Thu, 29 Aug 2024 17:57:56 +0800 Subject: [PATCH 008/258] `feat`: Add a bunch of draw.element(s). --- src/draw/element.border.ixx | 117 ++++++++++++++++++++ src/draw/element.impl.ixx | 42 +++++++ src/draw/element.ixx | 165 ++++++++++++++++++++++++++++ src/draw/element.layout.ixx | 211 ++++++++++++++++++++++++++++++++++++ src/draw/element.text.ixx | 124 +++++++++++++++++++++ 5 files changed, 659 insertions(+) create mode 100644 src/draw/element.border.ixx create mode 100644 src/draw/element.impl.ixx create mode 100644 src/draw/element.ixx create mode 100644 src/draw/element.layout.ixx create mode 100644 src/draw/element.text.ixx diff --git a/src/draw/element.border.ixx b/src/draw/element.border.ixx new file mode 100644 index 00000000..8b7305b0 --- /dev/null +++ b/src/draw/element.border.ixx @@ -0,0 +1,117 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:element.border; + +import std; + +import gal.prometheus.primitive; + +import :surface; +import :element; + +#else +#include + +#include +#include +#include +#include + +#endif + +namespace +{ + using namespace gal::prometheus; + using namespace draw; + + // todo + constexpr auto pixel_offset_x = 4; + constexpr auto pixel_offset_y = 4; + + class Border final : public impl::Element + { + public: + using color_type = Surface::draw_list_type::color_type; + using point_type = rect_type::point_type; + + private: + std::optional color_; + + public: + Border(elements_type children, const std::optional color) noexcept + : Element{std::move(children)}, + color_{color} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(not children_.empty()); + + Element::calculate_requirement(surface); + + requirement_ = children_[0]->requirement(); + requirement_.min_width += 2 * pixel_offset_x; + requirement_.min_height += 2 * pixel_offset_y; + if (children_.size() == 2) + { + requirement_.min_width = std::ranges::max( + requirement_.min_width, + children_[1]->requirement().min_width + 2 * pixel_offset_x + ); + } + } + + auto set_rect(const rect_type& rect) noexcept -> void override + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(not children_.empty()); + + Element::set_rect(rect); + + if (children_.size() == 2) + { + const rect_type title_box + { + rect.left_top() + point_type{pixel_offset_x, 0}, + rect.right_bottom() + point_type{-pixel_offset_x, 0} + }; + children_[1]->set_rect(title_box); + } + + const rect_type box + { + rect.left_top() + point_type{pixel_offset_x, pixel_offset_y}, + rect.right_bottom() + point_type{-pixel_offset_y, -pixel_offset_y} + }; + children_[0]->set_rect(box); + } + + auto render(Surface& surface) noexcept -> void override + { + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(not children_.empty()); + + children_[0]->render(surface); + + surface.draw_list().rect(rect_, color_ ? *color_ : primitive::colors::black, 0, DrawFlag::ROUND_CORNER_ALL, 1); + + if (children_.size() == 2) + { + children_[1]->render(surface); + } + } + }; +} + +namespace gal::prometheus::draw::element +{ + [[nodiscard]] auto border(element_type element) noexcept -> element_type + { + return make_element(elements_type{std::move(element)}, std::nullopt); + } +} diff --git a/src/draw/element.impl.ixx b/src/draw/element.impl.ixx new file mode 100644 index 00000000..e7373e54 --- /dev/null +++ b/src/draw/element.impl.ixx @@ -0,0 +1,42 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:element.impl; + +import std; + +GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE + +import :surface; +import :element; + +#else +#include +#include +#include +#include GAL_PROMETHEUS_ERROR_DEBUG_MODULE + +#endif + +namespace gal::prometheus::draw +{ + auto Surface::render(impl::Element& element) noexcept -> void + { + // todo + element.calculate_requirement(*this); + element.set_rect({rect_.point.x, rect_.point.y, rect_.point.x + element.requirement().min_width, rect_.point.y + element.requirement().min_height}); + element.render(*this); + } + + namespace impl + { + Element::~Element() noexcept = default; + } +} diff --git a/src/draw/element.ixx b/src/draw/element.ixx new file mode 100644 index 00000000..d96a28ac --- /dev/null +++ b/src/draw/element.ixx @@ -0,0 +1,165 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:element; + +import std; +import gal.prometheus.primitive; + +#else +#pragma once + +#include +#include +#include + +#include +#include + +#endif + +GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) +{ + class Surface; + + namespace impl + { + class Element; + } + + using element_type = std::shared_ptr; + using elements_type = std::vector; + + template T, typename... Args> + [[nodiscard]] constexpr auto make_element(Args&&... args) noexcept -> element_type + { + return std::make_shared(std::forward(args)...); + } + + namespace impl + { + template + concept derived_element_t = std::derived_from; + + struct requirement_type + { + float min_width{0}; + float min_height{0}; + + float flex_grow_width{0}; + float flex_grow_height{0}; + float flex_shrink_width{0}; + float flex_shrink_height{0}; + }; + + class Element + { + public: + using rect_type = primitive::basic_rect_2d; + + protected: + elements_type children_; + + requirement_type requirement_; + rect_type rect_; + + public: + Element(const Element&) noexcept = default; + auto operator=(const Element&) noexcept -> Element& = default; + Element(Element&&) noexcept = default; + auto operator=(Element&&) noexcept -> Element& = default; + + explicit Element() noexcept = default; + + explicit Element(elements_type children) noexcept + : children_{std::move(children)} {} + + virtual ~Element() noexcept = 0; + + [[nodiscard]] auto requirement() const noexcept -> const requirement_type& + { + return requirement_; + } + + virtual auto calculate_requirement(Surface& surface) noexcept -> void + { + std::ranges::for_each( + children_, + [&surface](auto& node) noexcept -> void { node->calculate_requirement(surface); } + ); + } + + [[nodiscard]] auto rect() const noexcept -> const rect_type& + { + return rect_; + } + + virtual auto set_rect(const rect_type& rect) noexcept -> void + { + rect_ = rect; + } + + virtual auto render(Surface& surface) noexcept -> void + { + std::ranges::for_each( + children_, + [&surface](auto& element) noexcept -> void { element->render(surface); } + ); + } + }; + } + + inline namespace decorator + { + // element => element + template + concept decorator_t = requires(element_type element, Decorator&& decorator) + { + { + std::invoke(std::forward(decorator), element) + } -> impl::derived_element_t; + }; + + template + [[nodiscard]] constexpr auto operator|(element_type element, Decorator&& decorator) noexcept -> element_type + { + return std::invoke(std::forward(decorator), element); + } + } + + namespace element + { + // =============================== + // TEXT + + [[nodiscard]] auto text(std::string text) noexcept -> element_type; + + // =============================== + // BORDER + + [[nodiscard]] auto border(element_type element) noexcept -> element_type; + + // =============================== + // LAYOUT + + [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type; + + template + [[nodiscard]] auto horizontal_box(Element&&... elements) noexcept -> element_type + { + elements_type e{}; + e.reserve(sizeof...(elements)); + + (e.emplace_back(std::forward(elements)), ...); + + return horizontal_box(std::move(e)); + } + } +} diff --git a/src/draw/element.layout.ixx b/src/draw/element.layout.ixx new file mode 100644 index 00000000..a803c7f0 --- /dev/null +++ b/src/draw/element.layout.ixx @@ -0,0 +1,211 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:element.layout; + +import std; + +import gal.prometheus.primitive; + +import :surface; +import :element; + +#else +#include + +#include +#include +#include +#include + +#endif + +namespace +{ + using namespace gal::prometheus; + using namespace draw; + + struct element_size + { + float min_size = 0; + float flex_grow = 0; + float flex_shrink = 0; + + float size = 0; + }; + + template Range> + constexpr auto calculate_grow(Range& range, float extra_space, float flex_grow_sum) noexcept -> void + { + std::ranges::for_each( + range, + [extra_space, flex_grow_sum](element_size& element) mutable noexcept -> void + { + const auto added_space = extra_space * element.flex_grow / std::ranges::max(flex_grow_sum, 1.f); + + extra_space -= added_space; + flex_grow_sum -= element.flex_grow; + element.size = element.min_size + added_space; + } + ); + } + + template Range> + constexpr auto calculate_shrink_easy(Range& range, float extra_space, float flex_shrink_sum) noexcept -> void + { + std::ranges::for_each( + range, + [extra_space, flex_shrink_sum](element_size& element) mutable noexcept -> void + { + const auto added_space = extra_space * element.min_size * element.flex_shrink / std::ranges::max(flex_shrink_sum, 1.f); + + extra_space -= added_space; + flex_shrink_sum -= element.flex_shrink * element.min_size; + element.size = element.min_size + added_space; + } + ); + } + + template Range> + constexpr auto calculate_shrink_hard(Range& range, float extra_space, float size) noexcept -> void + { + std::ranges::for_each( + range, + [extra_space, size](element_size& element) mutable noexcept -> void + { + if (element.flex_shrink != 0) + { + element.size = 0; + return; + } + + const auto added_space = extra_space * element.min_size / std::ranges::max(size, 1.f); + + extra_space -= added_space; + size -= element.min_size; + element.size = element.min_size + added_space; + } + ); + } + + template Range> + constexpr auto calculate(Range& range, const float target_size) noexcept -> void + { + float size = 0; + float flex_grow_sum = 0; + float flex_shrink_sum = 0; + float flex_shrink_size = 0; + + std::ranges::for_each( + range, + [&](const element_size& element) noexcept -> void + { + size += element.min_size; + flex_grow_sum += element.flex_grow; + if (element.flex_shrink != 0) + { + flex_shrink_sum += element.min_size * element.flex_shrink; + flex_shrink_size += element.min_size; + } + } + ); + + if (const auto extra_space = target_size - size; + extra_space >= 0) + { + calculate_grow(range, extra_space, flex_grow_sum); + } + else if (flex_shrink_size + extra_space >= 0) + { + calculate_shrink_easy(range, extra_space, flex_shrink_sum); + } + else + { + calculate_shrink_hard(range, extra_space + flex_shrink_size, size - flex_shrink_size); + } + } + + class HorizontalBox final : public impl::Element + { + public: + using point_type = rect_type::point_type; + + explicit HorizontalBox(elements_type children) noexcept + : Element{std::move(children)} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + std::ranges::for_each( + children_, + [this, &surface](const auto& child) noexcept -> void + { + child->calculate_requirement(surface); + + requirement_.min_width += child->requirement().min_width; + requirement_.min_height = std::ranges::max( + requirement_.min_height, + child->requirement().min_height + ); + } + ); + } + + auto set_rect(const rect_type& rect) noexcept -> void override + { + Element::set_rect(rect); + + std::vector elements{}; + elements.reserve(children_.size()); + + std::ranges::transform( + children_, + std::back_inserter(elements), + [](const auto& child) noexcept -> element_size + { + const auto& r = child->requirement(); + return { + .min_size = r.min_width, + .flex_grow = r.flex_grow_width, + .flex_shrink = r.flex_shrink_width, + .size = 0 + }; + } + ); + + const auto target_size = rect.width() + 1; + calculate(elements, target_size); + + auto x = rect.left_top().x; + std::ranges::for_each( + std::views::zip(children_, elements), + [&x, &rect](std::tuple pack) noexcept -> void + { + auto& [child, element] = pack; + + const rect_type box + { + point_type{x, rect.left_top().y}, + point_type{x + element.size - 1, rect.right_bottom().y} + }; + child->set_rect(box); + x = box.right_bottom().x + 1; + } + ); + } + }; +} + +namespace gal::prometheus::draw::element +{ + [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type + { + return make_element(std::move(elements)); + } +} diff --git a/src/draw/element.text.ixx b/src/draw/element.text.ixx new file mode 100644 index 00000000..8b8b014d --- /dev/null +++ b/src/draw/element.text.ixx @@ -0,0 +1,124 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:element.text; + +import std; + +import gal.prometheus.primitive; +import gal.prometheus.chars; + +import :surface; +import :element; + +#else +#include +#include +#include +#include +#include + +#endif + +namespace +{ + using namespace gal::prometheus; + using namespace draw; + + // todo + constexpr auto default_font_size = 18.f; + + [[nodiscard]] auto calculate_text_area( + const font_type& font, + const float font_size, + const std::string_view text + ) noexcept -> impl::Element::rect_type::extent_type + { + using rect_type = impl::Element::rect_type; + using point_type = rect_type::point_type; + using extent_type = rect_type::extent_type; + + const auto utf32_text = chars::convert(text); + + const float scale = font_size / font.pixel_height; + + auto it_input_current = utf32_text.begin(); + const auto it_input_end = utf32_text.end(); + + auto area = extent_type{0, 0}; + auto cursor = point_type{0, font_size}; + + while (it_input_current != it_input_end) + { + const auto c = *it_input_current; + it_input_current += 1; + + if (c == U'\n') + { + area.width = std::ranges::max(area.width, cursor.x); + cursor.x = 0; + cursor.y += font.pixel_height * scale; + } + + const auto& [glyph_rect, glyph_uv, glyph_advance_x] = [&] + { + if (const auto it = font.glyphs.find(c); + it != font.glyphs.end()) + { + return it->second; + } + + return font.fallback_glyph; + }(); + + const auto advance_x = glyph_advance_x * scale; + cursor.x += advance_x; + } + + area.width = std::ranges::max(area.width, cursor.x); + area.height = cursor.y; + return area; + } + + class Text final : public impl::Element + { + public: + using text_type = std::string; + + private: + text_type text_; + + public: + explicit Text(text_type&& text) noexcept + : text_{std::move(text)} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + const auto& font = surface.draw_list().shared_data()->get_default_font(); + const auto area = calculate_text_area(font, default_font_size, text_); + requirement_.min_width = area.width; + requirement_.min_height = area.height; + } + + auto render(Surface& surface) noexcept -> void override + { + // todo + surface.draw_list().text(default_font_size, rect_.left_top(), primitive::colors::black, text_); + } + }; +} + +namespace gal::prometheus::draw::element +{ + [[nodiscard]] auto text(std::string text) noexcept -> element_type + { + return make_element(std::move(text)); + } +} From 2c0bae9739e5c84c71b061f83959667668722aa5 Mon Sep 17 00:00:00 2001 From: life4gal Date: Fri, 30 Aug 2024 13:51:20 +0800 Subject: [PATCH 009/258] `feat`: Allow draw.draw_list to draw utf32 strings directly. --- src/draw/draw_list.ixx | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/draw/draw_list.ixx b/src/draw/draw_list.ixx index 54e016e4..c1bbc605 100644 --- a/src/draw/draw_list.ixx +++ b/src/draw/draw_list.ixx @@ -1093,7 +1093,7 @@ namespace gal::prometheus::draw const float font_size, const point_type& p, const color_type& color, - const std::string_view text, + const std::u32string_view text, const float wrap_width ) noexcept -> void { @@ -1104,10 +1104,8 @@ namespace gal::prometheus::draw push_texture_id(font.texture_id); } - const auto utf32_text = chars::convert(text); - - const auto vertex_count = 4 * utf32_text.size(); - const auto index_count = 6 * utf32_text.size(); + const auto vertex_count = 4 * text.size(); + const auto index_count = 6 * text.size(); vertex_list_.reserve(vertex_list_.size() + vertex_count); index_list_.reserve(index_list_.size() + index_count); @@ -1115,8 +1113,8 @@ namespace gal::prometheus::draw const float scale = font_size / font.pixel_height; - auto it_input_current = utf32_text.begin(); - const auto it_input_end = utf32_text.end(); + auto it_input_current = text.begin(); + const auto it_input_end = text.end(); auto cursor = p + point_type{0, font_size}; @@ -2573,7 +2571,7 @@ namespace gal::prometheus::draw const float font_size, const point_type& p, const color_type& color, - const std::string_view text, + const std::u32string_view text, const float wrap_width = .0f ) noexcept -> void { @@ -2593,8 +2591,9 @@ namespace gal::prometheus::draw debug_vertex_range_type{current_vertex_size, vertex_list_.size()}, debug_index_range_type{current_index_size, index_list_.size()} ); + const auto utf8_text = chars::convert(text); debug_list_info_list_.emplace_back( - std::format("[TEXT] [{}({:.3f}): {}]({})", p, font_size, text, color), + std::format("[TEXT] [{}({:.3f}): {}]({})", p, font_size, utf8_text, color), // the data stored now is unreliable debug_vertex_list_type{vertex_list_.begin() + current_vertex_size, vertex_list_.end()}, // the data stored now is unreliable @@ -2603,6 +2602,30 @@ namespace gal::prometheus::draw #endif } + constexpr auto text( + const float font_size, + const point_type& p, + const color_type& color, + const std::u32string_view text, + const float wrap_width = .0f + ) noexcept -> void + { + this->text(shared_data_->get_default_font(), font_size, p, color, text, wrap_width); + } + + constexpr auto text( + const font_type& font, + const float font_size, + const point_type& p, + const color_type& color, + const std::string_view text, + const float wrap_width = .0f + ) noexcept -> void + { + const auto utf32_text = chars::convert(text); + this->text(font, font_size, p, color, utf32_text, wrap_width); + } + constexpr auto text( const float font_size, const point_type& p, From 0d1b40f4247e5be685f89f1466040dd08304474c Mon Sep 17 00:00:00 2001 From: life4gal Date: Fri, 30 Aug 2024 13:52:11 +0800 Subject: [PATCH 010/258] `feat`: Add element.text(utf32string). --- src/draw/element.ixx | 1 + src/draw/element.text.ixx | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/draw/element.ixx b/src/draw/element.ixx index d96a28ac..38db34b3 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -139,6 +139,7 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) // =============================== // TEXT + [[nodiscard]] auto text(std::u32string text) noexcept -> element_type; [[nodiscard]] auto text(std::string text) noexcept -> element_type; // =============================== diff --git a/src/draw/element.text.ixx b/src/draw/element.text.ixx index 8b8b014d..026be20f 100644 --- a/src/draw/element.text.ixx +++ b/src/draw/element.text.ixx @@ -38,19 +38,17 @@ namespace [[nodiscard]] auto calculate_text_area( const font_type& font, const float font_size, - const std::string_view text + const std::u32string_view text ) noexcept -> impl::Element::rect_type::extent_type { using rect_type = impl::Element::rect_type; using point_type = rect_type::point_type; using extent_type = rect_type::extent_type; - const auto utf32_text = chars::convert(text); - const float scale = font_size / font.pixel_height; - auto it_input_current = utf32_text.begin(); - const auto it_input_end = utf32_text.end(); + auto it_input_current = text.begin(); + const auto it_input_end = text.end(); auto area = extent_type{0, 0}; auto cursor = point_type{0, font_size}; @@ -90,13 +88,13 @@ namespace class Text final : public impl::Element { public: - using text_type = std::string; + using text_type = std::u32string; private: text_type text_; public: - explicit Text(text_type&& text) noexcept + explicit Text(std::u32string&& text) noexcept : text_{std::move(text)} {} auto calculate_requirement(Surface& surface) noexcept -> void override @@ -117,8 +115,13 @@ namespace namespace gal::prometheus::draw::element { - [[nodiscard]] auto text(std::string text) noexcept -> element_type + [[nodiscard]] auto text(std::u32string text) noexcept -> element_type { return make_element(std::move(text)); } + + [[nodiscard]] auto text(std::string text) noexcept -> element_type + { + return element::text(chars::convert(text)); + } } From 847602ffb148decceea518a192789b6e75b75e69 Mon Sep 17 00:00:00 2001 From: life4gal Date: Fri, 30 Aug 2024 17:19:02 +0800 Subject: [PATCH 011/258] `feat`: Add a bunch of draw.element(s). --- src/draw/element.border.ixx | 19 ++--- src/draw/element.ixx | 137 +++++++++++++++++++++++++++++------- src/draw/element.layout.ixx | 80 ++++++++++++++++++++- src/draw/element.text.ixx | 2 +- 4 files changed, 203 insertions(+), 35 deletions(-) diff --git a/src/draw/element.border.ixx b/src/draw/element.border.ixx index 8b7305b0..585e94f1 100644 --- a/src/draw/element.border.ixx +++ b/src/draw/element.border.ixx @@ -18,8 +18,6 @@ import :surface; import :element; #else -#include - #include #include #include @@ -43,10 +41,10 @@ namespace using point_type = rect_type::point_type; private: - std::optional color_; + color_type color_; public: - Border(elements_type children, const std::optional color) noexcept + Border(elements_type children, const color_type color) noexcept : Element{std::move(children)}, color_{color} {} @@ -98,7 +96,7 @@ namespace children_[0]->render(surface); - surface.draw_list().rect(rect_, color_ ? *color_ : primitive::colors::black, 0, DrawFlag::ROUND_CORNER_ALL, 1); + surface.draw_list().rect(rect_, color_, 0, DrawFlag::ROUND_CORNER_ALL, 1); if (children_.size() == 2) { @@ -108,10 +106,15 @@ namespace }; } -namespace gal::prometheus::draw::element +namespace gal::prometheus::draw::element::impl { - [[nodiscard]] auto border(element_type element) noexcept -> element_type + [[nodiscard]] auto border(elements_type elements, const primitive::colors::color_type color) noexcept -> element_type + { + return make_element(std::move(elements), color); + } + + [[nodiscard]] auto border(element_type element, const primitive::colors::color_type color) noexcept -> element_type { - return make_element(elements_type{std::move(element)}, std::nullopt); + return border(elements_type{std::move(element)}, color); } } diff --git a/src/draw/element.ixx b/src/draw/element.ixx index 38db34b3..07028262 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -12,6 +12,7 @@ export module gal.prometheus.draw:element; import std; import gal.prometheus.primitive; +import gal.prometheus.functional; #else #pragma once @@ -22,6 +23,7 @@ import gal.prometheus.primitive; #include #include +#include #endif @@ -43,6 +45,12 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) return std::make_shared(std::forward(args)...); } + template T> + [[nodiscard]] constexpr auto cast_element(const element_type element) noexcept -> std::shared_ptr + { + return std::static_pointer_cast(element); + } + namespace impl { template @@ -57,6 +65,17 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) float flex_grow_height{0}; float flex_shrink_width{0}; float flex_shrink_height{0}; + + constexpr auto reset() noexcept -> void + { + min_width = 0; + min_height = 0; + + flex_grow_width = 0; + flex_grow_height = 0; + flex_shrink_width = 0; + flex_shrink_height = 0; + } }; class Element @@ -116,51 +135,119 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) }; } - inline namespace decorator + namespace element { - // element => element + template + requires (impl::derived_element_t and impl::derived_element_t>) + [[nodiscard]] constexpr auto operator|(Element&& element, Decorator&& decorator) noexcept -> decltype(auto) + { + return std::invoke(std::forward(decorator), std::forward(element)); + } + template - concept decorator_t = requires(element_type element, Decorator&& decorator) + requires impl::derived_element_t> + [[nodiscard]] constexpr auto operator|(elements_type& elements, Decorator&& decorator) noexcept -> decltype(auto) { - { - std::invoke(std::forward(decorator), element) - } -> impl::derived_element_t; - }; + return std::invoke(std::forward(decorator), elements); + } - template - [[nodiscard]] constexpr auto operator|(element_type element, Decorator&& decorator) noexcept -> element_type + template + requires impl::derived_element_t> + [[nodiscard]] constexpr auto operator|(elements_type&& elements, Decorator&& decorator) noexcept -> decltype(auto) { - return std::invoke(std::forward(decorator), element); + return std::invoke(std::forward(decorator), std::move(elements)); } - } - namespace element - { // =============================== // TEXT - [[nodiscard]] auto text(std::u32string text) noexcept -> element_type; - [[nodiscard]] auto text(std::string text) noexcept -> element_type; + namespace impl + { + [[nodiscard]] auto text(std::u32string text) noexcept -> element_type; + [[nodiscard]] auto text(std::string text) noexcept -> element_type; + } + + constexpr auto text = functional::overloaded{ + [](std::u32string string) noexcept -> element_type + { + return impl::text(std::move(string)); + }, + [](std::string string) noexcept -> element_type + { + return impl::text(std::move(string)); + }, + }; // =============================== // BORDER - [[nodiscard]] auto border(element_type element) noexcept -> element_type; + namespace impl + { + [[nodiscard]] auto border(elements_type elements, primitive::colors::color_type color) noexcept -> element_type; + [[nodiscard]] auto border(element_type element, primitive::colors::color_type color) noexcept -> element_type; + } + + constexpr auto border = functional::overloaded{ + [](elements_type elements) noexcept -> element_type + { + return impl::border(std::move(elements), primitive::colors::black); + }, + [](element_type element) noexcept -> element_type + { + return impl::border(std::move(element), primitive::colors::black); + }, + [](primitive::colors::color_type color) noexcept -> auto + { + return functional::overloaded{ + [color](elements_type elements) noexcept -> element_type + { + return impl::border(std::move(elements), color); + }, + [color](element_type element) noexcept -> element_type + { + return impl::border(std::move(element), color); + }, + }; + }, + }; // =============================== // LAYOUT - [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type; - - template - [[nodiscard]] auto horizontal_box(Element&&... elements) noexcept -> element_type + namespace impl { - elements_type e{}; - e.reserve(sizeof...(elements)); + [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type; + [[nodiscard]] auto vertical_box(elements_type elements) noexcept -> element_type; + } - (e.emplace_back(std::forward(elements)), ...); + constexpr auto horizontal_box = functional::overloaded{ + [](elements_type elements) noexcept -> element_type + { + return impl::horizontal_box(std::move(elements)); + }, + [](draw::impl::derived_element_t auto... elements) noexcept -> element_type + { + elements_type es{}; + es.reserve(sizeof...(elements)); + + (es.emplace_back(std::move(elements)), ...); + return impl::horizontal_box(std::move(es)); + }, + }; - return horizontal_box(std::move(e)); - } + constexpr auto vertical_box = functional::overloaded{ + [](elements_type elements) noexcept -> element_type + { + return impl::vertical_box(std::move(elements)); + }, + [](draw::impl::derived_element_t auto... elements) noexcept -> element_type + { + elements_type es{}; + es.reserve(sizeof...(elements)); + + (es.emplace_back(std::move(elements)), ...); + return impl::vertical_box(std::move(es)); + }, + }; } } diff --git a/src/draw/element.layout.ixx b/src/draw/element.layout.ixx index a803c7f0..41639fc5 100644 --- a/src/draw/element.layout.ixx +++ b/src/draw/element.layout.ixx @@ -142,6 +142,8 @@ namespace auto calculate_requirement(Surface& surface) noexcept -> void override { + requirement_.reset(); + std::ranges::for_each( children_, [this, &surface](const auto& child) noexcept -> void @@ -200,12 +202,88 @@ namespace ); } }; + + class VerticalBox final : public impl::Element + { + public: + using point_type = rect_type::point_type; + + explicit VerticalBox(elements_type children) noexcept + : Element{std::move(children)} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + requirement_.reset(); + + std::ranges::for_each( + children_, + [this, &surface](const auto& child) noexcept -> void + { + child->calculate_requirement(surface); + + requirement_.min_height += child->requirement().min_height; + requirement_.min_width = std::ranges::max( + requirement_.min_width, + child->requirement().min_width + ); + } + ); + } + + auto set_rect(const rect_type& rect) noexcept -> void override + { + Element::set_rect(rect); + + std::vector elements{}; + elements.reserve(children_.size()); + + std::ranges::transform( + children_, + std::back_inserter(elements), + [](const auto& child) noexcept -> element_size + { + const auto& r = child->requirement(); + return { + .min_size = r.min_height, + .flex_grow = r.flex_grow_height, + .flex_shrink = r.flex_shrink_height, + .size = 0 + }; + } + ); + + const auto target_size = rect.width() + 1; + calculate(elements, target_size); + + auto y = rect.left_top().y; + std::ranges::for_each( + std::views::zip(children_, elements), + [&y, &rect](std::tuple pack) noexcept -> void + { + auto& [child, element] = pack; + + const rect_type box + { + point_type{rect.left_top().x, y}, + point_type{rect.right_bottom().x, y + element.size - 1} + }; + child->set_rect(box); + y = box.right_bottom().y + 1; + } + ); + } + }; } -namespace gal::prometheus::draw::element +namespace gal::prometheus::draw::element::impl { [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type { return make_element(std::move(elements)); } + + [[nodiscard]] auto vertical_box(elements_type elements) noexcept -> element_type + { + return make_element(std::move(elements)); + } } diff --git a/src/draw/element.text.ixx b/src/draw/element.text.ixx index 026be20f..947dc9a8 100644 --- a/src/draw/element.text.ixx +++ b/src/draw/element.text.ixx @@ -113,7 +113,7 @@ namespace }; } -namespace gal::prometheus::draw::element +namespace gal::prometheus::draw::element::impl { [[nodiscard]] auto text(std::u32string text) noexcept -> element_type { From de7105a90abd1f8a9d342a14a277e16334e79726 Mon Sep 17 00:00:00 2001 From: life4gal Date: Mon, 2 Sep 2024 17:46:42 +0800 Subject: [PATCH 012/258] `feat`: Add a bunch of draw.element(s). --- src/draw/draw.ixx | 10 ++ src/draw/element.border.ixx | 136 +++++++++++++------ src/draw/element.impl.ixx | 16 +++ src/draw/element.ixx | 257 +++++++++++++++++++++++++++++++++--- src/draw/element.layout.ixx | 245 ++++++++++++++++++++++++++++++++-- src/draw/element.text.ixx | 10 +- src/draw/style.ixx | 42 ++++++ 7 files changed, 636 insertions(+), 80 deletions(-) create mode 100644 src/draw/style.ixx diff --git a/src/draw/draw.ixx b/src/draw/draw.ixx index cfbb5e28..33d45678 100644 --- a/src/draw/draw.ixx +++ b/src/draw/draw.ixx @@ -9,10 +9,20 @@ export module gal.prometheus.draw; export import :font; export import :draw_list; +export import :surface; +export import :style; + +export import :element; + #else #pragma once #include #include +#include +#include + +#include + #endif diff --git a/src/draw/element.border.ixx b/src/draw/element.border.ixx index 585e94f1..d13cedd4 100644 --- a/src/draw/element.border.ixx +++ b/src/draw/element.border.ixx @@ -15,12 +15,14 @@ import std; import gal.prometheus.primitive; import :surface; +import :style; import :element; #else #include #include #include +#include #include #endif @@ -30,91 +32,137 @@ namespace using namespace gal::prometheus; using namespace draw; - // todo - constexpr auto pixel_offset_x = 4; - constexpr auto pixel_offset_y = 4; - class Border final : public impl::Element { public: - using color_type = Surface::draw_list_type::color_type; - using point_type = rect_type::point_type; + using color_type = Style::color_type; private: color_type color_; public: - Border(elements_type children, const color_type color) noexcept - : Element{std::move(children)}, + Border(element_type element, const color_type color) noexcept + : Element{elements_type{std::move(element)}}, color_{color} {} auto calculate_requirement(Surface& surface) noexcept -> void override { - GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(not children_.empty()); - Element::calculate_requirement(surface); requirement_ = children_[0]->requirement(); - requirement_.min_width += 2 * pixel_offset_x; - requirement_.min_height += 2 * pixel_offset_y; - if (children_.size() == 2) - { - requirement_.min_width = std::ranges::max( - requirement_.min_width, - children_[1]->requirement().min_width + 2 * pixel_offset_x - ); - } + requirement_.min_width += 2 * Style::instance().line_pixel_width; + requirement_.min_height += 2 * Style::instance().line_pixel_width; } auto set_rect(const rect_type& rect) noexcept -> void override { - GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(not children_.empty()); - Element::set_rect(rect); - if (children_.size() == 2) - { - const rect_type title_box - { - rect.left_top() + point_type{pixel_offset_x, 0}, - rect.right_bottom() + point_type{-pixel_offset_x, 0} - }; - children_[1]->set_rect(title_box); - } - + const auto& [point, extent] = rect; const rect_type box { - rect.left_top() + point_type{pixel_offset_x, pixel_offset_y}, - rect.right_bottom() + point_type{-pixel_offset_y, -pixel_offset_y} + // left + point.x + Style::instance().line_pixel_width, + // top + point.y + Style::instance().line_pixel_width, + // right + point.x + extent.width - Style::instance().line_pixel_width, + // bottom + point.y + extent.height - Style::instance().line_pixel_width }; + children_[0]->set_rect(box); } auto render(Surface& surface) noexcept -> void override { - GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(not children_.empty()); - children_[0]->render(surface); - surface.draw_list().rect(rect_, color_, 0, DrawFlag::ROUND_CORNER_ALL, 1); + surface.draw_list().rect(rect_, color_, 0, DrawFlag::ROUND_CORNER_ALL, Style::instance().line_pixel_width); + } + }; + + class Window final : public impl::Element + { + public: + using color_type = Style::color_type; - if (children_.size() == 2) - { - children_[1]->render(surface); - } + private: + color_type color_; + + public: + // title + content(border) + Window(elements_type children, const color_type color) noexcept + : Element{std::move(children)}, + color_{color} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + Element::calculate_requirement(surface); + + // border + requirement_ = children_[1]->requirement(); + // title + requirement_.min_width = std::ranges::max(requirement_.min_width, children_[0]->requirement().min_width); + requirement_.min_height += children_[0]->requirement().min_height; + } + + auto set_rect(const rect_type& rect) noexcept -> void override + { + Element::set_rect(rect); + + const auto& [point, extent] = rect; + + // title + const rect_type title_box{ + // left + point.x, + // top + point.y, + // right + point.x + extent.width, + // bottom + point.y + children_[0]->requirement().min_height + }; + children_[0]->set_rect(title_box); + + // border + const rect_type content_box{ + // left + point.x, + // top + point.y + title_box.height(), + // right + point.y + title_box.width(), + // bottom + point.y + extent.height + }; + children_[1]->set_rect(content_box); + } + + auto render(Surface& surface) noexcept -> void override + { + // title + using functional::operators::operator|; + surface.draw_list().rect_filled(children_[0]->rect(), color_, 0, DrawFlag::ROUND_CORNER_LEFT_TOP | DrawFlag::ROUND_CORNER_RIGHT_TOP); + children_[0]->render(surface); + + // border + children_[1]->render(surface); } }; } namespace gal::prometheus::draw::element::impl { - [[nodiscard]] auto border(elements_type elements, const primitive::colors::color_type color) noexcept -> element_type + [[nodiscard]] auto border(element_type element, const Style::color_type color) noexcept -> element_type { - return make_element(std::move(elements), color); + return make_element(std::move(element), color); } - [[nodiscard]] auto border(element_type element, const primitive::colors::color_type color) noexcept -> element_type + [[nodiscard]] auto window(element_type title, const Style::color_type title_color, element_type content, const Style::color_type content_color) noexcept -> element_type { - return border(elements_type{std::move(element)}, color); + auto b = border(std::move(content), content_color); + return make_element(elements_type{std::move(title), std::move(b)}, title_color); } } diff --git a/src/draw/element.impl.ixx b/src/draw/element.impl.ixx index e7373e54..60858e3e 100644 --- a/src/draw/element.impl.ixx +++ b/src/draw/element.impl.ixx @@ -15,11 +15,13 @@ import std; GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE import :surface; +import :style; import :element; #else #include #include +#include #include #include GAL_PROMETHEUS_ERROR_DEBUG_MODULE @@ -35,6 +37,20 @@ namespace gal::prometheus::draw element.render(*this); } + auto Style::instance() noexcept -> Style& + { + Style style{ + .font_pixel_size = 18.f, + .line_pixel_width = 1.f, + .flex_pixel_x = 0.f, + .flex_pixel_y = 0.f, + .border_default_color = primitive::colors::black, + .window_title_default_color = primitive::colors::red + }; + + return style; + } + namespace impl { Element::~Element() noexcept = default; diff --git a/src/draw/element.ixx b/src/draw/element.ixx index 07028262..92ab5c54 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -14,6 +14,8 @@ import std; import gal.prometheus.primitive; import gal.prometheus.functional; +import :style; + #else #pragma once @@ -24,6 +26,7 @@ import gal.prometheus.functional; #include #include #include +#include #endif @@ -53,9 +56,6 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) namespace impl { - template - concept derived_element_t = std::derived_from; - struct requirement_type { float min_width{0}; @@ -137,22 +137,25 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) namespace element { + template + concept derived_element_t = std::derived_from; + template - requires (impl::derived_element_t and impl::derived_element_t>) - [[nodiscard]] constexpr auto operator|(Element&& element, Decorator&& decorator) noexcept -> decltype(auto) + requires (derived_element_t and derived_element_t>) + [[nodiscard]] constexpr auto operator|(Element&& element, Decorator&& decorator) noexcept -> element_type // { return std::invoke(std::forward(decorator), std::forward(element)); } template - requires impl::derived_element_t> + requires derived_element_t> [[nodiscard]] constexpr auto operator|(elements_type& elements, Decorator&& decorator) noexcept -> decltype(auto) { return std::invoke(std::forward(decorator), elements); } template - requires impl::derived_element_t> + requires derived_element_t> [[nodiscard]] constexpr auto operator|(elements_type&& elements, Decorator&& decorator) noexcept -> decltype(auto) { return std::invoke(std::forward(decorator), std::move(elements)); @@ -179,30 +182,31 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) }; // =============================== - // BORDER + // SEPARATOR namespace impl { - [[nodiscard]] auto border(elements_type elements, primitive::colors::color_type color) noexcept -> element_type; - [[nodiscard]] auto border(element_type element, primitive::colors::color_type color) noexcept -> element_type; + [[nodiscard]] auto separator() noexcept -> element_type; + } + + // =============================== + // BORDER & WINDOW + + namespace impl + { + [[nodiscard]] auto border(element_type element, Style::color_type color) noexcept -> element_type; + + [[nodiscard]] auto window(element_type title, Style::color_type title_color, element_type content, Style::color_type content_color) noexcept -> element_type; } constexpr auto border = functional::overloaded{ - [](elements_type elements) noexcept -> element_type - { - return impl::border(std::move(elements), primitive::colors::black); - }, [](element_type element) noexcept -> element_type { - return impl::border(std::move(element), primitive::colors::black); + return impl::border(std::move(element), Style::instance().border_default_color); }, - [](primitive::colors::color_type color) noexcept -> auto + [](const Style::color_type color) noexcept -> auto { return functional::overloaded{ - [color](elements_type elements) noexcept -> element_type - { - return impl::border(std::move(elements), color); - }, [color](element_type element) noexcept -> element_type { return impl::border(std::move(element), color); @@ -211,21 +215,180 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) }, }; + constexpr auto window = functional::overloaded{ + [](element_type title, element_type content) noexcept -> element_type + { + return impl::window(std::move(title), Style::instance().window_title_default_color, std::move(content), Style::instance().border_default_color); + }, + [](const Style::color_type title_color, const Style::color_type content_color) noexcept -> auto + { + return functional::overloaded{ + [title_color, content_color](element_type title, element_type content) noexcept -> element_type + { + return impl::window(std::move(title), title_color, std::move(content), content_color); + }, + }; + } + }; + // =============================== // LAYOUT namespace impl { + // todo + #define LAMBDA_DEDUCING_THIS_WORKAROUND 1 + + enum class FlexOption : std::uint32_t + { + NONE = 0b0000'0000, + ALL = 0b0001'0000'0000, + + GROW = 0b0000'0001, + SHRINK = 0b0000'0010, + + HORIZONTAL = 0b0001'0000, + VERTICAL = 0b0010'0000, + }; + + template + struct flex_option : std::integral_constant, (... + std::to_underlying(Os))> {}; + + #if LAMBDA_DEDUCING_THIS_WORKAROUND + template<> + struct flex_option<> : std::integral_constant, std::to_underlying(FlexOption::NONE)> {}; + #endif + + enum class CenterOption : std::uint32_t + { + ALL = 0b0000, + HORIZONTAL = 0b0001, + VERTICAL = 0b0010, + }; + + template + struct center_option : std::integral_constant, (... + std::to_underlying(Os))> {}; + + #if LAMBDA_DEDUCING_THIS_WORKAROUND + template<> + struct center_option<> : std::integral_constant, std::to_underlying(CenterOption::ALL)> {}; + #endif + + [[nodiscard]] auto filler() noexcept -> element_type; + [[nodiscard]] auto no_flex(element_type element) noexcept -> element_type; + + [[nodiscard]] auto flex(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_grow(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_shrink(element_type element) noexcept -> element_type; + + [[nodiscard]] auto horizontal_flex(element_type element) noexcept -> element_type; + [[nodiscard]] auto horizontal_flex_grow(element_type element) noexcept -> element_type; + [[nodiscard]] auto horizontal_flex_shrink(element_type element) noexcept -> element_type; + + [[nodiscard]] auto vertical_flex(element_type element) noexcept -> element_type; + [[nodiscard]] auto vertical_flex_grow(element_type element) noexcept -> element_type; + [[nodiscard]] auto vertical_flex_shrink(element_type element) noexcept -> element_type; + [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type; [[nodiscard]] auto vertical_box(elements_type elements) noexcept -> element_type; + [[nodiscard]] auto stack_box(elements_type elements) noexcept -> element_type; + + [[nodiscard]] auto horizontal_center(element_type element) noexcept -> element_type; + [[nodiscard]] auto vertical_center(element_type element) noexcept -> element_type; + [[nodiscard]] auto center(element_type element) noexcept -> element_type; } + #if LAMBDA_DEDUCING_THIS_WORKAROUND + constexpr auto flex_option_none = impl::flex_option{}; + #else + constexpr auto flex_option_none = impl::flex_option{}; + #endif + constexpr auto flex_option_all = impl::flex_option{}; + constexpr auto flex_option_grow = impl::flex_option{}; + constexpr auto flex_option_shrink = impl::flex_option{}; + constexpr auto flex_option_horizontal_grow = impl::flex_option{}; + constexpr auto flex_option_horizontal_shrink = impl::flex_option{}; + constexpr auto flex_option_vertical_grow = impl::flex_option{}; + constexpr auto flex_option_vertical_shrink = impl::flex_option{}; + + #if LAMBDA_DEDUCING_THIS_WORKAROUND + constexpr auto center_option_all = impl::center_option{}; + #else + constexpr auto center_option_all = impl::center_option{}; + #endif + constexpr auto center_option_horizontal = impl::center_option{}; + constexpr auto center_option_vertical = impl::center_option{}; + + constexpr auto flex = functional::overloaded{ + []( + element_type element, + const impl::flex_option + #if LAMBDA_DEDUCING_THIS_WORKAROUND + = flex_option_none + #endif + ) noexcept -> element_type + { + #if LAMBDA_DEDUCING_THIS_WORKAROUND + constexpr auto value = std::conditional_t, impl::flex_option>::value; + #else + constexpr auto value = impl::flex_option::value; + #endif + if constexpr (value & std::to_underlying(impl::FlexOption::GROW)) + { + if constexpr (value & std::to_underlying(impl::FlexOption::HORIZONTAL)) + { + return impl::horizontal_flex_grow(std::move(element)); + } + else if constexpr (value & std::to_underlying(impl::FlexOption::VERTICAL)) + { + return impl::vertical_flex_grow(std::move(element)); + } + else + { + static_assert(value == std::to_underlying(impl::FlexOption::GROW)); + return impl::flex_grow(std::move(element)); + } + } + else if constexpr (value & std::to_underlying(impl::FlexOption::SHRINK)) + { + if constexpr (value & std::to_underlying(impl::FlexOption::HORIZONTAL)) + { + return impl::horizontal_flex_shrink(std::move(element)); + } + else if constexpr (value & std::to_underlying(impl::FlexOption::VERTICAL)) + { + return impl::vertical_flex_shrink(std::move(element)); + } + else + { + static_assert(value == std::to_underlying(impl::FlexOption::SHRINK)); + return impl::flex_shrink(std::move(element)); + } + } + else if constexpr (value == std::to_underlying(impl::FlexOption::ALL)) + { + return impl::flex(std::move(element)); + } + else + { + static_assert(value == std::to_underlying(impl::FlexOption::NONE)); + return impl::no_flex(std::move(element)); + } + }, + #if not LAMBDA_DEDUCING_THIS_WORKAROUND + [](this Self&& self, element_type element) noexcept -> element_type + { + return std::forward(self)(std::move(element), flex_option_none); + }, + #endif + }; + constexpr auto horizontal_box = functional::overloaded{ [](elements_type elements) noexcept -> element_type { return impl::horizontal_box(std::move(elements)); }, - [](draw::impl::derived_element_t auto... elements) noexcept -> element_type + [](derived_element_t auto... elements) noexcept -> element_type { elements_type es{}; es.reserve(sizeof...(elements)); @@ -240,7 +403,7 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { return impl::vertical_box(std::move(elements)); }, - [](draw::impl::derived_element_t auto... elements) noexcept -> element_type + [](derived_element_t auto... elements) noexcept -> element_type { elements_type es{}; es.reserve(sizeof...(elements)); @@ -249,5 +412,55 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) return impl::vertical_box(std::move(es)); }, }; + + constexpr auto stack_box = functional::overloaded{ + [](elements_type elements) noexcept -> element_type + { + return impl::stack_box(std::move(elements)); + }, + [](derived_element_t auto... elements) noexcept -> element_type + { + elements_type es{}; + es.reserve(sizeof...(elements)); + + (es.emplace_back(std::move(elements)), ...); + return impl::stack_box(std::move(es)); + }, + }; + + constexpr auto center = functional::overloaded + { + []( + element_type element, + const impl::center_option + #if LAMBDA_DEDUCING_THIS_WORKAROUND + = center_option_all + #endif + ) noexcept -> element_type + { + constexpr auto value = std::conditional_t, impl::center_option>::value; + if constexpr (value == std::to_underlying(impl::CenterOption::ALL)) + { + return impl::center(std::move(element)); + } + else if constexpr (value == std::to_underlying(impl::CenterOption::HORIZONTAL)) + { + return impl::horizontal_center(std::move(element)); + } + else + { + static_assert(value == std::to_underlying(impl::CenterOption::VERTICAL)); + return impl::vertical_center(std::move(element)); + } + }, + #if not LAMBDA_DEDUCING_THIS_WORKAROUND + [](this Self&& self, element_type element) noexcept -> element_type + { + return std::forward(self)(std::move(element), center_option_all); + }, + #endif + }; } } + +#undef LAMBDA_DEDUCING_THIS_WORKAROUND diff --git a/src/draw/element.layout.ixx b/src/draw/element.layout.ixx index 41639fc5..42f35acd 100644 --- a/src/draw/element.layout.ixx +++ b/src/draw/element.layout.ixx @@ -15,6 +15,7 @@ import std; import gal.prometheus.primitive; import :surface; +import :style; import :element; #else @@ -23,6 +24,7 @@ import :element; #include #include #include +#include #include #endif @@ -32,6 +34,71 @@ namespace using namespace gal::prometheus; using namespace draw; + namespace function + { + using impl::requirement_type; + + auto flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + requirement.flex_grow_height = Style::instance().flex_pixel_y; + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + + auto no_flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = 0; + requirement.flex_grow_height = 0; + requirement.flex_shrink_width = 0; + requirement.flex_shrink_height = 0; + } + + auto flex_grow(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + requirement.flex_grow_height = Style::instance().flex_pixel_y; + } + + auto flex_shrink(requirement_type& requirement) noexcept -> void + { + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + + auto horizontal_flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + } + + auto horizontal_flex_grow(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + } + + auto horizontal_flex_shrink(requirement_type& requirement) noexcept -> void + { + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + } + + auto vertical_flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_height = Style::instance().flex_pixel_y; + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + + auto vertical_flex_grow(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_height = Style::instance().flex_pixel_y; + } + + auto vertical_flex_shrink(requirement_type& requirement) noexcept -> void + { + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + } + struct element_size { float min_size = 0; @@ -132,6 +199,49 @@ namespace } } + class Flex final : public impl::Element + { + public: + using function_type = void(*)(impl::requirement_type&); + + private: + function_type function_; + + public: + explicit Flex(const function_type function) noexcept + : Element{}, + function_{function} {} + + Flex(const function_type function, element_type element) noexcept + : Element{elements_type{std::move(element)}}, + function_{function} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + requirement_.reset(); + + if (not children_.empty()) + [[unlikely]] + { + children_[0]->calculate_requirement(surface); + requirement_ = children_[0]->requirement(); + } + + function_(requirement_); + } + + auto set_rect(const rect_type& rect) noexcept -> void override + { + if (children_.empty()) + [[unlikely]] + { + return; + } + + children_[0]->set_rect(rect); + } + }; + class HorizontalBox final : public impl::Element { public: @@ -181,23 +291,24 @@ namespace } ); - const auto target_size = rect.width() + 1; + const auto flex_pixel_x = Style::instance().flex_pixel_x; + const auto target_size = rect.width() + flex_pixel_x; calculate(elements, target_size); auto x = rect.left_top().x; std::ranges::for_each( std::views::zip(children_, elements), - [&x, &rect](std::tuple pack) noexcept -> void + [&x, &rect, flex_pixel_x](std::tuple pack) noexcept -> void { auto& [child, element] = pack; const rect_type box { point_type{x, rect.left_top().y}, - point_type{x + element.size - 1, rect.right_bottom().y} + point_type{x + element.size - flex_pixel_x, rect.right_bottom().y} }; child->set_rect(box); - x = box.right_bottom().x + 1; + x = box.right_bottom().x + flex_pixel_x; } ); } @@ -252,23 +363,66 @@ namespace } ); - const auto target_size = rect.width() + 1; + const auto flex_pixel_y = Style::instance().flex_pixel_y; + const auto target_size = rect.height() + flex_pixel_y; calculate(elements, target_size); auto y = rect.left_top().y; std::ranges::for_each( std::views::zip(children_, elements), - [&y, &rect](std::tuple pack) noexcept -> void + [&y, &rect, flex_pixel_y](std::tuple pack) noexcept -> void { auto& [child, element] = pack; const rect_type box { point_type{rect.left_top().x, y}, - point_type{rect.right_bottom().x, y + element.size - 1} + point_type{rect.right_bottom().x, y + element.size - flex_pixel_y} }; child->set_rect(box); - y = box.right_bottom().y + 1; + y = box.right_bottom().y + flex_pixel_y; + } + ); + } + }; + + class StackBox final : public impl::Element + { + public: + explicit StackBox(elements_type children) noexcept + : Element{std::move(children)} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + requirement_.reset(); + + std::ranges::for_each( + children_, + [this, &surface](const auto& child) noexcept -> void + { + child->calculate_requirement(surface); + + requirement_.min_width = std::ranges::max( + requirement_.min_width, + child->requirement().min_width + ); + requirement_.min_height = std::ranges::max( + requirement_.min_height, + child->requirement().min_height + ); + } + ); + } + + auto set_rect(const rect_type& rect) noexcept -> void override + { + Element::set_rect(rect); + + std::ranges::for_each( + children_, + [rect](auto& child) noexcept -> void + { + child->set_rect(rect); } ); } @@ -277,6 +431,61 @@ namespace namespace gal::prometheus::draw::element::impl { + [[nodiscard]] auto filler() noexcept -> element_type + { + return make_element(function::flex); + } + + [[nodiscard]] auto no_flex(element_type element) noexcept -> element_type + { + return make_element(function::no_flex, std::move(element)); + } + + [[nodiscard]] auto flex(element_type element) noexcept -> element_type + { + return make_element(function::flex, std::move(element)); + } + + [[nodiscard]] auto flex_grow(element_type element) noexcept -> element_type + { + return make_element(function::flex_grow, std::move(element)); + } + + [[nodiscard]] auto flex_shrink(element_type element) noexcept -> element_type + { + return make_element(function::flex_shrink, std::move(element)); + } + + [[nodiscard]] auto horizontal_flex(element_type element) noexcept -> element_type + { + return make_element(function::horizontal_flex, std::move(element)); + } + + [[nodiscard]] auto horizontal_flex_grow(element_type element) noexcept -> element_type + { + return make_element(function::horizontal_flex_grow, std::move(element)); + } + + [[nodiscard]] auto horizontal_flex_shrink(element_type element) noexcept -> element_type + { + return make_element(function::horizontal_flex_shrink, std::move(element)); + } + + [[nodiscard]] auto vertical_flex(element_type element) noexcept -> element_type + { + return make_element(function::vertical_flex, std::move(element)); + } + + [[nodiscard]] auto vertical_flex_grow(element_type element) noexcept -> element_type + { + return make_element(function::vertical_flex_grow, std::move(element)); + } + + [[nodiscard]] auto vertical_flex_shrink(element_type element) noexcept -> element_type + { + return make_element(function::vertical_flex_shrink, std::move(element)); + } + [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type { return make_element(std::move(elements)); @@ -286,4 +495,24 @@ namespace gal::prometheus::draw::element::impl { return make_element(std::move(elements)); } + + [[nodiscard]] auto stack_box(elements_type elements) noexcept -> element_type + { + return make_element(std::move(elements)); + } + + [[nodiscard]] auto horizontal_center(element_type element) noexcept -> element_type + { + return element::horizontal_box(filler(), std::move(element), filler()); + } + + [[nodiscard]] auto vertical_center(element_type element) noexcept -> element_type + { + return element::vertical_box(filler(), std::move(element), filler()); + } + + [[nodiscard]] auto center(element_type element) noexcept -> element_type + { + return horizontal_center(vertical_center(std::move(element))); + } } diff --git a/src/draw/element.text.ixx b/src/draw/element.text.ixx index 947dc9a8..d42c25e3 100644 --- a/src/draw/element.text.ixx +++ b/src/draw/element.text.ixx @@ -16,6 +16,7 @@ import gal.prometheus.primitive; import gal.prometheus.chars; import :surface; +import :style; import :element; #else @@ -23,6 +24,7 @@ import :element; #include #include #include +#include #include #endif @@ -32,9 +34,6 @@ namespace using namespace gal::prometheus; using namespace draw; - // todo - constexpr auto default_font_size = 18.f; - [[nodiscard]] auto calculate_text_area( const font_type& font, const float font_size, @@ -100,15 +99,14 @@ namespace auto calculate_requirement(Surface& surface) noexcept -> void override { const auto& font = surface.draw_list().shared_data()->get_default_font(); - const auto area = calculate_text_area(font, default_font_size, text_); + const auto area = calculate_text_area(font, Style::instance().font_pixel_size, text_); requirement_.min_width = area.width; requirement_.min_height = area.height; } auto render(Surface& surface) noexcept -> void override { - // todo - surface.draw_list().text(default_font_size, rect_.left_top(), primitive::colors::black, text_); + surface.draw_list().text(Style::instance().font_pixel_size, rect_.left_top(), primitive::colors::black, text_); } }; } diff --git a/src/draw/style.ixx b/src/draw/style.ixx new file mode 100644 index 00000000..d95d6640 --- /dev/null +++ b/src/draw/style.ixx @@ -0,0 +1,42 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:style; + +import std; +import gal.prometheus.primitive; + +#else +#pragma once + +#include +#include + +#endif + +GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) +{ + class Style final + { + public: + using color_type = primitive::colors::color_type; + + [[nodiscard]] static auto instance() noexcept -> Style&; + + float font_pixel_size; + float line_pixel_width; + + float flex_pixel_x; + float flex_pixel_y; + + color_type border_default_color; + color_type window_title_default_color; + }; +} From c40dcd8404d9839be25dcc77d8ee5ffed51931f5 Mon Sep 17 00:00:00 2001 From: life4gal Date: Tue, 3 Sep 2024 10:52:29 +0800 Subject: [PATCH 013/258] `fix`: Fix layout. --- src/draw/element.border.ixx | 18 +++++++----- src/draw/element.impl.ixx | 4 +-- src/draw/element.ixx | 14 ++++++++++ src/draw/element.layout.ixx | 56 +++++++++++++++++++++---------------- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/draw/element.border.ixx b/src/draw/element.border.ixx index d13cedd4..c79f27b1 100644 --- a/src/draw/element.border.ixx +++ b/src/draw/element.border.ixx @@ -50,25 +50,29 @@ namespace Element::calculate_requirement(surface); requirement_ = children_[0]->requirement(); - requirement_.min_width += 2 * Style::instance().line_pixel_width; - requirement_.min_height += 2 * Style::instance().line_pixel_width; + + const auto line_pixel_width = Style::instance().line_pixel_width; + requirement_.min_width += 2 * line_pixel_width; + requirement_.min_height += 2 * line_pixel_width; } auto set_rect(const rect_type& rect) noexcept -> void override { Element::set_rect(rect); + const auto line_pixel_width = Style::instance().line_pixel_width; const auto& [point, extent] = rect; + const rect_type box { // left - point.x + Style::instance().line_pixel_width, + point.x + line_pixel_width, // top - point.y + Style::instance().line_pixel_width, + point.y + line_pixel_width, // right - point.x + extent.width - Style::instance().line_pixel_width, + point.x + extent.width - 2 * line_pixel_width, // bottom - point.y + extent.height - Style::instance().line_pixel_width + point.y + extent.height - 2 * line_pixel_width }; children_[0]->set_rect(box); @@ -133,7 +137,7 @@ namespace // top point.y + title_box.height(), // right - point.y + title_box.width(), + point.x + title_box.width(), // bottom point.y + extent.height }; diff --git a/src/draw/element.impl.ixx b/src/draw/element.impl.ixx index 60858e3e..92f269de 100644 --- a/src/draw/element.impl.ixx +++ b/src/draw/element.impl.ixx @@ -42,8 +42,8 @@ namespace gal::prometheus::draw Style style{ .font_pixel_size = 18.f, .line_pixel_width = 1.f, - .flex_pixel_x = 0.f, - .flex_pixel_y = 0.f, + .flex_pixel_x = 3.f, + .flex_pixel_y = 3.f, .border_default_color = primitive::colors::black, .window_title_default_color = primitive::colors::red }; diff --git a/src/draw/element.ixx b/src/draw/element.ixx index 92ab5c54..1dca6e3b 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -381,6 +381,13 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) return std::forward(self)(std::move(element), flex_option_none); }, #endif + [](this Self&& self, const impl::flex_option options) noexcept -> auto + { + return [s = std::forward(self), options](element_type element) noexcept -> element_type + { + return s(std::move(element), options); + }; + }, }; constexpr auto horizontal_box = functional::overloaded{ @@ -459,6 +466,13 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) return std::forward(self)(std::move(element), center_option_all); }, #endif + [](this Self&& self, const impl::center_option options) noexcept -> auto + { + return [s = std::forward(self), options](element_type element) noexcept -> element_type + { + return s(std::move(element), options); + }; + }, }; } } diff --git a/src/draw/element.layout.ixx b/src/draw/element.layout.ixx index 42f35acd..b8570d94 100644 --- a/src/draw/element.layout.ixx +++ b/src/draw/element.layout.ixx @@ -245,8 +245,6 @@ namespace class HorizontalBox final : public impl::Element { public: - using point_type = rect_type::point_type; - explicit HorizontalBox(elements_type children) noexcept : Element{std::move(children)} {} @@ -256,11 +254,11 @@ namespace std::ranges::for_each( children_, - [this, &surface](const auto& child) noexcept -> void + [this, &surface, flex_pixel_x = Style::instance().flex_pixel_x](const auto& child) noexcept -> void { child->calculate_requirement(surface); - requirement_.min_width += child->requirement().min_width; + requirement_.min_width += child->requirement().min_width + 2 * flex_pixel_x; requirement_.min_height = std::ranges::max( requirement_.min_height, child->requirement().min_height @@ -291,24 +289,30 @@ namespace } ); - const auto flex_pixel_x = Style::instance().flex_pixel_x; - const auto target_size = rect.width() + flex_pixel_x; - calculate(elements, target_size); + calculate(elements, rect.width()); + + const auto& [point, extent] = rect; - auto x = rect.left_top().x; + auto x = point.x; std::ranges::for_each( std::views::zip(children_, elements), - [&x, &rect, flex_pixel_x](std::tuple pack) noexcept -> void + [&x, y = point.y, b = point.y + extent.height, flex_pixel_x= Style::instance().flex_pixel_x](std::tuple pack) noexcept -> void { auto& [child, element] = pack; const rect_type box { - point_type{x, rect.left_top().y}, - point_type{x + element.size - flex_pixel_x, rect.right_bottom().y} + // left + x + flex_pixel_x, + // top + y, + // right + x + flex_pixel_x + element.size + flex_pixel_x, + // bottom + b }; child->set_rect(box); - x = box.right_bottom().x + flex_pixel_x; + x = x + flex_pixel_x + element.size + flex_pixel_x; } ); } @@ -317,8 +321,6 @@ namespace class VerticalBox final : public impl::Element { public: - using point_type = rect_type::point_type; - explicit VerticalBox(elements_type children) noexcept : Element{std::move(children)} {} @@ -328,11 +330,11 @@ namespace std::ranges::for_each( children_, - [this, &surface](const auto& child) noexcept -> void + [this, &surface, flex_pixel_y = Style::instance().flex_pixel_y](const auto& child) noexcept -> void { child->calculate_requirement(surface); - requirement_.min_height += child->requirement().min_height; + requirement_.min_height += child->requirement().min_height + 2 * flex_pixel_y; requirement_.min_width = std::ranges::max( requirement_.min_width, child->requirement().min_width @@ -363,24 +365,30 @@ namespace } ); - const auto flex_pixel_y = Style::instance().flex_pixel_y; - const auto target_size = rect.height() + flex_pixel_y; - calculate(elements, target_size); + calculate(elements, rect.height()); + + const auto& [point, extent] = rect; - auto y = rect.left_top().y; + auto y = point.y; std::ranges::for_each( std::views::zip(children_, elements), - [&y, &rect, flex_pixel_y](std::tuple pack) noexcept -> void + [&y, x = point.x, r = point.x + extent.width, flex_pixel_y = Style::instance().flex_pixel_y](std::tuple pack) noexcept -> void { auto& [child, element] = pack; const rect_type box { - point_type{rect.left_top().x, y}, - point_type{rect.right_bottom().x, y + element.size - flex_pixel_y} + // left + x, + // top + y + flex_pixel_y, + // right + r, + // bottom + y + flex_pixel_y + element.size + flex_pixel_y }; child->set_rect(box); - y = box.right_bottom().y + flex_pixel_y; + y = y + flex_pixel_y + element.size + flex_pixel_y; } ); } From aa91a6483a1a7a7ada40fb143257c94708a8f842 Mon Sep 17 00:00:00 2001 From: life4gal Date: Tue, 3 Sep 2024 17:38:25 +0800 Subject: [PATCH 014/258] `feat`: Add draw.element.box_grid. --- src/draw/element.impl.ixx | 2 +- src/draw/element.ixx | 413 +++++++++++++++++++------------- src/draw/element.layout.ixx | 461 ++++++++++++++++++++++++------------ 3 files changed, 553 insertions(+), 323 deletions(-) diff --git a/src/draw/element.impl.ixx b/src/draw/element.impl.ixx index 92f269de..80e78764 100644 --- a/src/draw/element.impl.ixx +++ b/src/draw/element.impl.ixx @@ -39,7 +39,7 @@ namespace gal::prometheus::draw auto Style::instance() noexcept -> Style& { - Style style{ + static Style style{ .font_pixel_size = 18.f, .line_pixel_width = 1.f, .flex_pixel_x = 3.f, diff --git a/src/draw/element.ixx b/src/draw/element.ixx index 1dca6e3b..d94afdab 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -21,6 +21,7 @@ import :style; #include #include +#include #include #include @@ -40,7 +41,18 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) } using element_type = std::shared_ptr; + using elements_type = std::vector; + using elements_view_type = std::span; + + using element_matrix_type = std::vector; + using element_matrix_view_type = std::span; + + template + concept derived_element_t = std::derived_from; + + template + concept derived_elements_t = std::ranges::range and std::derived_from; template T, typename... Args> [[nodiscard]] constexpr auto make_element(Args&&... args) noexcept -> element_type @@ -137,9 +149,6 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) namespace element { - template - concept derived_element_t = std::derived_from; - template requires (derived_element_t and derived_element_t>) [[nodiscard]] constexpr auto operator|(Element&& element, Decorator&& decorator) noexcept -> element_type // @@ -236,13 +245,16 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) namespace impl { - // todo - #define LAMBDA_DEDUCING_THIS_WORKAROUND 1 + enum class BoxOption : std::uint32_t + { + HORIZONTAL = 0b0000'0001, + VERTICAL = 0b0000'0010, + GRID = 0b0000'0100, + }; enum class FlexOption : std::uint32_t { NONE = 0b0000'0000, - ALL = 0b0001'0000'0000, GROW = 0b0000'0001, SHRINK = 0b0000'0010, @@ -251,229 +263,292 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) VERTICAL = 0b0010'0000, }; - template - struct flex_option : std::integral_constant, (... + std::to_underlying(Os))> {}; - - #if LAMBDA_DEDUCING_THIS_WORKAROUND - template<> - struct flex_option<> : std::integral_constant, std::to_underlying(FlexOption::NONE)> {}; - #endif - enum class CenterOption : std::uint32_t { - ALL = 0b0000, - HORIZONTAL = 0b0001, - VERTICAL = 0b0010, + HORIZONTAL = 0b0000'0001, + VERTICAL = 0b0000'0010, }; - template - struct center_option : std::integral_constant, (... + std::to_underlying(Os))> {}; + template + struct options : std::integral_constant>...>, (0 | ... | std::to_underlying(Os))> {}; - #if LAMBDA_DEDUCING_THIS_WORKAROUND - template<> - struct center_option<> : std::integral_constant, std::to_underlying(CenterOption::ALL)> {}; - #endif + [[nodiscard]] auto box_horizontal(elements_type elements) noexcept -> element_type; + [[nodiscard]] auto box_vertical(elements_type elements) noexcept -> element_type; + [[nodiscard]] auto box_grid(element_matrix_type elements_grid) noexcept -> element_type; - [[nodiscard]] auto filler() noexcept -> element_type; - [[nodiscard]] auto no_flex(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_filler() noexcept -> element_type; + [[nodiscard]] auto flex_none(element_type element) noexcept -> element_type; [[nodiscard]] auto flex(element_type element) noexcept -> element_type; [[nodiscard]] auto flex_grow(element_type element) noexcept -> element_type; [[nodiscard]] auto flex_shrink(element_type element) noexcept -> element_type; - [[nodiscard]] auto horizontal_flex(element_type element) noexcept -> element_type; - [[nodiscard]] auto horizontal_flex_grow(element_type element) noexcept -> element_type; - [[nodiscard]] auto horizontal_flex_shrink(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_horizontal(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_horizontal_grow(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_horizontal_shrink(element_type element) noexcept -> element_type; - [[nodiscard]] auto vertical_flex(element_type element) noexcept -> element_type; - [[nodiscard]] auto vertical_flex_grow(element_type element) noexcept -> element_type; - [[nodiscard]] auto vertical_flex_shrink(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_vertical(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_vertical_grow(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_vertical_shrink(element_type element) noexcept -> element_type; - [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type; - [[nodiscard]] auto vertical_box(elements_type elements) noexcept -> element_type; - [[nodiscard]] auto stack_box(elements_type elements) noexcept -> element_type; - - [[nodiscard]] auto horizontal_center(element_type element) noexcept -> element_type; - [[nodiscard]] auto vertical_center(element_type element) noexcept -> element_type; + [[nodiscard]] auto center_horizontal(element_type element) noexcept -> element_type; + [[nodiscard]] auto center_vertical(element_type element) noexcept -> element_type; [[nodiscard]] auto center(element_type element) noexcept -> element_type; } - #if LAMBDA_DEDUCING_THIS_WORKAROUND - constexpr auto flex_option_none = impl::flex_option{}; - #else - constexpr auto flex_option_none = impl::flex_option{}; - #endif - constexpr auto flex_option_all = impl::flex_option{}; - constexpr auto flex_option_grow = impl::flex_option{}; - constexpr auto flex_option_shrink = impl::flex_option{}; - constexpr auto flex_option_horizontal_grow = impl::flex_option{}; - constexpr auto flex_option_horizontal_shrink = impl::flex_option{}; - constexpr auto flex_option_vertical_grow = impl::flex_option{}; - constexpr auto flex_option_vertical_shrink = impl::flex_option{}; - - #if LAMBDA_DEDUCING_THIS_WORKAROUND - constexpr auto center_option_all = impl::center_option{}; - #else - constexpr auto center_option_all = impl::center_option{}; - #endif - constexpr auto center_option_horizontal = impl::center_option{}; - constexpr auto center_option_vertical = impl::center_option{}; - - constexpr auto flex = functional::overloaded{ - []( - element_type element, - const impl::flex_option - #if LAMBDA_DEDUCING_THIS_WORKAROUND - = flex_option_none - #endif - ) noexcept -> element_type + constexpr auto option_box_horizontal = impl::options{}; + constexpr auto option_box_vertical = impl::options{}; + constexpr auto option_box_grid = impl::options{}; + + constexpr auto option_flex_none = impl::options{}; + constexpr auto option_flex_grow = impl::options{}; + constexpr auto option_flex_shrink = impl::options{}; + constexpr auto option_flex_horizontal_grow = impl::options{}; + constexpr auto option_flex_horizontal_shrink = impl::options{}; + constexpr auto option_flex_vertical_grow = impl::options{}; + constexpr auto option_flex_vertical_shrink = impl::options{}; + constexpr auto option_flex_all = impl::options{}; + + constexpr auto option_center_horizontal = impl::options{}; + constexpr auto option_center_vertical = impl::options{}; + constexpr auto option_center_all = impl::options{}; + + constexpr auto layout = functional::overloaded{ + // BOX + [](impl::options, elements_type elements) noexcept -> element_type { - #if LAMBDA_DEDUCING_THIS_WORKAROUND - constexpr auto value = std::conditional_t, impl::flex_option>::value; - #else - constexpr auto value = impl::flex_option::value; - #endif - if constexpr (value & std::to_underlying(impl::FlexOption::GROW)) + if constexpr (constexpr auto value = impl::options::value; + value == option_box_horizontal) { - if constexpr (value & std::to_underlying(impl::FlexOption::HORIZONTAL)) - { - return impl::horizontal_flex_grow(std::move(element)); - } - else if constexpr (value & std::to_underlying(impl::FlexOption::VERTICAL)) - { - return impl::vertical_flex_grow(std::move(element)); - } - else - { - static_assert(value == std::to_underlying(impl::FlexOption::GROW)); - return impl::flex_grow(std::move(element)); - } + return impl::box_horizontal(std::move(elements)); } - else if constexpr (value & std::to_underlying(impl::FlexOption::SHRINK)) + else if constexpr (value == option_box_vertical) { - if constexpr (value & std::to_underlying(impl::FlexOption::HORIZONTAL)) - { - return impl::horizontal_flex_shrink(std::move(element)); - } - else if constexpr (value & std::to_underlying(impl::FlexOption::VERTICAL)) - { - return impl::vertical_flex_shrink(std::move(element)); - } - else - { - static_assert(value == std::to_underlying(impl::FlexOption::SHRINK)); - return impl::flex_shrink(std::move(element)); - } + return impl::box_vertical(std::move(elements)); } - else if constexpr (value == std::to_underlying(impl::FlexOption::ALL)) + else { - return impl::flex(std::move(element)); + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } + }, + [](impl::options, element_matrix_type elements_matrix) noexcept -> element_type + { + if constexpr (constexpr auto value = impl::options::value; + value == option_box_grid) + { + return impl::box_grid(std::move(elements_matrix)); } else { - static_assert(value == std::to_underlying(impl::FlexOption::NONE)); - return impl::no_flex(std::move(element)); + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); } }, - #if not LAMBDA_DEDUCING_THIS_WORKAROUND - [](this Self&& self, element_type element) noexcept -> element_type + [](this const Self& self, impl::options options, derived_element_t auto... elements) noexcept -> element_type + { + elements_type es{}; + es.reserve(sizeof...(elements)); + + (es.emplace_back(std::move(elements)), ...); + + return self(options, std::move(es)); + }, + // (options, element...) => (options, elements) + // (options, element...) !=> (options, elements...) + [](this const Self& self, impl::options options, derived_elements_t auto... elements) noexcept -> element_type // + requires (sizeof...(elements) != 1) { - return std::forward(self)(std::move(element), flex_option_none); + element_matrix_type es{}; + es.reserve(sizeof...(elements)); + + (es.emplace_back(std::move(elements)), ...); + + return self(options, std::move(es)); }, - #endif - [](this Self&& self, const impl::flex_option options) noexcept -> auto + [](this const Self& self, impl::options options) noexcept -> auto { - return [s = std::forward(self), options](element_type element) noexcept -> element_type + return functional::overloaded{ + [self, options](elements_type elements) noexcept -> element_type + { + return self(std::move(elements), options); + }, + [self, options](derived_element_t auto... elements) noexcept -> element_type + { + return self(options, std::move(elements)...); + }, + [self, options](derived_elements_t auto... elements) noexcept -> element_type + { + return self(options, std::move(elements)...); + } + }; + }, + // FLEX + [](impl::options, element_type element) noexcept -> element_type + { + if constexpr (constexpr auto value = impl::options::value; + value == option_flex_none) { - return s(std::move(element), options); + return impl::flex_none(std::move(element)); + } + else if constexpr (value == option_flex_grow) + { + return impl::flex_grow(std::move(element)); + } + else if constexpr (value == option_flex_shrink) + { + return impl::flex_shrink(std::move(element)); + } + else if constexpr (value == option_flex_horizontal_grow) + { + return impl::flex_horizontal_grow(std::move(element)); + } + else if constexpr (value == option_flex_horizontal_shrink) + { + return impl::flex_horizontal_shrink(std::move(element)); + } + else if constexpr (value == option_flex_vertical_grow) + { + return impl::flex_vertical_grow(std::move(element)); + } + else if constexpr (value == option_flex_vertical_shrink) + { + return impl::flex_vertical_shrink(std::move(element)); + } + else if constexpr (value == option_flex_all) + { + return impl::flex(std::move(element)); + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } + }, + [](this const Self& self, impl::options options) noexcept -> auto + { + return [self, options](element_type element) noexcept -> element_type + { + return self(options, std::move(element)); }; }, - }; - - constexpr auto horizontal_box = functional::overloaded{ - [](elements_type elements) noexcept -> element_type + // CENTER + [](impl::options, element_type element) noexcept -> element_type { - return impl::horizontal_box(std::move(elements)); + if constexpr (constexpr auto value = impl::options::value; + value == option_center_horizontal) + { + return impl::center_horizontal(std::move(element)); + } + else if constexpr (value == option_center_vertical) + { + return impl::center_vertical(std::move(element)); + } + else if constexpr (value == option_center_all) + { + return impl::center(std::move(element)); + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } }, - [](derived_element_t auto... elements) noexcept -> element_type + [](this const Self& self, impl::options options) noexcept -> auto { - elements_type es{}; - es.reserve(sizeof...(elements)); - - (es.emplace_back(std::move(elements)), ...); - return impl::horizontal_box(std::move(es)); + return [self, options](element_type element) noexcept -> element_type + { + return self(options, std::move(element)); + }; }, }; - constexpr auto vertical_box = functional::overloaded{ + // ---------------------- + // ALIAS + + constexpr auto box_horizontal = functional::overloaded{ [](elements_type elements) noexcept -> element_type { - return impl::vertical_box(std::move(elements)); + return layout(option_box_horizontal, std::move(elements)); }, [](derived_element_t auto... elements) noexcept -> element_type { - elements_type es{}; - es.reserve(sizeof...(elements)); - - (es.emplace_back(std::move(elements)), ...); - return impl::vertical_box(std::move(es)); + return layout(option_box_horizontal, std::move(elements)...); }, }; - constexpr auto stack_box = functional::overloaded{ + constexpr auto box_vertical = functional::overloaded{ [](elements_type elements) noexcept -> element_type { - return impl::stack_box(std::move(elements)); + return layout(option_box_vertical, std::move(elements)); }, [](derived_element_t auto... elements) noexcept -> element_type { - elements_type es{}; - es.reserve(sizeof...(elements)); - - (es.emplace_back(std::move(elements)), ...); - return impl::stack_box(std::move(es)); + return layout(option_box_vertical, std::move(elements)...); }, }; - constexpr auto center = functional::overloaded - { - []( - element_type element, - const impl::center_option - #if LAMBDA_DEDUCING_THIS_WORKAROUND - = center_option_all - #endif - ) noexcept -> element_type - { - constexpr auto value = std::conditional_t, impl::center_option>::value; - if constexpr (value == std::to_underlying(impl::CenterOption::ALL)) - { - return impl::center(std::move(element)); - } - else if constexpr (value == std::to_underlying(impl::CenterOption::HORIZONTAL)) - { - return impl::horizontal_center(std::move(element)); - } - else - { - static_assert(value == std::to_underlying(impl::CenterOption::VERTICAL)); - return impl::vertical_center(std::move(element)); - } - }, - #if not LAMBDA_DEDUCING_THIS_WORKAROUND - [](this Self&& self, element_type element) noexcept -> element_type + constexpr auto box_grid = functional::overloaded{ + [](element_matrix_type elements) noexcept -> element_type { - return std::forward(self)(std::move(element), center_option_all); + return layout(option_box_grid, std::move(elements)); }, - #endif - [](this Self&& self, const impl::center_option options) noexcept -> auto + [](derived_elements_t auto... elements) noexcept -> element_type { - return [s = std::forward(self), options](element_type element) noexcept -> element_type - { - return s(std::move(element), options); - }; + return layout(option_box_grid, std::move(elements)...); }, }; + + constexpr auto flex_none = [](element_type element) noexcept -> element_type + { + return layout(option_flex_none, std::move(element)); + }; + + constexpr auto flex_grow = [](element_type element) noexcept -> element_type + { + return layout(option_flex_grow, std::move(element)); + }; + + constexpr auto flex_shrink = [](element_type element) noexcept -> element_type + { + return layout(option_flex_shrink, std::move(element)); + }; + + constexpr auto flex_horizontal_grow = [](element_type element) noexcept -> element_type + { + return layout(option_flex_horizontal_grow, std::move(element)); + }; + + constexpr auto flex_horizontal_shrink = [](element_type element) noexcept -> element_type + { + return layout(option_flex_horizontal_shrink, std::move(element)); + }; + + constexpr auto flex_vertical_grow = [](element_type element) noexcept -> element_type + { + return layout(option_flex_vertical_grow, std::move(element)); + }; + + constexpr auto flex_vertical_shrink = [](element_type element) noexcept -> element_type + { + return layout(option_flex_vertical_shrink, std::move(element)); + }; + + constexpr auto flex_all = [](element_type element) noexcept -> element_type + { + return layout(option_flex_all, std::move(element)); + }; + + constexpr auto center_horizontal = [](element_type element) noexcept -> element_type + { + return layout(option_center_horizontal, std::move(element)); + }; + + constexpr auto center_vertical = [](element_type element) noexcept -> element_type + { + return layout(option_center_vertical, std::move(element)); + }; + + constexpr auto center_all = [](element_type element) noexcept -> element_type + { + return layout(option_center_all, std::move(element)); + }; } } diff --git a/src/draw/element.layout.ixx b/src/draw/element.layout.ixx index b8570d94..5fd87baa 100644 --- a/src/draw/element.layout.ixx +++ b/src/draw/element.layout.ixx @@ -34,71 +34,6 @@ namespace using namespace gal::prometheus; using namespace draw; - namespace function - { - using impl::requirement_type; - - auto flex(requirement_type& requirement) noexcept -> void - { - requirement.flex_grow_width = Style::instance().flex_pixel_x; - requirement.flex_grow_height = Style::instance().flex_pixel_y; - requirement.flex_shrink_width = Style::instance().flex_pixel_x; - requirement.flex_shrink_height = Style::instance().flex_pixel_y; - } - - auto no_flex(requirement_type& requirement) noexcept -> void - { - requirement.flex_grow_width = 0; - requirement.flex_grow_height = 0; - requirement.flex_shrink_width = 0; - requirement.flex_shrink_height = 0; - } - - auto flex_grow(requirement_type& requirement) noexcept -> void - { - requirement.flex_grow_width = Style::instance().flex_pixel_x; - requirement.flex_grow_height = Style::instance().flex_pixel_y; - } - - auto flex_shrink(requirement_type& requirement) noexcept -> void - { - requirement.flex_shrink_width = Style::instance().flex_pixel_x; - requirement.flex_shrink_height = Style::instance().flex_pixel_y; - } - - auto horizontal_flex(requirement_type& requirement) noexcept -> void - { - requirement.flex_grow_width = Style::instance().flex_pixel_x; - requirement.flex_shrink_width = Style::instance().flex_pixel_x; - } - - auto horizontal_flex_grow(requirement_type& requirement) noexcept -> void - { - requirement.flex_grow_width = Style::instance().flex_pixel_x; - } - - auto horizontal_flex_shrink(requirement_type& requirement) noexcept -> void - { - requirement.flex_shrink_width = Style::instance().flex_pixel_x; - } - - auto vertical_flex(requirement_type& requirement) noexcept -> void - { - requirement.flex_grow_height = Style::instance().flex_pixel_y; - requirement.flex_shrink_height = Style::instance().flex_pixel_y; - } - - auto vertical_flex_grow(requirement_type& requirement) noexcept -> void - { - requirement.flex_grow_height = Style::instance().flex_pixel_y; - } - - auto vertical_flex_shrink(requirement_type& requirement) noexcept -> void - { - requirement.flex_shrink_height = Style::instance().flex_pixel_y; - } - } - struct element_size { float min_size = 0; @@ -199,49 +134,6 @@ namespace } } - class Flex final : public impl::Element - { - public: - using function_type = void(*)(impl::requirement_type&); - - private: - function_type function_; - - public: - explicit Flex(const function_type function) noexcept - : Element{}, - function_{function} {} - - Flex(const function_type function, element_type element) noexcept - : Element{elements_type{std::move(element)}}, - function_{function} {} - - auto calculate_requirement(Surface& surface) noexcept -> void override - { - requirement_.reset(); - - if (not children_.empty()) - [[unlikely]] - { - children_[0]->calculate_requirement(surface); - requirement_ = children_[0]->requirement(); - } - - function_(requirement_); - } - - auto set_rect(const rect_type& rect) noexcept -> void override - { - if (children_.empty()) - [[unlikely]] - { - return; - } - - children_[0]->set_rect(rect); - } - }; - class HorizontalBox final : public impl::Element { public: @@ -296,7 +188,7 @@ namespace auto x = point.x; std::ranges::for_each( std::views::zip(children_, elements), - [&x, y = point.y, b = point.y + extent.height, flex_pixel_x= Style::instance().flex_pixel_x](std::tuple pack) noexcept -> void + [&x, y = point.y, b = point.y + extent.height, flex_pixel_x = Style::instance().flex_pixel_x](std::tuple pack) noexcept -> void { auto& [child, element] = pack; @@ -394,57 +286,335 @@ namespace } }; - class StackBox final : public impl::Element + class GridBox final : public impl::Element { public: - explicit StackBox(elements_type children) noexcept - : Element{std::move(children)} {} + using element_grid_type = element_matrix_type; + using size_type = element_grid_type::size_type; + + private: + element_grid_type element_grid_; + size_type width_; + size_type height_; + + public: + explicit GridBox(element_grid_type element_grid) noexcept + : Element{}, + element_grid_{std::move(element_grid)}, + width_{0}, + height_{element_grid_.size()} + { + width_ = std::ranges::max_element(element_grid_, {}, &element_grid_type::value_type::size)->size(); + + std::ranges::for_each( + element_grid_, + [this](auto& line) noexcept -> void + { + if (const auto diff = width_ - line.size(); + diff != 0) + { + std::ranges::generate_n(std::back_inserter(line), diff, &element::impl::flex_filler); + } + } + ); + } auto calculate_requirement(Surface& surface) noexcept -> void override { requirement_.reset(); std::ranges::for_each( - children_, - [this, &surface](const auto& child) noexcept -> void + element_grid_, + [&surface](auto& line) noexcept -> void { - child->calculate_requirement(surface); - - requirement_.min_width = std::ranges::max( - requirement_.min_width, - child->requirement().min_width + std::ranges::for_each( + line, + [&surface](auto& element) noexcept -> void + { + element->calculate_requirement(surface); + } ); - requirement_.min_height = std::ranges::max( - requirement_.min_height, - child->requirement().min_height + } + ); + + // Compute the size of each column/row + std::vector size_x{}; + std::vector size_y{}; + size_x.resize(width_); + size_y.reserve(height_); + + std::ranges::for_each( + element_grid_, + [&size_x, &size_y, flex_pixel_x = Style::instance().flex_pixel_x, flex_pixel_y = Style::instance().flex_pixel_y](const auto& line) noexcept -> void + { + const auto view = line | std::views::transform([](const auto& element) noexcept -> const auto& { return element->requirement(); }); + const auto& max_y = std::ranges::max_element(view, {}, &impl::requirement_type::min_height); + + size_y.emplace_back(max_y.base()->get()->requirement().min_height + 2 * flex_pixel_y); + + std::ranges::for_each( + std::views::zip(size_x, line), + [flex_pixel_x](std::tuple pack) noexcept -> void + { + auto& [x, element] = pack; + x = std::ranges::max(x, element->requirement().min_width + 2 * flex_pixel_x); + } ); } ); + + const auto integrate = [](std::vector& size) noexcept -> float + { + float accumulate = 0.f; + std::ranges::for_each( + size, + [&accumulate](auto& s) noexcept -> void + { + accumulate += std::exchange(s, accumulate); + } + ); + + return accumulate; + }; + + requirement_.min_width = integrate(size_x); + requirement_.min_height = integrate(size_y); } auto set_rect(const rect_type& rect) noexcept -> void override { Element::set_rect(rect); + std::vector elements_x{width_, {.min_size = 0, .flex_grow = std::numeric_limits::max(), .flex_shrink = std::numeric_limits::max(), .size = 0}}; + std::vector elements_y{height_, {.min_size = 0, .flex_grow = std::numeric_limits::max(), .flex_shrink = std::numeric_limits::max(), .size = 0}}; + std::ranges::for_each( - children_, - [rect](auto& child) noexcept -> void + std::views::zip(elements_y, element_grid_), + [&elements_x](std::tuple pack) noexcept -> void { - child->set_rect(rect); + auto& [y, line] = pack; + + std::ranges::for_each( + std::views::zip(elements_x, line), + [&y](std::tuple inner_pack) noexcept -> void + { + auto& [x, element] = inner_pack; + + const auto& [min_width, min_height, flex_grow_width, flex_grow_height, flex_shrink_width, flex_shrink_height] = element->requirement(); + + x.min_size = std::ranges::max(x.min_size, min_width); + x.flex_grow = std::ranges::min(x.flex_grow, flex_grow_width); + x.flex_shrink = std::ranges::min(x.flex_shrink, flex_shrink_width); + + y.min_size = std::ranges::max(y.min_size, min_height); + y.flex_grow = std::ranges::min(y.flex_grow, flex_grow_height); + y.flex_shrink = std::ranges::min(y.flex_shrink, flex_shrink_height); + } + ); } ); + + calculate(elements_x, rect.width()); + calculate(elements_y, rect.height()); + + const auto& [point, extent] = rect; + + auto [x, y] = point; + std::ranges::for_each( + std::views::zip(elements_y, element_grid_), + [ + &elements_x , + x, + &y, + flex_pixel_x = Style::instance().flex_pixel_x, + flex_pixel_y = Style::instance().flex_pixel_y + ](std::tuple pack) noexcept -> void + { + auto& [element_y, line] = pack; + + std::ranges::for_each( + std::views::zip(elements_x, line), + [ + current_x = x, + current_y = y, + &element_y, + flex_pixel_x, + flex_pixel_y + ](std::tuple inner_pack) mutable noexcept -> void + { + auto& [element_x, element] = inner_pack; + + const rect_type box + { + // left + current_x + flex_pixel_x, + // top + current_y + flex_pixel_y, + // right + current_x + flex_pixel_x + element_x.size + flex_pixel_x, + // bottom + current_y + flex_pixel_y + element_y.size + flex_pixel_y + }; + element->set_rect(box); + current_x = current_x + flex_pixel_x + element_x.size + flex_pixel_x; + } + ); + + y = y + flex_pixel_y + element_y.size + flex_pixel_y; + } + ); + } + + auto render(Surface& surface) noexcept -> void override + { + std::ranges::for_each( + element_grid_, + [&surface](auto& line) noexcept -> void + { + std::ranges::for_each( + line, + [&surface](auto& element) noexcept -> void + { + element->render(surface); + } + ); + } + ); + } + }; + + namespace function + { + using impl::requirement_type; + + auto flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + requirement.flex_grow_height = Style::instance().flex_pixel_y; + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + + auto no_flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = 0; + requirement.flex_grow_height = 0; + requirement.flex_shrink_width = 0; + requirement.flex_shrink_height = 0; + } + + auto flex_grow(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + requirement.flex_grow_height = Style::instance().flex_pixel_y; + } + + auto flex_shrink(requirement_type& requirement) noexcept -> void + { + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + + auto horizontal_flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + } + + auto horizontal_flex_grow(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_width = Style::instance().flex_pixel_x; + } + + auto horizontal_flex_shrink(requirement_type& requirement) noexcept -> void + { + requirement.flex_shrink_width = Style::instance().flex_pixel_x; + } + + auto vertical_flex(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_height = Style::instance().flex_pixel_y; + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + + auto vertical_flex_grow(requirement_type& requirement) noexcept -> void + { + requirement.flex_grow_height = Style::instance().flex_pixel_y; + } + + auto vertical_flex_shrink(requirement_type& requirement) noexcept -> void + { + requirement.flex_shrink_height = Style::instance().flex_pixel_y; + } + } + + class Flex final : public impl::Element + { + public: + using function_type = void(*)(impl::requirement_type&); + + private: + function_type function_; + + public: + explicit Flex(const function_type function) noexcept + : Element{}, + function_{function} {} + + Flex(const function_type function, element_type element) noexcept + : Element{elements_type{std::move(element)}}, + function_{function} {} + + auto calculate_requirement(Surface& surface) noexcept -> void override + { + requirement_.reset(); + + if (not children_.empty()) + [[unlikely]] + { + children_[0]->calculate_requirement(surface); + requirement_ = children_[0]->requirement(); + } + + function_(requirement_); + } + + auto set_rect(const rect_type& rect) noexcept -> void override + { + if (children_.empty()) + [[unlikely]] + { + return; + } + + children_[0]->set_rect(rect); } }; } namespace gal::prometheus::draw::element::impl { - [[nodiscard]] auto filler() noexcept -> element_type + [[nodiscard]] auto box_horizontal(elements_type elements) noexcept -> element_type + { + return make_element(std::move(elements)); + } + + [[nodiscard]] auto box_vertical(elements_type elements) noexcept -> element_type + { + return make_element(std::move(elements)); + } + + [[nodiscard]] auto box_grid(element_matrix_type elements_grid) noexcept -> element_type + { + return make_element(std::move(elements_grid)); + } + + [[nodiscard]] auto flex_filler() noexcept -> element_type { return make_element(function::flex); } - [[nodiscard]] auto no_flex(element_type element) noexcept -> element_type + [[nodiscard]] auto flex_none(element_type element) noexcept -> element_type { return make_element(function::no_flex, std::move(element)); } @@ -464,63 +634,48 @@ namespace gal::prometheus::draw::element::impl return make_element(function::flex_shrink, std::move(element)); } - [[nodiscard]] auto horizontal_flex(element_type element) noexcept -> element_type + [[nodiscard]] auto flex_horizontal(element_type element) noexcept -> element_type { return make_element(function::horizontal_flex, std::move(element)); } - [[nodiscard]] auto horizontal_flex_grow(element_type element) noexcept -> element_type + [[nodiscard]] auto flex_horizontal_grow(element_type element) noexcept -> element_type { return make_element(function::horizontal_flex_grow, std::move(element)); } - [[nodiscard]] auto horizontal_flex_shrink(element_type element) noexcept -> element_type + [[nodiscard]] auto flex_horizontal_shrink(element_type element) noexcept -> element_type { return make_element(function::horizontal_flex_shrink, std::move(element)); } - [[nodiscard]] auto vertical_flex(element_type element) noexcept -> element_type + [[nodiscard]] auto flex_vertical(element_type element) noexcept -> element_type { return make_element(function::vertical_flex, std::move(element)); } - [[nodiscard]] auto vertical_flex_grow(element_type element) noexcept -> element_type + [[nodiscard]] auto flex_vertical_grow(element_type element) noexcept -> element_type { return make_element(function::vertical_flex_grow, std::move(element)); } - [[nodiscard]] auto vertical_flex_shrink(element_type element) noexcept -> element_type + [[nodiscard]] auto flex_vertical_shrink(element_type element) noexcept -> element_type { return make_element(function::vertical_flex_shrink, std::move(element)); } - [[nodiscard]] auto horizontal_box(elements_type elements) noexcept -> element_type - { - return make_element(std::move(elements)); - } - - [[nodiscard]] auto vertical_box(elements_type elements) noexcept -> element_type - { - return make_element(std::move(elements)); - } - - [[nodiscard]] auto stack_box(elements_type elements) noexcept -> element_type - { - return make_element(std::move(elements)); - } - - [[nodiscard]] auto horizontal_center(element_type element) noexcept -> element_type + [[nodiscard]] auto center_horizontal(element_type element) noexcept -> element_type { - return element::horizontal_box(filler(), std::move(element), filler()); + return element::box_horizontal(flex_filler(), std::move(element), flex_filler()); } - [[nodiscard]] auto vertical_center(element_type element) noexcept -> element_type + [[nodiscard]] auto center_vertical(element_type element) noexcept -> element_type { - return element::vertical_box(filler(), std::move(element), filler()); + return element::box_vertical(flex_filler(), std::move(element), flex_filler()); } [[nodiscard]] auto center(element_type element) noexcept -> element_type { - return horizontal_center(vertical_center(std::move(element))); + return center_horizontal(center_vertical(std::move(element))); } } From 85d0d4c2f8f54eec06bdf202833e48d18c645816 Mon Sep 17 00:00:00 2001 From: life4gal Date: Wed, 4 Sep 2024 10:40:39 +0800 Subject: [PATCH 015/258] `fix`: Add border rounding. --- src/draw/element.border.ixx | 17 ++++++++++++----- src/draw/element.impl.ixx | 4 +--- src/draw/element.ixx | 6 ++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/draw/element.border.ixx b/src/draw/element.border.ixx index c79f27b1..51e53d83 100644 --- a/src/draw/element.border.ixx +++ b/src/draw/element.border.ixx @@ -13,6 +13,7 @@ export module gal.prometheus.draw:element.border; import std; import gal.prometheus.primitive; +GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE import :surface; import :style; @@ -24,6 +25,7 @@ import :element; #include #include #include +#include GAL_PROMETHEUS_ERROR_DEBUG_MODULE #endif @@ -80,9 +82,13 @@ namespace auto render(Surface& surface) noexcept -> void override { - children_[0]->render(surface); + render(surface, DrawFlag::ROUND_CORNER_ALL); + } - surface.draw_list().rect(rect_, color_, 0, DrawFlag::ROUND_CORNER_ALL, Style::instance().line_pixel_width); + auto render(Surface& surface, const DrawFlag flag) const noexcept -> void + { + surface.draw_list().rect(rect_, color_, Style::instance().border_round, flag, Style::instance().line_pixel_width); + children_[0]->render(surface); } }; @@ -147,12 +153,13 @@ namespace auto render(Surface& surface) noexcept -> void override { // title - using functional::operators::operator|; - surface.draw_list().rect_filled(children_[0]->rect(), color_, 0, DrawFlag::ROUND_CORNER_LEFT_TOP | DrawFlag::ROUND_CORNER_RIGHT_TOP); + surface.draw_list().rect_filled(children_[0]->rect(), color_, Style::instance().border_round, DrawFlag::ROUND_CORNER_TOP); children_[0]->render(surface); // border - children_[1]->render(surface); + const auto border = cast_element_unchecked(children_[1]); + GAL_PROMETHEUS_ERROR_DEBUG_ASSUME(border != nullptr); + border->render(surface, DrawFlag::ROUND_CORNER_BOTTOM); } }; } diff --git a/src/draw/element.impl.ixx b/src/draw/element.impl.ixx index 80e78764..b95a01aa 100644 --- a/src/draw/element.impl.ixx +++ b/src/draw/element.impl.ixx @@ -12,8 +12,6 @@ export module gal.prometheus.draw:element.impl; import std; -GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE - import :surface; import :style; import :element; @@ -23,7 +21,6 @@ import :element; #include #include #include -#include GAL_PROMETHEUS_ERROR_DEBUG_MODULE #endif @@ -44,6 +41,7 @@ namespace gal::prometheus::draw .line_pixel_width = 1.f, .flex_pixel_x = 3.f, .flex_pixel_y = 3.f, + .border_round = 2.f, .border_default_color = primitive::colors::black, .window_title_default_color = primitive::colors::red }; diff --git a/src/draw/element.ixx b/src/draw/element.ixx index d94afdab..e76bf37e 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -62,6 +62,12 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) template T> [[nodiscard]] constexpr auto cast_element(const element_type element) noexcept -> std::shared_ptr + { + return std::dynamic_pointer_cast(element); + } + + template T> + [[nodiscard]] constexpr auto cast_element_unchecked(const element_type element) noexcept -> std::shared_ptr { return std::static_pointer_cast(element); } From e201f9bdd669f84d62f159d512532f75f4568420 Mon Sep 17 00:00:00 2001 From: life4gal Date: Wed, 4 Sep 2024 17:49:13 +0800 Subject: [PATCH 016/258] `feat`: * Add draw.element.separator. * Most calculations/drawings are controlled by draw.style. * Fix draw.element.layout padding/spacing issue. --- src/draw/element.border.ixx | 31 ++++--- src/draw/element.impl.ixx | 17 ++-- src/draw/element.ixx | 17 ++-- src/draw/element.layout.ixx | 160 +++++++++++++++++++++------------ src/draw/element.separator.ixx | 78 ++++++++++++++++ src/draw/element.text.ixx | 4 +- src/draw/style.ixx | 24 ++++- 7 files changed, 244 insertions(+), 87 deletions(-) create mode 100644 src/draw/element.separator.ixx diff --git a/src/draw/element.border.ixx b/src/draw/element.border.ixx index 51e53d83..d9d5bd1c 100644 --- a/src/draw/element.border.ixx +++ b/src/draw/element.border.ixx @@ -42,6 +42,16 @@ namespace private: color_type color_; + [[nodiscard]] static auto extra_offset() noexcept -> Style::extern_type + { + const auto line_width = Style::instance().line_width; + const auto& border_padding = Style::instance().border_padding; + const auto offset_x = line_width + border_padding.width; + const auto offset_y = line_width + border_padding.height; + + return {offset_x, offset_y}; + } + public: Border(element_type element, const color_type color) noexcept : Element{elements_type{std::move(element)}}, @@ -53,28 +63,29 @@ namespace requirement_ = children_[0]->requirement(); - const auto line_pixel_width = Style::instance().line_pixel_width; - requirement_.min_width += 2 * line_pixel_width; - requirement_.min_height += 2 * line_pixel_width; + const auto [offset_x, offset_y] = extra_offset(); + + requirement_.min_width += 2 * offset_x; + requirement_.min_height += 2 * offset_y; } auto set_rect(const rect_type& rect) noexcept -> void override { Element::set_rect(rect); - const auto line_pixel_width = Style::instance().line_pixel_width; + const auto [offset_x, offset_y] = extra_offset(); const auto& [point, extent] = rect; const rect_type box { // left - point.x + line_pixel_width, + point.x + offset_x, // top - point.y + line_pixel_width, + point.y + offset_y, // right - point.x + extent.width - 2 * line_pixel_width, + point.x + extent.width - offset_x, // bottom - point.y + extent.height - 2 * line_pixel_width + point.y + extent.height - offset_y }; children_[0]->set_rect(box); @@ -87,7 +98,7 @@ namespace auto render(Surface& surface, const DrawFlag flag) const noexcept -> void { - surface.draw_list().rect(rect_, color_, Style::instance().border_round, flag, Style::instance().line_pixel_width); + surface.draw_list().rect(rect_, color_, Style::instance().border_rounding, flag, Style::instance().line_width); children_[0]->render(surface); } }; @@ -153,7 +164,7 @@ namespace auto render(Surface& surface) noexcept -> void override { // title - surface.draw_list().rect_filled(children_[0]->rect(), color_, Style::instance().border_round, DrawFlag::ROUND_CORNER_TOP); + surface.draw_list().rect_filled(children_[0]->rect(), color_, Style::instance().border_rounding, DrawFlag::ROUND_CORNER_TOP); children_[0]->render(surface); // border diff --git a/src/draw/element.impl.ixx b/src/draw/element.impl.ixx index b95a01aa..a17a746b 100644 --- a/src/draw/element.impl.ixx +++ b/src/draw/element.impl.ixx @@ -28,7 +28,6 @@ namespace gal::prometheus::draw { auto Surface::render(impl::Element& element) noexcept -> void { - // todo element.calculate_requirement(*this); element.set_rect({rect_.point.x, rect_.point.y, rect_.point.x + element.requirement().min_width, rect_.point.y + element.requirement().min_height}); element.render(*this); @@ -37,13 +36,17 @@ namespace gal::prometheus::draw auto Style::instance() noexcept -> Style& { static Style style{ - .font_pixel_size = 18.f, - .line_pixel_width = 1.f, - .flex_pixel_x = 3.f, - .flex_pixel_y = 3.f, - .border_round = 2.f, + .font_size = 18.f, + .line_width = 1.f, + .separator_color = primitive::colors::red, + .flex_x = 3.f, + .flex_y = 3.f, + .container_padding = {1.f, 1.f}, + .container_spacing = {2.f, 2.f}, + .border_rounding = 2.f, + .border_padding = {3.f, 3.f}, .border_default_color = primitive::colors::black, - .window_title_default_color = primitive::colors::red + .window_title_default_color = primitive::colors::blue_violet }; return style; diff --git a/src/draw/element.ixx b/src/draw/element.ixx index e76bf37e..7478ebe4 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -49,10 +49,10 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) using element_matrix_view_type = std::span; template - concept derived_element_t = std::derived_from; + concept derived_element_t = std::is_base_of_v; template - concept derived_elements_t = std::ranges::range and std::derived_from; + concept derived_elements_t = std::ranges::range and derived_element_t; template T, typename... Args> [[nodiscard]] constexpr auto make_element(Args&&... args) noexcept -> element_type @@ -129,7 +129,7 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { std::ranges::for_each( children_, - [&surface](auto& node) noexcept -> void { node->calculate_requirement(surface); } + [&surface](auto& child) noexcept -> void { child->calculate_requirement(surface); } ); } @@ -147,7 +147,7 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { std::ranges::for_each( children_, - [&surface](auto& element) noexcept -> void { element->render(surface); } + [&surface](auto& child) noexcept -> void { child->render(surface); } ); } }; @@ -156,7 +156,7 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) namespace element { template - requires (derived_element_t and derived_element_t>) + requires (derived_element_t> and derived_element_t>) [[nodiscard]] constexpr auto operator|(Element&& element, Decorator&& decorator) noexcept -> element_type // { return std::invoke(std::forward(decorator), std::forward(element)); @@ -204,6 +204,13 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) [[nodiscard]] auto separator() noexcept -> element_type; } + constexpr auto separator = functional::overloaded{ + []() noexcept -> element_type + { + return impl::separator(); + }, + }; + // =============================== // BORDER & WINDOW diff --git a/src/draw/element.layout.ixx b/src/draw/element.layout.ixx index 5fd87baa..86918aa1 100644 --- a/src/draw/element.layout.ixx +++ b/src/draw/element.layout.ixx @@ -146,17 +146,25 @@ namespace std::ranges::for_each( children_, - [this, &surface, flex_pixel_x = Style::instance().flex_pixel_x](const auto& child) noexcept -> void + [this, &surface](const auto& child) noexcept -> void { child->calculate_requirement(surface); - requirement_.min_width += child->requirement().min_width + 2 * flex_pixel_x; + requirement_.min_width += child->requirement().min_width; requirement_.min_height = std::ranges::max( requirement_.min_height, child->requirement().min_height ); } ); + + const auto& container_padding = Style::instance().container_padding; + const auto& container_spacing = Style::instance().container_spacing; + const auto extra_x = 2 * container_padding.width + (children_.size() - 1) * container_spacing.width; + const auto extra_y = 2 * container_padding.height; + + requirement_.min_width += extra_x; + requirement_.min_height += extra_y; } auto set_rect(const rect_type& rect) noexcept -> void override @@ -181,30 +189,38 @@ namespace } ); - calculate(elements, rect.width()); + const auto& container_padding = Style::instance().container_padding; + const auto& container_spacing = Style::instance().container_spacing; + const auto extra_x = 2 * container_padding.width + (children_.size() - 1) * container_spacing.width; + + calculate(elements, rect.width() - extra_x); const auto& [point, extent] = rect; - auto x = point.x; std::ranges::for_each( std::views::zip(children_, elements), - [&x, y = point.y, b = point.y + extent.height, flex_pixel_x = Style::instance().flex_pixel_x](std::tuple pack) noexcept -> void + [ + current_x = point.x + container_padding.width, + current_y = point.y + container_padding.height, + current_bottom = point.y + extent.height - container_padding.height, + container_spacing = container_spacing.width + ](std::tuple pack) mutable noexcept -> void { auto& [child, element] = pack; const rect_type box { // left - x + flex_pixel_x, + current_x, // top - y, + current_y, // right - x + flex_pixel_x + element.size + flex_pixel_x, + current_x + element.size, // bottom - b + current_bottom }; child->set_rect(box); - x = x + flex_pixel_x + element.size + flex_pixel_x; + current_x = current_x + element.size + container_spacing; } ); } @@ -222,17 +238,25 @@ namespace std::ranges::for_each( children_, - [this, &surface, flex_pixel_y = Style::instance().flex_pixel_y](const auto& child) noexcept -> void + [this, &surface](const auto& child) noexcept -> void { child->calculate_requirement(surface); - requirement_.min_height += child->requirement().min_height + 2 * flex_pixel_y; + requirement_.min_height += child->requirement().min_height; requirement_.min_width = std::ranges::max( requirement_.min_width, child->requirement().min_width ); } ); + + const auto& container_padding = Style::instance().container_padding; + const auto& container_spacing = Style::instance().container_spacing; + const auto extra_x = 2 * container_padding.width; + const auto extra_y = 2 * container_padding.height + (children_.size() - 1) * container_spacing.height; + + requirement_.min_width += extra_x; + requirement_.min_height += extra_y; } auto set_rect(const rect_type& rect) noexcept -> void override @@ -257,30 +281,38 @@ namespace } ); - calculate(elements, rect.height()); + const auto& container_padding = Style::instance().container_padding; + const auto& container_spacing = Style::instance().container_spacing; + const auto extra_y = 2 * container_padding.height + (children_.size() - 1) * container_spacing.height; + + calculate(elements, rect.height() - extra_y); const auto& [point, extent] = rect; - auto y = point.y; std::ranges::for_each( std::views::zip(children_, elements), - [&y, x = point.x, r = point.x + extent.width, flex_pixel_y = Style::instance().flex_pixel_y](std::tuple pack) noexcept -> void + [ + current_x = point.x + container_padding.width, + current_y = point.y + container_padding.height, + current_right = point.x + extent.width - container_padding.width, + container_spacing = container_spacing.height + ](std::tuple pack) mutable noexcept -> void { auto& [child, element] = pack; const rect_type box { // left - x, + current_x, // top - y + flex_pixel_y, + current_y, // right - r, + current_right, // bottom - y + flex_pixel_y + element.size + flex_pixel_y + current_y + element.size }; child->set_rect(box); - y = y + flex_pixel_y + element.size + flex_pixel_y; + current_y = current_y + element.size + container_spacing; } ); } @@ -345,19 +377,22 @@ namespace std::ranges::for_each( element_grid_, - [&size_x, &size_y, flex_pixel_x = Style::instance().flex_pixel_x, flex_pixel_y = Style::instance().flex_pixel_y](const auto& line) noexcept -> void + [ + &size_x, + &size_y + ](const auto& line) noexcept -> void { const auto view = line | std::views::transform([](const auto& element) noexcept -> const auto& { return element->requirement(); }); const auto& max_y = std::ranges::max_element(view, {}, &impl::requirement_type::min_height); - size_y.emplace_back(max_y.base()->get()->requirement().min_height + 2 * flex_pixel_y); + size_y.emplace_back(max_y.base()->get()->requirement().min_height); std::ranges::for_each( std::views::zip(size_x, line), - [flex_pixel_x](std::tuple pack) noexcept -> void + [](std::tuple pack) noexcept -> void { auto& [x, element] = pack; - x = std::ranges::max(x, element->requirement().min_width + 2 * flex_pixel_x); + x = std::ranges::max(x, element->requirement().min_width); } ); } @@ -377,8 +412,13 @@ namespace return accumulate; }; - requirement_.min_width = integrate(size_x); - requirement_.min_height = integrate(size_y); + const auto& container_padding = Style::instance().container_padding; + const auto& container_spacing = Style::instance().container_spacing; + const auto extra_x = 2 * container_padding.width + (width_ - 1) * container_spacing.width; + const auto extra_y = 2 * container_padding.height + (height_ - 1) * container_spacing.height; + + requirement_.min_width = integrate(size_x) + extra_x; + requirement_.min_height = integrate(size_y) + extra_y; } auto set_rect(const rect_type& rect) noexcept -> void override @@ -414,21 +454,24 @@ namespace } ); - calculate(elements_x, rect.width()); - calculate(elements_y, rect.height()); + const auto& container_padding = Style::instance().container_padding; + const auto& container_spacing = Style::instance().container_spacing; + const auto extra_x = 2 * container_padding.width + (width_ - 1) * container_spacing.width; + const auto extra_y = 2 * container_padding.height + (height_ - 1) * container_spacing.height; + + calculate(elements_x, rect.width() - extra_x); + calculate(elements_y, rect.height() - extra_y); const auto& [point, extent] = rect; - auto [x, y] = point; std::ranges::for_each( std::views::zip(elements_y, element_grid_), [ &elements_x , - x, - &y, - flex_pixel_x = Style::instance().flex_pixel_x, - flex_pixel_y = Style::instance().flex_pixel_y - ](std::tuple pack) noexcept -> void + x = point.x + container_padding.width, + y = point.y + container_padding.height, + container_spacing = container_spacing + ](std::tuple pack) mutable noexcept -> void { auto& [element_y, line] = pack; @@ -437,9 +480,8 @@ namespace [ current_x = x, current_y = y, - &element_y, - flex_pixel_x, - flex_pixel_y + current_height = element_y.size, + container_spacing ](std::tuple inner_pack) mutable noexcept -> void { auto& [element_x, element] = inner_pack; @@ -447,20 +489,20 @@ namespace const rect_type box { // left - current_x + flex_pixel_x, + current_x, // top - current_y + flex_pixel_y, + current_y, // right - current_x + flex_pixel_x + element_x.size + flex_pixel_x, + current_x + element_x.size, // bottom - current_y + flex_pixel_y + element_y.size + flex_pixel_y + current_y + current_height }; element->set_rect(box); - current_x = current_x + flex_pixel_x + element_x.size + flex_pixel_x; + current_x = current_x + element_x.size + container_spacing.width; } ); - y = y + flex_pixel_y + element_y.size + flex_pixel_y; + y = y + element_y.size + container_spacing.height; } ); } @@ -489,10 +531,10 @@ namespace auto flex(requirement_type& requirement) noexcept -> void { - requirement.flex_grow_width = Style::instance().flex_pixel_x; - requirement.flex_grow_height = Style::instance().flex_pixel_y; - requirement.flex_shrink_width = Style::instance().flex_pixel_x; - requirement.flex_shrink_height = Style::instance().flex_pixel_y; + requirement.flex_grow_width = Style::instance().flex_x; + requirement.flex_grow_height = Style::instance().flex_y; + requirement.flex_shrink_width = Style::instance().flex_x; + requirement.flex_shrink_height = Style::instance().flex_y; } auto no_flex(requirement_type& requirement) noexcept -> void @@ -505,46 +547,46 @@ namespace auto flex_grow(requirement_type& requirement) noexcept -> void { - requirement.flex_grow_width = Style::instance().flex_pixel_x; - requirement.flex_grow_height = Style::instance().flex_pixel_y; + requirement.flex_grow_width = Style::instance().flex_x; + requirement.flex_grow_height = Style::instance().flex_y; } auto flex_shrink(requirement_type& requirement) noexcept -> void { - requirement.flex_shrink_width = Style::instance().flex_pixel_x; - requirement.flex_shrink_height = Style::instance().flex_pixel_y; + requirement.flex_shrink_width = Style::instance().flex_x; + requirement.flex_shrink_height = Style::instance().flex_y; } auto horizontal_flex(requirement_type& requirement) noexcept -> void { - requirement.flex_grow_width = Style::instance().flex_pixel_x; - requirement.flex_shrink_width = Style::instance().flex_pixel_x; + requirement.flex_grow_width = Style::instance().flex_x; + requirement.flex_shrink_width = Style::instance().flex_x; } auto horizontal_flex_grow(requirement_type& requirement) noexcept -> void { - requirement.flex_grow_width = Style::instance().flex_pixel_x; + requirement.flex_grow_width = Style::instance().flex_x; } auto horizontal_flex_shrink(requirement_type& requirement) noexcept -> void { - requirement.flex_shrink_width = Style::instance().flex_pixel_x; + requirement.flex_shrink_width = Style::instance().flex_x; } auto vertical_flex(requirement_type& requirement) noexcept -> void { - requirement.flex_grow_height = Style::instance().flex_pixel_y; - requirement.flex_shrink_height = Style::instance().flex_pixel_y; + requirement.flex_grow_height = Style::instance().flex_y; + requirement.flex_shrink_height = Style::instance().flex_y; } auto vertical_flex_grow(requirement_type& requirement) noexcept -> void { - requirement.flex_grow_height = Style::instance().flex_pixel_y; + requirement.flex_grow_height = Style::instance().flex_y; } auto vertical_flex_shrink(requirement_type& requirement) noexcept -> void { - requirement.flex_shrink_height = Style::instance().flex_pixel_y; + requirement.flex_shrink_height = Style::instance().flex_y; } } diff --git a/src/draw/element.separator.ixx b/src/draw/element.separator.ixx new file mode 100644 index 00000000..0c374c37 --- /dev/null +++ b/src/draw/element.separator.ixx @@ -0,0 +1,78 @@ +// This file is part of prometheus +// Copyright (C) 2022-2024 Life4gal +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#if GAL_PROMETHEUS_USE_MODULE +module; + +#include + +export module gal.prometheus.draw:element.separator; + +import std; + +import gal.prometheus.primitive; + +import :surface; +import :style; +import :element; + +#else +#include + +#include +#include +#include +#include +#include + +#endif + +namespace +{ + using namespace gal::prometheus; + using namespace draw; + + class Separator final : public impl::Element + { + public: + explicit Separator() noexcept = default; + + auto calculate_requirement([[maybe_unused]] Surface& surface) noexcept -> void override + { + const auto line_width = Style::instance().line_width; + requirement_.min_width = line_width; + requirement_.min_height = line_width; + } + + auto render(Surface& surface) noexcept -> void override + { + if (const auto line_width = Style::instance().line_width; + rect_.width() == line_width) + { + const auto from = rect_.left_top(); + auto to = rect_.right_bottom(); + to.x -= line_width; + + surface.draw_list().line(from, to, Style::instance().separator_color, line_width); + } + else + { + const auto from = rect_.left_top(); + auto to = rect_.right_bottom(); + to.y -= line_width; + + surface.draw_list().line(from, to, Style::instance().separator_color, line_width); + } + } + }; +} + +namespace gal::prometheus::draw::element::impl +{ + [[nodiscard]] auto separator() noexcept -> element_type + { + return make_element(); + } +} diff --git a/src/draw/element.text.ixx b/src/draw/element.text.ixx index d42c25e3..c73eb309 100644 --- a/src/draw/element.text.ixx +++ b/src/draw/element.text.ixx @@ -99,14 +99,14 @@ namespace auto calculate_requirement(Surface& surface) noexcept -> void override { const auto& font = surface.draw_list().shared_data()->get_default_font(); - const auto area = calculate_text_area(font, Style::instance().font_pixel_size, text_); + const auto area = calculate_text_area(font, Style::instance().font_size, text_); requirement_.min_width = area.width; requirement_.min_height = area.height; } auto render(Surface& surface) noexcept -> void override { - surface.draw_list().text(Style::instance().font_pixel_size, rect_.left_top(), primitive::colors::black, text_); + surface.draw_list().text(Style::instance().font_size, rect_.left_top(), primitive::colors::black, text_); } }; } diff --git a/src/draw/style.ixx b/src/draw/style.ixx index d95d6640..7eed6816 100644 --- a/src/draw/style.ixx +++ b/src/draw/style.ixx @@ -26,17 +26,33 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) class Style final { public: + using extern_type = primitive::basic_extent_2d; using color_type = primitive::colors::color_type; [[nodiscard]] static auto instance() noexcept -> Style&; - float font_pixel_size; - float line_pixel_width; + // Default font size used when drawing text + float font_size; + // Default width when drawing line + float line_width; - float flex_pixel_x; - float flex_pixel_y; + color_type separator_color; + float flex_x; + float flex_y; + + // Padding of the first/last element from the container boundary + extern_type container_padding; + // Spacing between elements in the container + extern_type container_spacing; + + // Corner rounding when drawing borders + float border_rounding; + // Padding of the elements within the boundary from the border + extern_type border_padding; + // Default color of the border when drawing the border color_type border_default_color; + // Default color of the title when drawing the window color_type window_title_default_color; }; } From 391afa23e5005cc90a4e10c893ba71cdaa97d647 Mon Sep 17 00:00:00 2001 From: life4gal Date: Thu, 5 Sep 2024 17:50:39 +0800 Subject: [PATCH 017/258] `feat`: Add draw.element.fixed. --- src/draw/element.ixx | 329 +++++++++++++++++++-- src/draw/element.layout.ixx | 558 +++++++++++++++++++++++------------- 2 files changed, 663 insertions(+), 224 deletions(-) diff --git a/src/draw/element.ixx b/src/draw/element.ixx index 7478ebe4..d6157190 100644 --- a/src/draw/element.ixx +++ b/src/draw/element.ixx @@ -55,19 +55,19 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) concept derived_elements_t = std::ranges::range and derived_element_t; template T, typename... Args> - [[nodiscard]] constexpr auto make_element(Args&&... args) noexcept -> element_type + [[nodiscard]] auto make_element(Args&&... args) noexcept -> element_type { return std::make_shared(std::forward(args)...); } template T> - [[nodiscard]] constexpr auto cast_element(const element_type element) noexcept -> std::shared_ptr + [[nodiscard]] constexpr auto cast_element(const element_type& element) noexcept -> std::shared_ptr { return std::dynamic_pointer_cast(element); } template T> - [[nodiscard]] constexpr auto cast_element_unchecked(const element_type element) noexcept -> std::shared_ptr + [[nodiscard]] constexpr auto cast_element_unchecked(const element_type& element) noexcept -> std::shared_ptr { return std::static_pointer_cast(element); } @@ -262,7 +262,19 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { HORIZONTAL = 0b0000'0001, VERTICAL = 0b0000'0010, - GRID = 0b0000'0100, + FLEX = 0b0000'0100, + + GRID = 0b0001'0000, + }; + + enum class FixedOption : std::uint32_t + { + WIDTH = 0b0000'0001, + HEIGHT = 0b0000'0010, + + LESS_THAN = 0b0001'0000, + EQUAL = 0b0010'0000, + GREATER_THAN = 0b0100'0000, }; enum class FlexOption : std::uint32_t @@ -287,20 +299,30 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) [[nodiscard]] auto box_horizontal(elements_type elements) noexcept -> element_type; [[nodiscard]] auto box_vertical(elements_type elements) noexcept -> element_type; + [[nodiscard]] auto box_flex(elements_type elements) noexcept -> element_type; [[nodiscard]] auto box_grid(element_matrix_type elements_grid) noexcept -> element_type; + [[nodiscard]] auto fixed_less_than(float width, float height, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_width_less_than(float width, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_height_less_than(float height, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_equal(float width, float height, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_width_equal(float width, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_height_equal(float height, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_greater_than(float width, float height, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_width_greater_than(float width, element_type element) noexcept -> element_type; + [[nodiscard]] auto fixed_height_greater_than(float height, element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_filler() noexcept -> element_type; [[nodiscard]] auto flex_none(element_type element) noexcept -> element_type; - [[nodiscard]] auto flex(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_grow(element_type element) noexcept -> element_type; [[nodiscard]] auto flex_shrink(element_type element) noexcept -> element_type; - [[nodiscard]] auto flex_horizontal(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_vertical(element_type element) noexcept -> element_type; + [[nodiscard]] auto flex_horizontal_grow(element_type element) noexcept -> element_type; [[nodiscard]] auto flex_horizontal_shrink(element_type element) noexcept -> element_type; - - [[nodiscard]] auto flex_vertical(element_type element) noexcept -> element_type; [[nodiscard]] auto flex_vertical_grow(element_type element) noexcept -> element_type; [[nodiscard]] auto flex_vertical_shrink(element_type element) noexcept -> element_type; @@ -311,20 +333,33 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) constexpr auto option_box_horizontal = impl::options{}; constexpr auto option_box_vertical = impl::options{}; + constexpr auto option_box_flex = impl::options{}; constexpr auto option_box_grid = impl::options{}; + constexpr auto option_fixed_less_than = impl::options{}; + constexpr auto option_fixed_width_less_than = impl::options{}; + constexpr auto option_fixed_height_less_than = impl::options{}; + constexpr auto option_fixed_equal = impl::options{}; + constexpr auto option_fixed_width_equal = impl::options{}; + constexpr auto option_fixed_height_equal = impl::options{}; + constexpr auto option_fixed_greater_than = impl::options{}; + constexpr auto option_fixed_width_greater_than = impl::options{}; + constexpr auto option_fixed_height_greater_than = impl::options{}; + constexpr auto option_flex_none = impl::options{}; - constexpr auto option_flex_grow = impl::options{}; - constexpr auto option_flex_shrink = impl::options{}; + constexpr auto option_flex_all = impl::options{}; + constexpr auto option_flex_grow = impl::options{}; + constexpr auto option_flex_shrink = impl::options{}; + constexpr auto option_flex_horizontal = impl::options{}; + constexpr auto option_flex_vertical = impl::options{}; constexpr auto option_flex_horizontal_grow = impl::options{}; constexpr auto option_flex_horizontal_shrink = impl::options{}; constexpr auto option_flex_vertical_grow = impl::options{}; constexpr auto option_flex_vertical_shrink = impl::options{}; - constexpr auto option_flex_all = impl::options{}; constexpr auto option_center_horizontal = impl::options{}; constexpr auto option_center_vertical = impl::options{}; - constexpr auto option_center_all = impl::options{}; + constexpr auto option_center = impl::options{}; constexpr auto layout = functional::overloaded{ // BOX @@ -339,6 +374,10 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { return impl::box_vertical(std::move(elements)); } + else if constexpr (value == option_box_flex) + { + return impl::box_flex(std::move(elements)); + } else { GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); @@ -394,6 +433,93 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) } }; }, + // FIXED + [](impl::options, const float width, const float height, element_type element) noexcept -> element_type // + requires ( + impl::options::value == option_fixed_less_than or + impl::options::value == option_fixed_equal or + impl::options::value == option_fixed_greater_than + ) + { + if constexpr (constexpr auto option_value = impl::options::value; + option_value == option_fixed_less_than) + { + return impl::fixed_less_than(width, height, std::move(element)); + } + else if constexpr (option_value == option_fixed_equal) + { + return impl::fixed_equal(width, height, std::move(element)); + } + else if constexpr (option_value == option_fixed_greater_than) + { + return impl::fixed_greater_than(width, height, std::move(element)); + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } + }, + [](impl::options, const float value, element_type element) noexcept -> element_type // + requires ( + impl::options::value != option_fixed_less_than and + impl::options::value != option_fixed_equal and + impl::options::value != option_fixed_greater_than + ) + { + if constexpr (constexpr auto option_value = impl::options::value; + option_value == option_fixed_width_less_than) + { + return impl::fixed_width_less_than(value, std::move(element)); + } + else if constexpr (option_value == option_fixed_height_less_than) + { + return impl::fixed_height_less_than(value, std::move(element)); + } + else if constexpr (option_value == option_fixed_width_equal) + { + return impl::fixed_width_equal(value, std::move(element)); + } + else if constexpr (option_value == option_fixed_height_equal) + { + return impl::fixed_height_equal(value, std::move(element)); + } + else if constexpr (option_value == option_fixed_width_greater_than) + { + return impl::fixed_width_greater_than(value, std::move(element)); + } + else if constexpr (option_value == option_fixed_height_greater_than) + { + return impl::fixed_height_greater_than(value, std::move(element)); + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } + }, + [](this const Self& self, impl::options options, const float width, const float height) noexcept -> auto // + requires ( + impl::options::value == option_fixed_less_than or + impl::options::value == option_fixed_equal or + impl::options::value == option_fixed_greater_than + ) + { + return [self, options, width, height](element_type element) noexcept -> element_type + { + return self(options, width, height, std::move(element)); + }; + }, + [](this const Self& self, impl::options options, const float value) noexcept -> auto // + requires ( + impl::options::value != option_fixed_less_than and + impl::options::value != option_fixed_equal and + impl::options::value != option_fixed_greater_than + ) + { + return [self, options, value](element_type element) noexcept -> element_type + { + return self(options, value, std::move(element)); + }; + }, // FLEX [](impl::options, element_type element) noexcept -> element_type { @@ -402,6 +528,10 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { return impl::flex_none(std::move(element)); } + else if constexpr (value == option_flex_all) + { + return impl::flex(std::move(element)); + } else if constexpr (value == option_flex_grow) { return impl::flex_grow(std::move(element)); @@ -410,6 +540,14 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { return impl::flex_shrink(std::move(element)); } + else if constexpr (value == option_flex_horizontal) + { + return impl::flex_horizontal(std::move(element)); + } + else if constexpr (value == option_flex_vertical) + { + return impl::flex_vertical(std::move(element)); + } else if constexpr (value == option_flex_horizontal_grow) { return impl::flex_horizontal_grow(std::move(element)); @@ -426,10 +564,6 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { return impl::flex_vertical_shrink(std::move(element)); } - else if constexpr (value == option_flex_all) - { - return impl::flex(std::move(element)); - } else { GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); @@ -454,7 +588,7 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) { return impl::center_vertical(std::move(element)); } - else if constexpr (value == option_center_all) + else if constexpr (value == option_center) { return impl::center(std::move(element)); } @@ -497,6 +631,17 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) }, }; + constexpr auto box_flex = functional::overloaded{ + [](elements_type elements) noexcept -> element_type + { + return layout(option_box_flex, std::move(elements)); + }, + [](derived_element_t auto... elements) noexcept -> element_type + { + return layout(option_box_flex, std::move(elements)...); + }, + }; + constexpr auto box_grid = functional::overloaded{ [](element_matrix_type elements) noexcept -> element_type { @@ -508,11 +653,142 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) }, }; + constexpr auto fixed_less_than = functional::overloaded{ + [](const float width, const float height, element_type element) noexcept -> element_type + { + return layout(option_fixed_less_than, width, height, std::move(element)); + }, + [](this const Self& self, const float width, const float height) noexcept -> auto + { + return [self, width, height](element_type element) noexcept -> element_type + { + return self(width, height, std::move(element)); + }; + }, + }; + + constexpr auto fixed_width_less_than = functional::overloaded{ + [](const float value, element_type element) noexcept -> element_type + { + return layout(option_fixed_width_less_than, value, std::move(element)); + }, + [](this const Self& self, const float value) noexcept -> auto + { + return [self, value](element_type element) noexcept -> element_type + { + return self(value, std::move(element)); + }; + }, + }; + + constexpr auto fixed_height_less_than = functional::overloaded{ + [](const float value, element_type element) noexcept -> element_type + { + return layout(option_fixed_height_less_than, value, std::move(element)); + }, + [](this const Self& self, const float value) noexcept -> auto + { + return [self, value](element_type element) noexcept -> element_type + { + return self(value, std::move(element)); + }; + }, + }; + + constexpr auto fixed_equal = functional::overloaded{ + [](const float width, const float height, element_type element) noexcept -> element_type + { + return layout(option_fixed_equal, width, height, std::move(element)); + }, + [](this const Self& self, const float width, const float height) noexcept -> auto + { + return [self, width, height](element_type element) noexcept -> element_type + { + return self(width, height, std::move(element)); + }; + }, + }; + + constexpr auto fixed_width_equal = functional::overloaded{ + [](const float value, element_type element) noexcept -> element_type + { + return layout(option_fixed_width_equal, value, std::move(element)); + }, + [](this const Self& self, const float value) noexcept -> auto + { + return [self, value](element_type element) noexcept -> element_type + { + return self(value, std::move(element)); + }; + }, + }; + + constexpr auto fixed_height_equal = functional::overloaded{ + [](const float value, element_type element) noexcept -> element_type + { + return layout(option_fixed_height_equal, value, std::move(element)); + }, + [](this const Self& self, const float value) noexcept -> auto + { + return [self, value](element_type element) noexcept -> element_type + { + return self(value, std::move(element)); + }; + }, + }; + + constexpr auto fixed_greater_than = functional::overloaded{ + [](const float width, const float height, element_type element) noexcept -> element_type + { + return layout(option_fixed_greater_than, width, height, std::move(element)); + }, + [](this const Self& self, const float width, const float height) noexcept -> auto + { + return [self, width, height](element_type element) noexcept -> element_type + { + return self(width, height, std::move(element)); + }; + }, + }; + + constexpr auto fixed_width_greater_than = functional::overloaded{ + [](const float value, element_type element) noexcept -> element_type + { + return layout(option_fixed_width_greater_than, value, std::move(element)); + }, + [](this const Self& self, const float value) noexcept -> auto + { + return [self, value](element_type element) noexcept -> element_type + { + return self(value, std::move(element)); + }; + }, + }; + + constexpr auto fixed_height_greater_than = functional::overloaded{ + [](const float value, element_type element) noexcept -> element_type + { + return layout(option_fixed_height_greater_than, value, std::move(element)); + }, + [](this const Self& self, const float value) noexcept -> auto + { + return [self, value](element_type element) noexcept -> element_type + { + return self(value, std::move(element)); + }; + }, + }; + constexpr auto flex_none = [](element_type element) noexcept -> element_type { return layout(option_flex_none, std::move(element)); }; + constexpr auto flex_all = [](element_type element) noexcept -> element_type + { + return layout(option_flex_all, std::move(element)); + }; + constexpr auto flex_grow = [](element_type element) noexcept -> element_type { return layout(option_flex_grow, std::move(element)); @@ -523,6 +799,16 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) return layout(option_flex_shrink, std::move(element)); }; + constexpr auto flex_horizontal = [](element_type element) noexcept -> element_type + { + return layout(option_flex_horizontal, std::move(element)); + }; + + constexpr auto flex_vertical = [](element_type element) noexcept -> element_type + { + return layout(option_flex_vertical, std::move(element)); + }; + constexpr auto flex_horizontal_grow = [](element_type element) noexcept -> element_type { return layout(option_flex_horizontal_grow, std::move(element)); @@ -543,11 +829,6 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) return layout(option_flex_vertical_shrink, std::move(element)); }; - constexpr auto flex_all = [](element_type element) noexcept -> element_type - { - return layout(option_flex_all, std::move(element)); - }; - constexpr auto center_horizontal = [](element_type element) noexcept -> element_type { return layout(option_center_horizontal, std::move(element)); @@ -558,9 +839,9 @@ GAL_PROMETHEUS_COMPILER_MODULE_EXPORT_NAMESPACE(gal::prometheus::draw) return layout(option_center_vertical, std::move(element)); }; - constexpr auto center_all = [](element_type element) noexcept -> element_type + constexpr auto center = [](element_type element) noexcept -> element_type { - return layout(option_center_all, std::move(element)); + return layout(option_center, std::move(element)); }; } } diff --git a/src/draw/element.layout.ixx b/src/draw/element.layout.ixx index 86918aa1..7552de65 100644 --- a/src/draw/element.layout.ixx +++ b/src/draw/element.layout.ixx @@ -13,6 +13,8 @@ export module gal.prometheus.draw:element.layout; import std; import gal.prometheus.primitive; +import gal.prometheus.functional; +// GAL_PROMETHEUS_ERROR_IMPORT_DEBUG_MODULE import :surface; import :style; @@ -23,9 +25,11 @@ import :element; #include #include +#include #include #include #include +// #include GAL_PROMETHEUS_ERROR_DEBUG_MODULE #endif @@ -134,12 +138,20 @@ namespace } } - class HorizontalBox final : public impl::Element + template + requires (Option == element::impl::BoxOption::HORIZONTAL or Option == element::impl::BoxOption::VERTICAL) + class Box final : public impl::Element { public: - explicit HorizontalBox(elements_type children) noexcept + using option_type = element::impl::BoxOption; + + constexpr static auto option = Option; + + explicit Box(elements_type children) noexcept : Element{std::move(children)} {} + ~Box() noexcept override = default; + auto calculate_requirement(Surface& surface) noexcept -> void override { requirement_.reset(); @@ -150,110 +162,53 @@ namespace { child->calculate_requirement(surface); - requirement_.min_width += child->requirement().min_width; - requirement_.min_height = std::ranges::max( - requirement_.min_height, - child->requirement().min_height - ); + using functional::operators::operator|; + if constexpr (option == option_type::HORIZONTAL) + { + requirement_.min_width += child->requirement().min_width; + requirement_.min_height = std::ranges::max( + requirement_.min_height, + child->requirement().min_height + ); + } + else if constexpr (option == element::impl::BoxOption::VERTICAL) + { + requirement_.min_height += child->requirement().min_height; + requirement_.min_width = std::ranges::max( + requirement_.min_width, + child->requirement().min_width + ); + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } } ); const auto& container_padding = Style::instance().container_padding; const auto& container_spacing = Style::instance().container_spacing; - const auto extra_x = 2 * container_padding.width + (children_.size() - 1) * container_spacing.width; - const auto extra_y = 2 * container_padding.height; - - requirement_.min_width += extra_x; - requirement_.min_height += extra_y; - } - - auto set_rect(const rect_type& rect) noexcept -> void override - { - Element::set_rect(rect); - - std::vector elements{}; - elements.reserve(children_.size()); - - std::ranges::transform( - children_, - std::back_inserter(elements), - [](const auto& child) noexcept -> element_size + const auto [extra_x, extra_y] = [&]() noexcept -> std::pair + { + if constexpr (option == option_type::HORIZONTAL) { - const auto& r = child->requirement(); - return { - .min_size = r.min_width, - .flex_grow = r.flex_grow_width, - .flex_shrink = r.flex_shrink_width, - .size = 0 - }; + return std::make_pair( + 2 * container_padding.width + (children_.size() - 1) * container_spacing.width, + 2 * container_padding.height + ); } - ); - - const auto& container_padding = Style::instance().container_padding; - const auto& container_spacing = Style::instance().container_spacing; - const auto extra_x = 2 * container_padding.width + (children_.size() - 1) * container_spacing.width; - - calculate(elements, rect.width() - extra_x); - - const auto& [point, extent] = rect; - - std::ranges::for_each( - std::views::zip(children_, elements), - [ - current_x = point.x + container_padding.width, - current_y = point.y + container_padding.height, - current_bottom = point.y + extent.height - container_padding.height, - container_spacing = container_spacing.width - ](std::tuple pack) mutable noexcept -> void + else if constexpr (option == element::impl::BoxOption::VERTICAL) { - auto& [child, element] = pack; - - const rect_type box - { - // left - current_x, - // top - current_y, - // right - current_x + element.size, - // bottom - current_bottom - }; - child->set_rect(box); - current_x = current_x + element.size + container_spacing; + return std::make_pair( + 2 * container_padding.width, + 2 * container_padding.height + (children_.size() - 1) * container_spacing.height + ); } - ); - } - }; - - class VerticalBox final : public impl::Element - { - public: - explicit VerticalBox(elements_type children) noexcept - : Element{std::move(children)} {} - - auto calculate_requirement(Surface& surface) noexcept -> void override - { - requirement_.reset(); - - std::ranges::for_each( - children_, - [this, &surface](const auto& child) noexcept -> void + else { - child->calculate_requirement(surface); - - requirement_.min_height += child->requirement().min_height; - requirement_.min_width = std::ranges::max( - requirement_.min_width, - child->requirement().min_width - ); + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); } - ); - - const auto& container_padding = Style::instance().container_padding; - const auto& container_spacing = Style::instance().container_spacing; - const auto extra_x = 2 * container_padding.width; - const auto extra_y = 2 * container_padding.height + (children_.size() - 1) * container_spacing.height; + }(); requirement_.min_width += extra_x; requirement_.min_height += extra_y; @@ -272,20 +227,49 @@ namespace [](const auto& child) noexcept -> element_size { const auto& r = child->requirement(); - return { - .min_size = r.min_height, - .flex_grow = r.flex_grow_height, - .flex_shrink = r.flex_shrink_height, - .size = 0 - }; + + if constexpr (option == option_type::HORIZONTAL) + { + return { + .min_size = r.min_width, + .flex_grow = r.flex_grow_width, + .flex_shrink = r.flex_shrink_width, + .size = 0 + }; + } + else if constexpr (option == element::impl::BoxOption::VERTICAL) + { + return { + .min_size = r.min_height, + .flex_grow = r.flex_grow_height, + .flex_shrink = r.flex_shrink_height, + .size = 0 + }; + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } } ); const auto& container_padding = Style::instance().container_padding; const auto& container_spacing = Style::instance().container_spacing; - const auto extra_y = 2 * container_padding.height + (children_.size() - 1) * container_spacing.height; - calculate(elements, rect.height() - extra_y); + if constexpr (option == option_type::HORIZONTAL) + { + const auto extra_x = 2 * container_padding.width + (children_.size() - 1) * container_spacing.width; + calculate(elements, rect.width() - extra_x); + } + else if constexpr (option == element::impl::BoxOption::VERTICAL) + { + const auto extra_y = 2 * container_padding.height + (children_.size() - 1) * container_spacing.height; + calculate(elements, rect.height() - extra_y); + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } const auto& [point, extent] = rect; @@ -295,24 +279,48 @@ namespace current_x = point.x + container_padding.width, current_y = point.y + container_padding.height, current_right = point.x + extent.width - container_padding.width, - container_spacing = container_spacing.height + current_bottom = point.y + extent.height - container_padding.height, + container_spacing ](std::tuple pack) mutable noexcept -> void { auto& [child, element] = pack; - const rect_type box + if constexpr (option == option_type::HORIZONTAL) { - // left - current_x, - // top - current_y, - // right - current_right, - // bottom - current_y + element.size - }; - child->set_rect(box); - current_y = current_y + element.size + container_spacing; + const rect_type box + { + // left + current_x, + // top + current_y, + // right + current_x + element.size, + // bottom + current_bottom + }; + child->set_rect(box); + current_x = current_x + element.size + container_spacing.width; + } + else if constexpr (option == element::impl::BoxOption::VERTICAL) + { + const rect_type box + { + // left + current_x, + // top + current_y, + // right + current_right, + // bottom + current_y + element.size + }; + child->set_rect(box); + current_y = current_y + element.size + container_spacing.height; + } + else + { + GAL_PROMETHEUS_SEMANTIC_STATIC_UNREACHABLE(); + } } ); } @@ -525,87 +533,156 @@ namespace } }; - namespace function + template + struct fixed_value; + + template + requires ( + ((std::to_underlying(Option) & std::to_underlying(element::impl::FixedOption::WIDTH)) != 0) and + ((std::to_underlying(Option) & std::to_underlying(element::impl::FixedOption::HEIGHT)) == 0) + ) + struct fixed_value