Show HN: I made a (bad?) CSS dialect for Clay (C/C++)

3 months ago 3
#pragma once #include <cstdint> #include <sstream> #include <string> #include <filesystem> #include <utility> #include <functional> #include <vector> #include <lexy/dsl.hpp> #include <lexy/callback.hpp> #include <clay.h> #include <engine/ui/styles/types.hpp> #include <engine/ui/manager.hpp> namespace engine::ui::styles::parser { namespace dsl = lexy::dsl; namespace basic { // MARK: identifier struct identifier { static constexpr auto rule = dsl::identifier( dsl::ascii::alpha, dsl::ascii::alpha_digit_underscore / dsl::lit_c<'-'> ); static constexpr auto value = lexy::as_string<std::string>; }; // MARK: boolean struct boolean { struct true_ { static constexpr auto rule = LEXY_LIT("true"); static constexpr auto value = lexy::constant(true); }; struct false_ { static constexpr auto rule = LEXY_LIT("false"); static constexpr auto value = lexy::constant(false); }; static constexpr auto rule = dsl::p<true_> | dsl::p<false_>; static constexpr auto value = lexy::forward<bool>; }; // MARK: float struct float_ { static constexpr auto rule = dsl::sign + dsl::integer<int> + dsl::opt(dsl::lit_c<'.'> >> dsl::integer<int>) ; static constexpr auto value = lexy::callback<float>( [](lexy::minus_sign, int i, int d) -> float { auto result = 0.0f; auto oss = std::ostringstream{}; oss << "-" << i << "." << d; auto iss = std::istringstream{oss.str()}; iss >> result; return result; }, [](lexy::minus_sign, int i, lexy::nullopt) -> float { return -static_cast<float>(i); }, [](lexy::plus_sign, int i, int d) -> float { auto result = 0.0f; auto oss = std::ostringstream{}; oss << i << "." << d; auto iss = std::istringstream{oss.str()}; iss >> result; return result; }, [](lexy::plus_sign, int i, lexy::nullopt) -> float { return static_cast<float>(i); }, [](int i, int d) -> float { auto result = 0.0f; auto oss = std::ostringstream{}; oss << i << "." << d; auto iss = std::istringstream{oss.str()}; iss >> result; return result; }, [](int i, lexy::nullopt) -> float { return static_cast<float>(i); } ); }; // MARK: color struct color { static constexpr auto rule = LEXY_LIT("rgba") + dsl::parenthesized(dsl::times<4>(dsl::p<float_>, dsl::sep(dsl::comma))) ; static constexpr auto value = lexy::construct<Clay_Color>; }; // MARK: str struct str { static constexpr auto rule = dsl::single_quoted(dsl::ascii::print); static constexpr auto value = lexy::as_string<std::string>; }; } namespace property_set { template <typename T> struct as_property { struct _property_value { static constexpr auto rule = T::property_value_rule; static constexpr auto value = T::property_value_eval; }; static constexpr auto rule = T::property_name_rule >> dsl::lit_c<':'> >> dsl::p<_property_value> >> dsl::lit_c<';'> ; using property_value_type = std::pair<T, typename T::property_value_type>; static constexpr auto value = lexy::callback<property_value_type>( [](typename T::property_value_type value) { return std::make_pair(T{}, value); } ); }; // MARK: elt props struct element_properties { struct _layout_sizing_axis { using property_value_type = Clay_SizingAxis; struct fit_value { static constexpr auto rule = LEXY_LIT("fit") >> dsl::parenthesized(dsl::times<2>(dsl::p<basic::float_>, dsl::sep(dsl::comma))) ; static constexpr auto value = lexy::callback<property_value_type>( [](float min, float max) { return CLAY_SIZING_FIT(min, max); } ); }; struct grow_value { static constexpr auto rule = LEXY_LIT("grow") >> dsl::parenthesized(dsl::times<2>(dsl::p<basic::float_>, dsl::sep(dsl::comma))) ; static constexpr auto value = lexy::callback<property_value_type>( [](float min, float max) { return CLAY_SIZING_GROW(min, max); } ); }; struct percent_value { static constexpr auto rule = dsl::p<basic::float_> + LEXY_LIT("%") ; static constexpr auto value = lexy::callback<property_value_type>( [](float percent) { return CLAY_SIZING_PERCENT(percent / 100.0f); } ); }; struct fixed_value { static constexpr auto rule = dsl::p<basic::float_>; static constexpr auto value = lexy::callback<property_value_type>( [](float size) { return CLAY_SIZING_FIXED(size); } ); }; static constexpr auto property_value_rule = dsl::p<fit_value> | dsl::p<grow_value> | dsl::peek(dsl::p<percent_value>) >> dsl::p<percent_value> | dsl::peek(dsl::p<fixed_value>) >> dsl::p<fixed_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; struct layout_sizing_width : public _layout_sizing_axis { static constexpr auto property_name_rule = LEXY_LIT("layout-sizing-width"); }; using prop_layout_sizing_width = as_property<layout_sizing_width>; struct layout_sizing_height : public _layout_sizing_axis { static constexpr auto property_name_rule = LEXY_LIT("layout-sizing-height"); }; using prop_layout_sizing_height = as_property<layout_sizing_height>; struct layout_padding { using property_value_type = Clay_Padding; static constexpr auto property_name_rule = LEXY_LIT("layout-padding"); static constexpr auto property_value_rule = dsl::integer<uint16_t>; static constexpr auto property_value_eval = lexy::callback<property_value_type>([](uint16_t padding) { return CLAY_PADDING_ALL(padding); }); }; using prop_layout_padding = as_property<layout_padding>; struct layout_padding_left { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("layout-padding-left"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_padding_left = as_property<layout_padding_left>; struct layout_padding_right { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("layout-padding-right"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_padding_right = as_property<layout_padding_right>; struct layout_padding_top { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("layout-padding-top"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_padding_top = as_property<layout_padding_top>; struct layout_padding_bottom { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("layout-padding-bottom"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_padding_bottom = as_property<layout_padding_bottom>; struct layout_child_gap { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("layout-child-gap"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_child_gap = as_property<layout_child_gap>; struct layout_child_alignment_x { using property_value_type = Clay_LayoutAlignmentX; struct left_value { static constexpr auto rule = LEXY_LIT("left"); static constexpr auto value = lexy::constant(CLAY_ALIGN_X_LEFT); }; struct center_value { static constexpr auto rule = LEXY_LIT("center"); static constexpr auto value = lexy::constant(CLAY_ALIGN_X_CENTER); }; struct right_value { static constexpr auto rule = LEXY_LIT("right"); static constexpr auto value = lexy::constant(CLAY_ALIGN_X_RIGHT); }; static constexpr auto property_name_rule = LEXY_LIT("layout-child-alignment-x"); static constexpr auto property_value_rule = dsl::p<left_value> | dsl::p<center_value> | dsl::p<right_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_child_alignment_x = as_property<layout_child_alignment_x>; struct layout_child_alignment_y { using property_value_type = Clay_LayoutAlignmentY; struct top_value { static constexpr auto rule = LEXY_LIT("top"); static constexpr auto value = lexy::constant(CLAY_ALIGN_Y_TOP); }; struct center_value { static constexpr auto rule = LEXY_LIT("center"); static constexpr auto value = lexy::constant(CLAY_ALIGN_Y_CENTER); }; struct bottom_value { static constexpr auto rule = LEXY_LIT("bottom"); static constexpr auto value = lexy::constant(CLAY_ALIGN_Y_BOTTOM); }; static constexpr auto property_name_rule = LEXY_LIT("layout-child-alignment-y"); static constexpr auto property_value_rule = dsl::p<top_value> | dsl::p<center_value> | dsl::p<bottom_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_child_alignment_y = as_property<layout_child_alignment_y>; struct layout_direction { using property_value_type = Clay_LayoutDirection; struct left_to_right_value { static constexpr auto rule = LEXY_LIT("left-to-right"); static constexpr auto value = lexy::constant(CLAY_LEFT_TO_RIGHT); }; struct top_to_bottom_value { static constexpr auto rule = LEXY_LIT("top-to-bottom"); static constexpr auto value = lexy::constant(CLAY_TOP_TO_BOTTOM); }; static constexpr auto property_name_rule = LEXY_LIT("layout-direction"); static constexpr auto property_value_rule = dsl::p<left_to_right_value> | dsl::p<top_to_bottom_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_layout_direction = as_property<layout_direction>; struct background_color { using property_value_type = Clay_Color; static constexpr auto property_name_rule = LEXY_LIT("background-color"); static constexpr auto property_value_rule = dsl::p<basic::color>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_background_color = as_property<background_color>; struct corner_radius { using property_value_type = Clay_CornerRadius; static constexpr auto property_name_rule = LEXY_LIT("corner-radius"); static constexpr auto property_value_rule = dsl::times<4>(dsl::p<basic::float_>); static constexpr auto property_value_eval = lexy::construct<property_value_type>; }; using prop_corner_radius = as_property<corner_radius>; struct corner_radius_top_left { using property_value_type = float; static constexpr auto property_name_rule = LEXY_LIT("corner-radius-top-left"); static constexpr auto property_value_rule = dsl::p<basic::float_>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_corner_radius_top_left = as_property<corner_radius_top_left>; struct corner_radius_top_right { using property_value_type = float; static constexpr auto property_name_rule = LEXY_LIT("corner-radius-top-right"); static constexpr auto property_value_rule = dsl::p<basic::float_>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_corner_radius_top_right = as_property<corner_radius_top_right>; struct corner_radius_bottom_left { using property_value_type = float; static constexpr auto property_name_rule = LEXY_LIT("corner-radius-bottom-left"); static constexpr auto property_value_rule = dsl::p<basic::float_>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_corner_radius_bottom_left = as_property<corner_radius_bottom_left>; struct corner_radius_bottom_right { using property_value_type = float; static constexpr auto property_name_rule = LEXY_LIT("corner-radius-bottom-right"); static constexpr auto property_value_rule = dsl::p<basic::float_>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_corner_radius_bottom_right = as_property<corner_radius_bottom_right>; struct aspect_ratio { using property_value_type = Clay_AspectRatioElementConfig; static constexpr auto property_name_rule = LEXY_LIT("aspect-ratio"); static constexpr auto property_value_rule = dsl::p<basic::float_>; static constexpr auto property_value_eval = lexy::construct<property_value_type>; }; using prop_aspect_ratio = as_property<aspect_ratio>; struct image { using property_value_type = std::filesystem::path; static constexpr auto property_name_rule = LEXY_LIT("image"); static constexpr auto property_value_rule = dsl::p<basic::str>; static constexpr auto property_value_eval = lexy::construct<property_value_type>; }; using prop_image = as_property<image>; struct floating_offset { using property_value_type = Clay_Vector2; static constexpr auto property_name_rule = LEXY_LIT("floating-offset"); static constexpr auto property_value_rule = dsl::times<2>(dsl::p<basic::float_>); static constexpr auto property_value_eval = lexy::construct<property_value_type>; }; using prop_floating_offset = as_property<floating_offset>; struct floating_expand { using property_value_type = Clay_Dimensions; static constexpr auto property_name_rule = LEXY_LIT("floating-expand"); static constexpr auto property_value_rule = dsl::times<2>(dsl::p<basic::float_>); static constexpr auto property_value_eval = lexy::construct<property_value_type>; }; using prop_floating_expand = as_property<floating_expand>; struct floating_zindex { using property_value_type = int16_t; static constexpr auto property_name_rule = LEXY_LIT("floating-zindex"); static constexpr auto property_value_rule = dsl::integer<int16_t>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_floating_zindex = as_property<floating_zindex>; struct _floating_attach_point_type { using property_value_type = Clay_FloatingAttachPointType; struct left_top_value { static constexpr auto rule = LEXY_LIT("left-top"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_LEFT_TOP); }; struct left_center_value { static constexpr auto rule = LEXY_LIT("left-center"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_LEFT_CENTER); }; struct left_bottom_value { static constexpr auto rule = LEXY_LIT("left-bottom"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_LEFT_BOTTOM); }; struct center_top_value { static constexpr auto rule = LEXY_LIT("center-top"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_CENTER_TOP); }; struct center_center_value { static constexpr auto rule = LEXY_LIT("center-center"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_CENTER_CENTER); }; struct center_bottom_value { static constexpr auto rule = LEXY_LIT("center-bottom"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_CENTER_BOTTOM); }; struct right_top_value { static constexpr auto rule = LEXY_LIT("right-top"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_RIGHT_TOP); }; struct right_center_value { static constexpr auto rule = LEXY_LIT("right-center"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_RIGHT_CENTER); }; struct right_bottom_value { static constexpr auto rule = LEXY_LIT("right-bottom"); static constexpr auto value = lexy::constant(CLAY_ATTACH_POINT_RIGHT_BOTTOM); }; static constexpr auto property_value_rule = dsl::p<left_top_value> | dsl::p<left_center_value> | dsl::p<left_bottom_value> | dsl::p<center_top_value> | dsl::p<center_center_value> | dsl::p<center_bottom_value> | dsl::p<right_top_value> | dsl::p<right_center_value> | dsl::p<right_bottom_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; struct floating_attach_point_element : public _floating_attach_point_type { static constexpr auto property_name_rule = LEXY_LIT("floating-attach-point-element"); }; using prop_floating_attach_point_element = as_property<floating_attach_point_element>; struct floating_attach_point_parent : public _floating_attach_point_type { static constexpr auto property_name_rule = LEXY_LIT("floating-attach-point-parent"); }; using prop_floating_attach_point_parent = as_property<floating_attach_point_parent>; struct floating_pointer_capture_mode { using property_value_type = Clay_PointerCaptureMode; struct capture_value { static constexpr auto rule = LEXY_LIT("capture"); static constexpr auto value = lexy::constant(CLAY_POINTER_CAPTURE_MODE_CAPTURE); }; struct passthrough_value { static constexpr auto rule = LEXY_LIT("passthrough"); static constexpr auto value = lexy::constant(CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH); }; static constexpr auto property_name_rule = LEXY_LIT("floating-pointer-capture-mode"); static constexpr auto property_value_rule = dsl::p<capture_value> | dsl::p<passthrough_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_floating_pointer_capture_mode = as_property<floating_pointer_capture_mode>; struct floating_attach_to { using property_value_type = Clay_FloatingAttachToElement; struct none_value { static constexpr auto rule = LEXY_LIT("none"); static constexpr auto value = lexy::constant(CLAY_ATTACH_TO_NONE); }; struct parent_value { static constexpr auto rule = LEXY_LIT("parent"); static constexpr auto value = lexy::constant(CLAY_ATTACH_TO_PARENT); }; struct element_value { static constexpr auto rule = LEXY_LIT("element"); static constexpr auto value = lexy::constant(CLAY_ATTACH_TO_ELEMENT_WITH_ID); }; struct root_value { static constexpr auto rule = LEXY_LIT("root"); static constexpr auto value = lexy::constant(CLAY_ATTACH_TO_ROOT); }; static constexpr auto property_name_rule = LEXY_LIT("floating-attach-to"); static constexpr auto property_value_rule = dsl::p<none_value> | dsl::p<parent_value> | dsl::p<element_value> | dsl::p<root_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_floating_attach_to = as_property<floating_attach_to>; struct floating_clip_to { using property_value_type = Clay_FloatingClipToElement; struct none_value { static constexpr auto rule = LEXY_LIT("none"); static constexpr auto value = lexy::constant(CLAY_CLIP_TO_NONE); }; struct parent_value { static constexpr auto rule = LEXY_LIT("parent"); static constexpr auto value = lexy::constant(CLAY_CLIP_TO_ATTACHED_PARENT); }; static constexpr auto property_name_rule = LEXY_LIT("floating-clip-to"); static constexpr auto property_value_rule = dsl::p<none_value> | dsl::p<parent_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_floating_clip_to = as_property<floating_clip_to>; struct clip_horizontal { using property_value_type = bool; static constexpr auto property_name_rule = LEXY_LIT("clip-horizontal"); static constexpr auto property_value_rule = dsl::p<basic::boolean>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_clip_horizontal = as_property<clip_horizontal>; struct clip_vertical { using property_value_type = bool; static constexpr auto property_name_rule = LEXY_LIT("clip-vertical"); static constexpr auto property_value_rule = dsl::p<basic::boolean>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_clip_vertical = as_property<clip_vertical>; struct clip_child_offset { using property_value_type = Clay_Vector2; static constexpr auto property_name_rule = LEXY_LIT("clip-child-offset"); static constexpr auto property_value_rule = dsl::times<2>(dsl::p<basic::float_>); static constexpr auto property_value_eval = lexy::construct<property_value_type>; }; using prop_clip_child_offset = as_property<clip_child_offset>; struct border_color { using property_value_type = Clay_Color; static constexpr auto property_name_rule = LEXY_LIT("border-color"); static constexpr auto property_value_rule = dsl::p<basic::color>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_border_color = as_property<border_color>; struct border_width { using property_value_type = Clay_BorderWidth; struct outside_value { static constexpr auto rule = LEXY_LIT("outside") + dsl::parenthesized(dsl::integer<uint16_t>) ; static constexpr auto value = lexy::callback<property_value_type>( [](uint16_t width) -> property_value_type { return CLAY_BORDER_OUTSIDE(width); } ); }; struct all_value { static constexpr auto rule = LEXY_LIT("all") + dsl::parenthesized(dsl::integer<uint16_t>) ; static constexpr auto value = lexy::callback<property_value_type>( [](uint16_t width) -> property_value_type { return CLAY_BORDER_ALL(width); } ); }; static constexpr auto property_name_rule = LEXY_LIT("border-width"); static constexpr auto property_value_rule = dsl::peek(dsl::p<outside_value>) >> dsl::p<outside_value> | dsl::peek(dsl::p<all_value>) >> dsl::p<all_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_border_width = as_property<border_width>; struct border_width_left { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("border-width-left"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_border_width_left = as_property<border_width_left>; struct border_width_right { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("border-width-right"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_border_width_right = as_property<border_width_right>; struct border_width_top { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("border-width-top"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_border_width_top = as_property<border_width_top>; struct border_width_bottom { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("border-width-bottom"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_border_width_bottom = as_property<border_width_bottom>; struct border_width_between_children { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("border-width-between-children"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_border_width_between_children = as_property<border_width_between_children>; static constexpr auto rule = dsl::partial_combination( dsl::p<prop_layout_sizing_width>, dsl::p<prop_layout_sizing_height>, dsl::p<prop_layout_padding>, dsl::p<prop_layout_padding_left>, dsl::p<prop_layout_padding_right>, dsl::p<prop_layout_padding_top>, dsl::p<prop_layout_padding_bottom>, dsl::p<prop_layout_child_gap>, dsl::p<prop_layout_child_alignment_x>, dsl::p<prop_layout_child_alignment_y>, dsl::p<prop_layout_direction>, dsl::p<prop_background_color>, dsl::p<prop_corner_radius>, dsl::p<prop_corner_radius_top_left>, dsl::p<prop_corner_radius_top_right>, dsl::p<prop_corner_radius_bottom_left>, dsl::p<prop_corner_radius_bottom_right>, dsl::p<prop_aspect_ratio>, dsl::p<prop_image>, dsl::p<prop_floating_offset>, dsl::p<prop_floating_expand>, dsl::p<prop_floating_zindex>, dsl::p<prop_floating_attach_point_element>, dsl::p<prop_floating_attach_point_parent>, dsl::p<prop_floating_pointer_capture_mode>, dsl::p<prop_floating_attach_to>, dsl::p<prop_floating_clip_to>, dsl::p<prop_clip_horizontal>, dsl::p<prop_clip_vertical>, dsl::p<prop_clip_child_offset>, dsl::p<prop_border_color>, dsl::p<prop_border_width>, dsl::p<prop_border_width_left>, dsl::p<prop_border_width_right>, dsl::p<prop_border_width_top>, dsl::p<prop_border_width_bottom>, dsl::p<prop_border_width_between_children> ); using property_setter = std::function<void(manager&, element_style&)>; using property_setters = std::vector<property_setter>; static constexpr auto value = lexy::fold_inplace<property_setters>( property_setters{}, [](property_setters& setters, prop_layout_sizing_width::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.sizing.width = value; }); }, [](property_setters& setters, prop_layout_sizing_height::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.sizing.height = value; }); }, [](property_setters& setters, prop_layout_padding::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.padding = value; }); }, [](property_setters& setters, prop_layout_padding_left::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.padding.left = value; }); }, [](property_setters& setters, prop_layout_padding_right::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.padding.right = value; }); }, [](property_setters& setters, prop_layout_padding_top::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.padding.top = value; }); }, [](property_setters& setters, prop_layout_padding_bottom::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.padding.bottom = value; }); }, [](property_setters& setters, prop_layout_child_gap::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.childGap = value; }); }, [](property_setters& setters, prop_layout_child_alignment_x::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.childAlignment.x = value; }); }, [](property_setters& setters, prop_layout_child_alignment_y::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.childAlignment.y = value; }); }, [](property_setters& setters, prop_layout_direction::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.layout.layoutDirection = value; }); }, [](property_setters& setters, prop_background_color::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.backgroundColor = value; }); }, [](property_setters& setters, prop_corner_radius::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.cornerRadius = value; }); }, [](property_setters& setters, prop_corner_radius_top_left::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.cornerRadius.topLeft = value; }); }, [](property_setters& setters, prop_corner_radius_top_right::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.cornerRadius.topRight = value; }); }, [](property_setters& setters, prop_corner_radius_bottom_left::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.cornerRadius.bottomLeft = value; }); }, [](property_setters& setters, prop_corner_radius_bottom_right::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.cornerRadius.bottomRight = value; }); }, [](property_setters& setters, prop_aspect_ratio::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.aspectRatio = value; }); }, [](property_setters& setters, prop_image::property_value_type property) { setters.push_back([property](manager& ui_manager, element_style& decl) { auto [_, value] = property; auto texture_id = ui_manager.add_texture_to_atlas(value); decl.image.imageData = reinterpret_cast<void*>(static_cast<intptr_t>(texture_id)); }); }, [](property_setters& setters, prop_floating_offset::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.offset = value; }); }, [](property_setters& setters, prop_floating_expand::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.expand = value; }); }, [](property_setters& setters, prop_floating_zindex::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.zIndex = value; }); }, [](property_setters& setters, prop_floating_attach_point_element::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.attachPoints.element = value; }); }, [](property_setters& setters, prop_floating_attach_point_parent::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.attachPoints.parent = value; }); }, [](property_setters& setters, prop_floating_pointer_capture_mode::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.pointerCaptureMode = value; }); }, [](property_setters& setters, prop_floating_attach_to::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.attachTo = value; }); }, [](property_setters& setters, prop_floating_clip_to::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.floating.clipTo = value; }); }, [](property_setters& setters, prop_clip_horizontal::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.clip.horizontal = value; }); }, [](property_setters& setters, prop_clip_vertical::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.clip.vertical = value; }); }, [](property_setters& setters, prop_clip_child_offset::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.clip.childOffset = value; }); }, [](property_setters& setters, prop_border_color::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.border.color = value; }); }, [](property_setters& setters, prop_border_width::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.border.width = value; }); }, [](property_setters& setters, prop_border_width_left::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.border.width.left = value; }); }, [](property_setters& setters, prop_border_width_right::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.border.width.right = value; }); }, [](property_setters& setters, prop_border_width_top::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.border.width.top = value; }); }, [](property_setters& setters, prop_border_width_bottom::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.border.width.bottom = value; }); }, [](property_setters& setters, prop_border_width_between_children::property_value_type property) { setters.push_back([property](manager&, element_style& decl) { auto [_, value] = property; decl.border.width.betweenChildren = value; }); } ); }; // MARK: txt props struct text_properties { struct color { using property_value_type = Clay_Color; static constexpr auto property_name_rule = LEXY_LIT("color"); static constexpr auto property_value_rule = dsl::p<basic::color>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_color = as_property<color>; struct font_face { using property_value_type = std::filesystem::path; static constexpr auto property_name_rule = LEXY_LIT("font-face"); static constexpr auto property_value_rule = dsl::p<basic::str>; static constexpr auto property_value_eval = lexy::construct<property_value_type>; }; using prop_font_face = as_property<font_face>; struct font_size { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("font-size"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_font_size = as_property<font_size>; struct letter_spacing { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("letter-spacing"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_letter_spacing = as_property<letter_spacing>; struct line_height { using property_value_type = uint16_t; static constexpr auto property_name_rule = LEXY_LIT("line-height"); static constexpr auto property_value_rule = dsl::integer<property_value_type>; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_line_height = as_property<line_height>; struct wrap_mode { using property_value_type = Clay_TextElementConfigWrapMode; struct words_value { static constexpr auto rule = LEXY_LIT("words"); static constexpr auto value = lexy::constant(CLAY_TEXT_WRAP_WORDS); }; struct newlines_value { static constexpr auto rule = LEXY_LIT("newlines"); static constexpr auto value = lexy::constant(CLAY_TEXT_WRAP_NEWLINES); }; struct none_value { static constexpr auto rule = LEXY_LIT("none"); static constexpr auto value = lexy::constant(CLAY_TEXT_WRAP_NONE); }; static constexpr auto property_name_rule = LEXY_LIT("wrap-mode"); static constexpr auto property_value_rule = dsl::p<words_value> | dsl::p<newlines_value> | dsl::p<none_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_wrap_mode = as_property<wrap_mode>; struct alignment { using property_value_type = Clay_TextAlignment; struct left_value { static constexpr auto rule = LEXY_LIT("left"); static constexpr auto value = lexy::constant(CLAY_TEXT_ALIGN_LEFT); }; struct center_value { static constexpr auto rule = LEXY_LIT("center"); static constexpr auto value = lexy::constant(CLAY_TEXT_ALIGN_CENTER); }; struct right_value { static constexpr auto rule = LEXY_LIT("right"); static constexpr auto value = lexy::constant(CLAY_TEXT_ALIGN_RIGHT); }; static constexpr auto property_name_rule = LEXY_LIT("alignment"); static constexpr auto property_value_rule = dsl::p<left_value> | dsl::p<center_value> | dsl::p<right_value> ; static constexpr auto property_value_eval = lexy::forward<property_value_type>; }; using prop_alignment = as_property<alignment>; static constexpr auto rule = dsl::partial_combination( dsl::p<prop_color>, dsl::p<prop_font_face>, dsl::p<prop_font_size>, dsl::p<prop_letter_spacing>, dsl::p<prop_line_height>, dsl::p<prop_wrap_mode>, dsl::p<prop_alignment> ); using property_setter = std::function<void(manager&, text_style&)>; using property_setters = std::vector<property_setter>; static constexpr auto value = lexy::fold_inplace<property_setters>( property_setters{}, [](property_setters& setters, prop_color::property_value_type property) { setters.push_back([property](manager&, text_style& config) { auto [_, value] = property; config.textColor = value; }); }, [](property_setters& setters, prop_font_face::property_value_type property) { setters.push_back([property](manager& manager, text_style& config) { auto [_, value] = property; auto font_id = manager.add_font_to_atlas(value); config.fontId = font_id; }); }, [](property_setters& setters, prop_font_size::property_value_type property) { setters.push_back([property](manager&, text_style& config) { auto [_, value] = property; config.fontSize = value; }); }, [](property_setters& setters, prop_letter_spacing::property_value_type property) { setters.push_back([property](manager&, text_style& config) { auto [_, value] = property; config.letterSpacing = value; }); }, [](property_setters& setters, prop_line_height::property_value_type property) { setters.push_back([property](manager&, text_style& config) { auto [_, value] = property; config.lineHeight = value; }); }, [](property_setters& setters, prop_wrap_mode::property_value_type property) { setters.push_back([property](manager&, text_style& config) { auto [_, value] = property; config.wrapMode = value; }); }, [](property_setters& setters, prop_alignment::property_value_type property) { setters.push_back([property](manager&, text_style& config) { auto [_, value] = property; config.textAlignment = value; }); } ) >> lexy::forward<property_setters>; }; // MARK: elt props state struct container_properties_with_state { static constexpr auto rule = dsl::p<property_set::element_properties> + dsl::opt(dsl::lit_c<'&'> >> dsl::lit_c<':'> >> LEXY_LIT("hover") >> dsl::curly_bracketed(dsl::p<property_set::element_properties>)) + dsl::opt(dsl::lit_c<'&'> >> dsl::lit_c<':'> >> LEXY_LIT("active") >> dsl::curly_bracketed(dsl::p<property_set::element_properties>)) ; static constexpr auto value = lexy::callback_with_state<container_style_with_state>( []( manager& ui_manager, property_set::element_properties::property_setters normal_setters, lexy::nullopt, lexy::nullopt ) { auto style = container_style_with_state{}; for (auto& setter : normal_setters) { setter(ui_manager, style.normal); } style.hovered = style.normal; style.active = style.normal; return style; }, []( manager& ui_manager, property_set::element_properties::property_setters normal_setters, property_set::element_properties::property_setters hovered_setters, lexy::nullopt ) { auto style = container_style_with_state{}; for (auto& setter : normal_setters) { setter(ui_manager, style.normal); } style.hovered = style.normal; style.active = style.normal; for (auto& setter : hovered_setters) { setter(ui_manager, style.hovered); } return style; }, []( manager& ui_manager, property_set::element_properties::property_setters normal_setters, lexy::nullopt, property_set::element_properties::property_setters active_setters ) { auto style = container_style_with_state{}; for (auto& setter : normal_setters) { setter(ui_manager, style.normal); } style.hovered = style.normal; style.active = style.normal; for (auto& setter : active_setters) { setter(ui_manager, style.active); } return style; }, []( manager& ui_manager, property_set::element_properties::property_setters normal_setters, property_set::element_properties::property_setters hovered_setters, property_set::element_properties::property_setters active_setters ) { auto style = container_style_with_state{}; for (auto& setter : normal_setters) { setter(ui_manager, style.normal); } style.hovered = style.normal; style.active = style.normal; for (auto& setter : hovered_setters) { setter(ui_manager, style.hovered); } for (auto& setter : active_setters) { setter(ui_manager, style.active); } return style; } ); }; // MARK: txt props state struct text_properties_with_state { static constexpr auto rule = dsl::p<property_set::text_properties> + dsl::opt(dsl::lit_c<'&'> >> dsl::lit_c<':'> >> LEXY_LIT("hover") >> dsl::curly_bracketed(dsl::p<property_set::text_properties>)) + dsl::opt(dsl::lit_c<'&'> >> dsl::lit_c<':'> >> LEXY_LIT("active") >> dsl::curly_bracketed(dsl::p<property_set::text_properties>)) ; static constexpr auto value = lexy::callback_with_state<text_style_with_state>( []( manager& ui_manager, property_set::text_properties::property_setters normal_setters, std::optional<property_set::text_properties::property_setters> hovered_setters, std::optional<property_set::text_properties::property_setters> active_setters ) { auto style = text_style_with_state{}; for (auto& setter : normal_setters) { setter(ui_manager, style.normal); } style.hovered = style.normal; style.active = style.normal; if (hovered_setters.has_value()) { for (auto& setter : hovered_setters.value()) { setter(ui_manager, style.hovered); } } if (active_setters.has_value()) { for (auto& setter : active_setters.value()) { setter(ui_manager, style.active); } } return style; } ); }; } namespace block { // MARK: elt block struct element_block { using block_type = std::pair<std::string, element_style>; static constexpr auto rule = LEXY_LIT("element") >> dsl::hash_sign + dsl::p<basic::identifier> + dsl::curly_bracketed(dsl::p<property_set::element_properties>) ; static constexpr auto value = lexy::callback_with_state<block_type>( [](manager& ui_manager, std::string id, property_set::element_properties::property_setters setters) { auto style = element_style{}; for (auto& setter : setters) { setter(ui_manager, style); } return block_type{id, style}; } ); }; // MARK: txt block struct text_block { using block_type = std::pair<std::string, text_style>; static constexpr auto rule = LEXY_LIT("text") >> dsl::hash_sign + dsl::p<basic::identifier> + dsl::curly_bracketed(dsl::p<property_set::text_properties>) ; static constexpr auto value = lexy::callback_with_state<block_type>( [](manager& ui_manager, std::string id, property_set::text_properties::property_setters setters) { auto style = text_style{}; for (auto& setter : setters) { setter(ui_manager, style); } return block_type{id, style}; } ); }; // MARK: button block struct button_block { using block_type = std::pair<std::string, button_style>; struct container_subblock { static constexpr auto rule = dsl::lit_c<'&'> >> dsl::lit_c<'.'> >> LEXY_LIT("container") >> dsl::curly_bracketed(dsl::p<property_set::container_properties_with_state>) ; static constexpr auto value = lexy::forward<container_style_with_state>; }; struct label_subblock { static constexpr auto rule = dsl::lit_c<'&'> >> dsl::lit_c<'.'> >> LEXY_LIT("label") >> dsl::curly_bracketed(dsl::p<property_set::text_properties_with_state>) ; static constexpr auto value = lexy::forward<text_style_with_state>; }; static constexpr auto rule = LEXY_LIT("button") >> dsl::hash_sign + dsl::p<basic::identifier> + dsl::curly_bracketed(dsl::p<container_subblock> + dsl::p<label_subblock>) ; static constexpr auto value = lexy::callback<block_type>( []( std::string id, container_style_with_state container, text_style_with_state label ) { return block_type{id, button_style{container, label}}; } ); }; // MARK: input block struct input_block { using block_type = std::pair<std::string, input_style>; struct container_subblock { static constexpr auto rule = dsl::lit_c<'&'> >> dsl::lit_c<'.'> >> LEXY_LIT("container") >> dsl::curly_bracketed(dsl::p<property_set::container_properties_with_state>) ; static constexpr auto value = lexy::forward<container_style_with_state>; }; struct content_subblock { static constexpr auto rule = dsl::lit_c<'&'> >> dsl::lit_c<'.'> >> LEXY_LIT("content") >> dsl::curly_bracketed(dsl::p<property_set::text_properties_with_state>) ; static constexpr auto value = lexy::forward<text_style_with_state>; }; static constexpr auto rule = LEXY_LIT("input") >> dsl::hash_sign + dsl::p<basic::identifier> + dsl::curly_bracketed(dsl::p<container_subblock> + dsl::p<content_subblock>) ; static constexpr auto value = lexy::callback<block_type>( []( std::string id, container_style_with_state container, text_style_with_state content ) { return block_type{id, input_style{container, content}}; } ); }; } // MARK: document struct document { using block_type = std::variant< block::element_block::block_type, block::text_block::block_type, block::button_block::block_type, block::input_block::block_type >; static constexpr auto whitespace = dsl::ascii::space; static constexpr auto rule = dsl::list( dsl::p<block::element_block> | dsl::p<block::text_block> | dsl::p<block::button_block> | dsl::p<block::input_block> ); static constexpr auto value = lexy::as_list<std::vector<block_type>> >> lexy::callback<stylesheet>( [](const std::vector<block_type>& blocks) { auto sheet = stylesheet{}; for (auto& block : blocks) { if (std::holds_alternative<block::element_block::block_type>(block)) { auto [id, style] = std::get<block::element_block::block_type>(block); sheet.elements[id] = style; } else if (std::holds_alternative<block::text_block::block_type>(block)) { auto [id, style] = std::get<block::text_block::block_type>(block); sheet.texts[id] = style; } else if (std::holds_alternative<block::button_block::block_type>(block)) { auto [id, style] = std::get<block::button_block::block_type>(block); sheet.buttons[id] = style; } else if (std::holds_alternative<block::input_block::block_type>(block)) { auto [id, style] = std::get<block::input_block::block_type>(block); sheet.inputs[id] = style; } } return sheet; } ); }; stylesheet eval(manager& ui_manager, const std::string& source); }
Read Entire Article