#pragma once #include "Gallery_Properties.h" #include "adminive/adminive.hpp" #include "adminive/adapters/nlohmann_json.hpp" #include #include #include #include #include #include #include namespace renderive::web::gallery_adminive { template struct Enum_Metadata; template <> struct Enum_Metadata { static constexpr std::array values{ std::pair{Gallery_Cache_Mode::Direct, std::string_view{"Direct"}}, std::pair{Gallery_Cache_Mode::Local_Pixel, std::string_view{"Local_Pixel"}} }; }; template <> struct Enum_Metadata { static constexpr std::array values{ std::pair{Gallery_Axis_Orientation::Horizontal, std::string_view{"Horizontal"}}, std::pair{Gallery_Axis_Orientation::Vertical, std::string_view{"Vertical"}} }; }; template <> struct Enum_Metadata { static constexpr std::array values{ std::pair{Gallery_Decimal_Separator::Dot, std::string_view{"."}}, std::pair{Gallery_Decimal_Separator::Comma, std::string_view{","}} }; }; template <> struct Enum_Metadata { static constexpr std::array values{ std::pair{Gallery_Line_Interpolation::Nearest_Sample, std::string_view{"Nearest_Sample"}}, std::pair{Gallery_Line_Interpolation::Linear_Value, std::string_view{"Linear_Value"}}, std::pair{Gallery_Line_Interpolation::Linear_Power_Domain, std::string_view{"Linear_Power_Domain"}}, std::pair{Gallery_Line_Interpolation::Step_Left, std::string_view{"Step_Left"}}, std::pair{Gallery_Line_Interpolation::Step_Right, std::string_view{"Step_Right"}}, std::pair{Gallery_Line_Interpolation::Cubic_Value, std::string_view{"Cubic_Value"}} }; }; template <> struct Enum_Metadata { static constexpr std::array values{ std::pair{Gallery_Image_Interpolation::Nearest, std::string_view{"Nearest"}}, std::pair{Gallery_Image_Interpolation::Bilinear, std::string_view{"Bilinear"}}, std::pair{Gallery_Image_Interpolation::Bicubic, std::string_view{"Bicubic"}} }; }; template <> struct Enum_Metadata { static constexpr std::array values{ std::pair{Gallery_Color_Map::Spectrum, std::string_view{"Spectrum"}}, std::pair{Gallery_Color_Map::Ember, std::string_view{"Ember"}}, std::pair{Gallery_Color_Map::Grayscale, std::string_view{"Grayscale"}} }; }; template <> struct Enum_Metadata { static constexpr std::array values{ std::pair{Gallery_Constellation_Type::PSK4, std::string_view{"PSK4"}}, std::pair{Gallery_Constellation_Type::PSK8, std::string_view{"PSK8"}}, std::pair{Gallery_Constellation_Type::PSK16, std::string_view{"PSK16"}} }; }; template struct Enum_Adapter { static std::vector values() { std::vector result; result.reserve(Enum_Metadata::values.size()); for(const auto& [value, name] : Enum_Metadata::values) result.push_back(value); return result; } static std::string_view name(Enum value) { for(const auto& [candidate, name] : Enum_Metadata::values) { if(candidate == value) return name; } return {}; } static std::optional cast(std::string_view name) { for(const auto& [value, candidate] : Enum_Metadata::values) { if(candidate == name) return value; } return std::nullopt; } }; template auto boolean_property(std::string name, std::string label, std::string api) { return adminive::field(std::move(name), std::move(label)).editable().boolean_input().description(std::move(api)); } template auto number_property(std::string name, std::string label, std::string api) { return adminive::field(std::move(name), std::move(label)).editable().number_input().description(std::move(api)); } template auto text_property(std::string name, std::string label, std::string api) { return adminive::field(std::move(name), std::move(label)).editable().text_input().description(std::move(api)); } template auto color_property(std::string name, std::string label, std::string api) { return adminive::field(std::move(name), std::move(label)).editable().color_input().description(std::move(api)); } template auto select_property(std::string name, std::string label, std::string api) { return adminive::field(std::move(name), std::move(label)).editable().select_input().description(std::move(api)); } inline auto common_fields() { using T = Gallery_Common_Properties; return std::tuple{ color_property<&T::background_color>("background_color", "背景颜色", "Plot_Core::set_background_color"), boolean_property<&T::performance_overlay>("performance_overlay", "性能叠层", "set_performance_overlay_enabled"), boolean_property<&T::renderable_visible>("renderable_visible", "控件可见", "Renderable::set_visible"), select_property<&T::cache_mode>("cache_mode", "缓存模式", "Renderable::set_cache_mode"), text_property<&T::object_name>("object_name", "对象名称", "Renderable::set_object_name") }; } inline auto low_latency_fields() { using T = Gallery_Low_Latency_Properties; return std::tuple{ boolean_property<&T::frequency_limit_enabled>("frequency_limit_enabled", "启用频率上限", "Plot_Core::set_max_render_fps / clear_max_render_fps"), number_property<&T::max_render_fps>("max_render_fps", "最大渲染 FPS", "Plot_Core::set_max_render_fps"), boolean_property<&T::consumer_feedback_enabled>("consumer_feedback_enabled", "启用消费者反馈", "Plot_Core::set_consumer_feedback / clear_consumer_feedback"), boolean_property<&T::consumer_pixel_feedback_enabled>("consumer_pixel_feedback_enabled", "启用像素响应反馈", "Gallery_Plot_Session::set_client_metrics"), boolean_property<&T::consumer_presentation_feedback_enabled>("consumer_presentation_feedback_enabled", "启用浏览器呈现反馈", "Gallery_Plot_Session::set_client_metrics"), boolean_property<&T::consumer_manual_feedback_enabled>("consumer_manual_feedback_enabled", "启用手动消费者反馈", "Gallery_Plot_Session::apply_consumer_feedback"), number_property<&T::consumer_manual_fps>("consumer_manual_fps", "手动消费者 FPS", "Gallery_Plot_Session::apply_consumer_feedback") }; } inline auto axis_fields() { using T = Gallery_Axis_Properties; return std::tuple{ select_property<&T::axis_orientation>("axis_orientation", "主轴方向", "Axis::set<&Axis_Base_Properties::orientation>"), number_property<&T::axis_x_offset>("axis_x_offset", "X 偏移", "Axis::set<&Axis_Base_Properties::x>"), number_property<&T::axis_y_offset>("axis_y_offset", "Y 偏移", "Axis::set<&Axis_Base_Properties::y>"), number_property<&T::axis_length_percent>("axis_length_percent", "轴长百分比", "Axis::set<&Axis_Base_Properties::pixel_length>"), number_property<&T::tick_length>("tick_length", "主刻度长度", "Axis::set<&Axis_Base_Properties::tick_length>"), number_property<&T::sub_tick_length>("sub_tick_length", "次刻度长度", "Axis::set<&Axis_Base_Properties::sub_tick_length>"), color_property<&T::axis_color>("axis_color", "轴颜色", "Axis::set<&Axis_Base_Properties::color>"), select_property<&T::decimal_separator>("decimal_separator", "小数点", "Axis::set<&Axis_Base_Properties::locale>"), text_property<&T::unit_text>("unit_text", "单位文本", "Axis::set<&Axis_Base_Properties::unit_text>"), number_property<&T::unit_font_size>("unit_font_size", "单位字号", "Axis::set<&Axis_Base_Properties::unit_text_font>"), number_property<&T::unit_font_weight>("unit_font_weight", "单位字重", "Axis::set<&Axis_Base_Properties::unit_text_font>"), boolean_property<&T::unit_font_italic>("unit_font_italic", "单位斜体", "Axis::set<&Axis_Base_Properties::unit_text_font>"), color_property<&T::unit_pen_color>("unit_pen_color", "单位文字颜色", "Axis::set<&Axis_Base_Properties::unit_text_pen>"), color_property<&T::unit_background_color>("unit_background_color", "单位背景颜色", "Axis::set<&Axis_Base_Properties::unit_text_background_brush>"), number_property<&T::label_rotation>("label_rotation", "标签旋转角度", "Axis::set<&Axis_Base_Properties::label_rotation_degrees>"), number_property<&T::coord_origin>("coord_origin", "坐标起点", "Axis::set<&Axis_Properties::coordinates>"), number_property<&T::coord_target>("coord_target", "坐标终点", "Axis::set<&Axis_Properties::coordinates>"), number_property<&T::label_precision>("label_precision", "标签精度", "Axis::set<&Axis_Properties::precision>"), boolean_property<&T::use_wheel>("use_wheel", "启用滚轮缩放", "Axis::set<&Axis_Properties::wheel>"), boolean_property<&T::use_drag>("use_drag", "启用拖拽平移", "Axis::set<&Axis_Properties::drag>") }; } inline auto time_axis_fields() { using T = Gallery_Time_Axis_Properties; return std::tuple{ number_property<&T::time_visible_count>("time_visible_count", "可见时间点", "Time_Axis::set<&Time_Axis_Properties::visible_count>"), number_property<&T::time_label_spacing>("time_label_spacing", "时间标签间距", "Time_Axis::set<&Time_Axis_Properties::tick_label_spacing_px>"), text_property<&T::time_format>("time_format", "时间格式", "Time_Axis::set<&Time_Axis_Properties::format>"), number_property<&T::time_font_size>("time_font_size", "时间字体", "Time_Axis::set<&Axis_Base_Properties::unit_text_font>"), boolean_property<&T::time_newest_at_start>("time_newest_at_start", "最新数据位于轴起点", "Time_Axis::set<&Time_Axis_Properties::newest_at_start>") }; } inline auto hover_fields() { using T = Gallery_Hover_Properties; return std::tuple{ boolean_property<&T::hover_enabled>("hover_enabled", "悬浮信息", "Plottable_State::set<&Properties::tooltip_enabled>"), number_property<&T::hover_font_size>("hover_font_size", "悬浮字体", "Plottable_State::set<&Properties::tooltip_font>"), color_property<&T::hover_text_color>("hover_text_color", "悬浮文字", "Plottable_State::set<&Properties::tooltip_text_pen>"), color_property<&T::hover_background>("hover_background", "悬浮背景", "Plottable_State::set<&Properties::tooltip_background_brush>") }; } inline auto spectrum_fields() { using T = Gallery_Spectrum_Properties; return std::tuple{ number_property<&T::frequency_point_size>("frequency_point_size", "频率点数", "Spectrum::set<&Properties::frequency_point_size>"), number_property<&T::frequency_origin>("frequency_origin", "数据频率起点", "Spectrum::set<&Properties::frequency_range>"), number_property<&T::frequency_target>("frequency_target", "数据频率终点", "Spectrum::set<&Properties::frequency_range>"), number_property<&T::center_frequency>("center_frequency", "中心频率", "Spectrum::set<&Properties::center_frequency>"), number_property<&T::sweep_origin>("sweep_origin", "扫频区起点", "Spectrum::set<&Properties::sweep_frequency_range>"), number_property<&T::sweep_target>("sweep_target", "扫频区终点", "Spectrum::set<&Properties::sweep_frequency_range>"), boolean_property<&T::max_hold_visible>("max_hold_visible", "最大保持线", "Spectrum::set<&Properties::max_hold_visible>"), boolean_property<&T::min_hold_visible>("min_hold_visible", "最小保持线", "Spectrum::set<&Properties::min_hold_visible>"), boolean_property<&T::max_marker_visible>("max_marker_visible", "峰值 Marker", "Spectrum::set<&Properties::max_marker_visible>"), boolean_property<&T::use_min_marker>("use_min_marker", "最小值 Marker", "Spectrum::set<&Properties::use_min_marker>"), boolean_property<&T::sweep_region_visible>("sweep_region_visible", "扫频区域", "Spectrum::set<&Properties::sweep_region_visible>"), boolean_property<&T::visible_range_only>("visible_range_only", "仅绘制可见范围", "Spectrum::set<&Properties::visible_range_only>"), select_property<&T::line_interpolation>("line_interpolation", "线插值模式", "Spectrum::set<&Properties::interpolation_mode>"), color_property<&T::max_brush>("max_brush", "最大保持填充", "Spectrum::set<&Properties::max_brush>"), color_property<&T::current_brush>("current_brush", "当前曲线填充", "Spectrum::set<&Properties::current_brush>"), color_property<&T::min_brush>("min_brush", "最小保持填充", "Spectrum::set<&Properties::min_brush>"), color_property<&T::max_pen>("max_pen", "最大保持线颜色", "Spectrum::set<&Properties::max_pen>"), color_property<&T::current_pen>("current_pen", "当前曲线颜色", "Spectrum::set<&Properties::current_pen>"), color_property<&T::min_pen>("min_pen", "最小保持线颜色", "Spectrum::set<&Properties::min_pen>"), color_property<&T::selected_marker_pen>("selected_marker_pen", "选中 Marker", "Spectrum::set<&Properties::selected_marker_pen>"), color_property<&T::marker_pen>("marker_pen", "Marker 颜色", "Spectrum::set<&Properties::marker_pen>"), color_property<&T::middle_frequency_pen>("middle_frequency_pen", "中心频率线", "Spectrum::set<&Properties::middle_frequency_pen>"), color_property<&T::sweep_region_brush>("sweep_region_brush", "扫频区填充", "Spectrum::set<&Properties::sweep_region_brush>") }; } template struct Property_Fields; template <> struct Property_Fields { static auto get() { return std::tuple_cat(common_fields(), axis_fields(), time_axis_fields()); } }; template <> struct Property_Fields { static auto get() { return std::tuple_cat(common_fields(), axis_fields(), spectrum_fields(), hover_fields()); } }; template <> struct Property_Fields { static auto get() { using T = Gallery_Selection_Overlay_Properties; return std::tuple_cat(Property_Fields::get(), std::tuple{ number_property<&T::selection_font_size>("selection_font_size", "框选标签字体", "Selection_Rectangle_Overlay::set<&Properties::label_font>"), color_property<&T::selection_label_pen>("selection_label_pen", "框选标签颜色", "Selection_Rectangle_Overlay::set<&Properties::label_pen>"), color_property<&T::selection_brush>("selection_brush", "框选填充", "Selection_Rectangle_Overlay::set<&Properties::selection_brush>"), color_property<&T::selection_border>("selection_border", "框选边框", "Selection_Rectangle_Overlay::set<&Properties::selection_border_pen>") }); } }; template <> struct Property_Fields { static auto get() { using T = Gallery_Waterfall_Properties; return std::tuple_cat(common_fields(), axis_fields(), time_axis_fields(), std::tuple{ number_property<&T::frequency_origin>("frequency_origin", "频率起点", "Waterfall::set<&Properties::frequency_range>"), number_property<&T::frequency_target>("frequency_target", "频率终点", "Waterfall::set<&Properties::frequency_range>"), number_property<&T::power_origin>("power_origin", "功率起点", "Waterfall::set<&Properties::power_range>"), number_property<&T::power_target>("power_target", "功率终点", "Waterfall::set<&Properties::power_range>"), number_property<&T::frequency_bin_count>("frequency_bin_count", "频率 Bin", "Waterfall::set<&Properties::frequency_bin_count>"), boolean_property<&T::visible_range_only>("visible_range_only", "仅绘制可见范围", "Waterfall::set<&Properties::visible_range_only>"), select_property<&T::image_interpolation>("image_interpolation", "图像插值(三模式)", "Waterfall::set<&Properties::interpolation_mode>"), select_property<&T::color_map>("color_map", "颜色映射", "Waterfall::set<&Properties::color_map>") }, hover_fields()); } }; template <> struct Property_Fields { static auto get() { using T = Gallery_Afterglow_Properties; return std::tuple_cat(common_fields(), axis_fields(), std::tuple{ number_property<&T::frequency_origin>("frequency_origin", "频率起点", "Afterglow::set<&Properties::frequency_range>"), number_property<&T::frequency_target>("frequency_target", "频率终点", "Afterglow::set<&Properties::frequency_range>"), number_property<&T::power_origin>("power_origin", "功率起点", "Afterglow::set<&Properties::power_range>"), number_property<&T::power_target>("power_target", "功率终点", "Afterglow::set<&Properties::power_range>"), number_property<&T::frequency_point_size>("frequency_point_size", "频率格点", "Afterglow::set<&Properties::frequency_point_size>"), number_property<&T::power_point_size>("power_point_size", "功率格点", "Afterglow::set<&Properties::power_point_size>"), boolean_property<&T::interpolate_power>("interpolate_power", "功率 Bin 插值", "Afterglow::set<&Properties::interpolate>"), number_property<&T::attenuation_rate>("attenuation_rate", "衰减率", "Afterglow::set<&Properties::attenuation_rate>"), select_property<&T::color_map>("color_map", "颜色映射", "Afterglow::set<&Properties::color_map>") }); } }; template <> struct Property_Fields { static auto get() { using T = Gallery_Sweep_Spectrum_Properties; return std::tuple_cat(common_fields(), axis_fields(), std::tuple{ number_property<&T::frequency_origin>("frequency_origin", "扫频起点", "Sweep_Spectrum::set<&Properties::frequency_range>"), number_property<&T::frequency_target>("frequency_target", "扫频终点", "Sweep_Spectrum::set<&Properties::frequency_range>"), number_property<&T::bins_per_block>("bins_per_block", "每块 Bin", "Sweep_Spectrum::set<&Properties::bins_per_block>"), number_property<&T::block_count>("block_count", "块数量", "Sweep_Spectrum::set<&Properties::block_count>"), color_property<&T::sweep_pen>("sweep_pen", "扫频曲线", "Sweep_Spectrum::set<&Properties::pen>"), color_property<&T::current_frequency_pen>("current_frequency_pen", "当前频率游标", "Sweep_Spectrum::set<&Properties::current_frequency_pen>"), boolean_property<&T::visible_range_only>("visible_range_only", "仅绘制可见范围", "Sweep_Spectrum::set<&Properties::visible_range_only>"), select_property<&T::line_interpolation>("line_interpolation", "线插值模式", "Sweep_Spectrum::set<&Properties::interpolation_mode>") }); } }; template <> struct Property_Fields { static auto get() { using T = Gallery_Frequency_Trace_Properties; return std::tuple_cat(common_fields(), axis_fields(), time_axis_fields(), std::tuple{ color_property<&T::trace_pen>("trace_pen", "轨迹颜色", "Frequency_Trace::set<&Properties::pen>"), number_property<&T::trace_pen_width>("trace_pen_width", "轨迹宽度", "Frequency_Trace::set<&Properties::pen>"), number_property<&T::trace_value_min>("trace_value_min", "数值下限", "Axis::set<&Axis_Properties::coordinates>"), number_property<&T::trace_value_max>("trace_value_max", "数值上限", "Axis::set<&Axis_Properties::coordinates>") }); } }; template <> struct Property_Fields { static auto get() { using T = Gallery_Constellation_Properties; return std::tuple_cat(common_fields(), axis_fields(), std::tuple{ number_property<&T::i_origin>("i_origin", "I 起点", "Constellation_Diagram::set<&Properties::i_range>"), number_property<&T::i_target>("i_target", "I 终点", "Constellation_Diagram::set<&Properties::i_range>"), number_property<&T::q_origin>("q_origin", "Q 起点", "Constellation_Diagram::set<&Properties::q_range>"), number_property<&T::q_target>("q_target", "Q 终点", "Constellation_Diagram::set<&Properties::q_range>"), color_property<&T::point_color>("point_color", "采样点颜色", "Constellation_Diagram::set<&Properties::point_color>"), color_property<&T::anchor_color>("anchor_color", "锚点颜色", "Constellation_Diagram::set<&Properties::anchor_color>"), number_property<&T::point_lifetime_ms>("point_lifetime_ms", "采样点寿命", "Constellation_Diagram::set<&Properties::point_lifetime_ms>"), select_property<&T::constellation_type>("constellation_type", "星座模式", "Constellation_Diagram::set<&Properties::type>"), number_property<&T::phase_offset>("phase_offset", "相位偏移", "Constellation_Diagram::set<&Properties::phase_offset_radians>") }); } }; template struct Property_Fields> { static auto get() { return std::tuple_cat(Property_Fields::get(), low_latency_fields()); } }; struct Property_Validator { template void operator()(const T& value, adminive::Validation_Context& context) const { if constexpr(requires { value.coord_origin; value.coord_target; }) validate_pair(context, "coord_origin", "coord_target", value.coord_origin, value.coord_target); if constexpr(requires { value.frequency_origin; value.frequency_target; }) validate_pair(context, "frequency_origin", "frequency_target", value.frequency_origin, value.frequency_target); if constexpr(requires { value.power_origin; value.power_target; }) validate_pair(context, "power_origin", "power_target", value.power_origin, value.power_target); if constexpr(requires { value.sweep_origin; value.sweep_target; }) validate_pair(context, "sweep_origin", "sweep_target", value.sweep_origin, value.sweep_target); if constexpr(requires { value.trace_value_min; value.trace_value_max; }) validate_pair(context, "trace_value_min", "trace_value_max", value.trace_value_min, value.trace_value_max); if constexpr(requires { value.i_origin; value.i_target; }) validate_pair(context, "i_origin", "i_target", value.i_origin, value.i_target); if constexpr(requires { value.q_origin; value.q_target; }) validate_pair(context, "q_origin", "q_target", value.q_origin, value.q_target); } private: template static void validate_pair(adminive::Validation_Context& context, std::string_view origin, std::string_view target, const Origin& origin_value, const Target& target_value) { if(static_cast(origin_value) == static_cast(target_value)) { context.error(std::string(origin), std::string(origin) + " 与 " + std::string(target) + " 不能相等"); context.error(std::string(target), std::string(origin) + " 与 " + std::string(target) + " 不能相等"); } } }; template auto property_descriptor() { auto fields = Property_Fields::get(); return std::apply([](auto... field) { return adminive::object("renderive_gallery_properties", "Renderive 控件属性", std::move(field)...).validator(Property_Validator{}); }, std::move(fields)); } } namespace adminive { template struct Value_Adapter, Json> { using Storage = renderive::web::Gallery_Number; using value_type = T; static constexpr T minimum = Minimum; static constexpr T maximum = Maximum; static constexpr T multiple_of = Step; static T read(const Storage& value) noexcept { return value.value(); } static void write(Storage& target, T value) { target = value; } }; template struct Value_Adapter { using value_type = std::string; static const std::string& read(const renderive::web::Gallery_Text& value) noexcept { return value.value(); } static void write(renderive::web::Gallery_Text& target, std::string value) { target = std::move(value); } }; template struct Value_Adapter { using value_type = std::string; static const std::string& read(const renderive::web::Gallery_Color& value) noexcept { return value.value(); } static void write(renderive::web::Gallery_Color& target, std::string value) { target = std::move(value); } }; #define RENDERIVE_GALLERY_ENUM_ADAPTER(Type) \ template <> \ struct Enum_Adapter : renderive::web::gallery_adminive::Enum_Adapter {} RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Cache_Mode); RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Axis_Orientation); RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Decimal_Separator); RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Line_Interpolation); RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Image_Interpolation); RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Color_Map); RENDERIVE_GALLERY_ENUM_ADAPTER(Gallery_Constellation_Type); #undef RENDERIVE_GALLERY_ENUM_ADAPTER #define RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Type) \ template <> \ struct Type_Descriptor { \ static auto get() { \ return renderive::web::gallery_adminive::property_descriptor(); \ } \ } RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Axis_Lab_Properties); RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Spectrum_Properties); RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Selection_Overlay_Properties); RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Waterfall_Properties); RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Afterglow_Properties); RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Sweep_Spectrum_Properties); RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Frequency_Trace_Properties); RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR(Gallery_Constellation_Properties); #undef RENDERIVE_GALLERY_PROPERTY_DESCRIPTOR template struct Type_Descriptor> { static auto get() { using T = renderive::web::Gallery_Low_Latency_Case_Properties; return renderive::web::gallery_adminive::property_descriptor(); } }; }