diff --git a/libelemental.vcxproj b/libelemental.vcxproj index 1e1636c..10a92a8 100644 --- a/libelemental.vcxproj +++ b/libelemental.vcxproj @@ -20,6 +20,7 @@ + diff --git a/libelemental.vcxproj.filters b/libelemental.vcxproj.filters index 315260c..ded84b8 100644 --- a/libelemental.vcxproj.filters +++ b/libelemental.vcxproj.filters @@ -301,6 +301,9 @@ src\el\elements + + src\el + diff --git a/src/el/dsl.h b/src/el/dsl.h new file mode 100644 index 0000000..930a032 --- /dev/null +++ b/src/el/dsl.h @@ -0,0 +1,134 @@ +/** + ****************************************************************************** + * Elemental Forms : a lightweight user interface framework * + ****************************************************************************** + * ©2015 Ben Vanik. All rights reserved. Released under the BSD license. * + * Portions ©2011-2015 Emil Segerås: https://github.com/fruxo/turbobadger * + ****************************************************************************** + */ + +#ifndef EL_DSL_H_ +#define EL_DSL_H_ + +#include +#include +#include +#include + +#include "el/color.h" +#include "el/element.h" +#include "el/parsing/parse_node.h" +#include "el/rect.h" + +namespace el { +namespace dsl { + +inline std::string operator"" _px(uint64_t value) { + return std::to_string(value) + "px"; +} +inline std::string operator"" _dp(uint64_t value) { + return std::to_string(value) + "dp"; +} +inline std::string operator"" _mm(uint64_t value) { + return std::to_string(value) + "mm"; +} + +class Node { + public: + el::parsing::ParseNode* parse_node() const { return parse_node_; } + + Node& set(const char* key, int32_t value) { + return set(key, el::Value(value)); + } + + Node& set(const char* key, float value) { return set(key, el::Value(value)); } + + Node& set(const char* key, const char* value) { + return set(key, el::Value(value)); + } + + Node& set(const char* key, const std::string& value) { + return set(key, el::Value(value.c_str())); + } + + Node& set(const char* key, el::Rect value) { + auto va = new el::ValueArray(); + va->AddInteger(value.x); + va->AddInteger(value.y); + va->AddInteger(value.w); + va->AddInteger(value.h); + auto node = GetOrCreateNode(key); + node->value().set_array(va, el::Value::Set::kTakeOwnership); + return *this; + } + + Node& set(const char* key, el::Value& value) { + auto node = GetOrCreateNode(key); + node->TakeValue(value); + return *this; + } + + Node& child_list(std::initializer_list children) { + for (auto& child : children) { + parse_node_->Add(child.parse_node_); + } + return *this; + } + + protected: + Node(const char* name, + std::vector> properties = {}, + std::vector children = {}) { + parse_node_ = el::parsing::ParseNode::Create(name); + for (auto& prop : properties) { + set(prop.first, prop.second); + } + for (auto& child : children) { + parse_node_->Add(child.parse_node_); + } + } + + el::parsing::ParseNode* GetOrCreateNode(const char* name) { + return parse_node_->GetNode(name, + el::parsing::ParseNode::MissingPolicy::kCreate); + } + + el::parsing::ParseNode* parse_node_ = nullptr; +}; + +struct Dimension { + Dimension(int32_t value) : value(std::to_string(value) + "px") {} + Dimension(const char* value) : value(value) {} + Dimension(std::string value) : value(std::move(value)) {} + operator std::string() { return value; } + std::string value; +}; + +struct Id { + Id(int32_t value) : is_int(true), int_value(value) {} + Id(const char* value) : str_value(value) {} + Id(std::string value) : str_value(std::move(value)) {} + void set(Node* node, const char* key) { + if (is_int) { + node->set(key, int_value); + } else { + node->set(key, str_value); + } + } + bool is_int = false; + int32_t int_value = 0; + std::string str_value; +}; + +struct CloneNode : public Node { + using R = CloneNode; + CloneNode(const Node& source) : Node(source.parse_node()->name()) { + parse_node_->value().Copy(source.parse_node()->value()); + parse_node_->CloneChildren(source.parse_node()); + } +}; + +} // namespace dsl +} // namespace el + +#endif // EL_DSL_H_ diff --git a/src/el/element.cc b/src/el/element.cc index e9e2e53..b0599e1 100644 --- a/src/el/element.cc +++ b/src/el/element.cc @@ -74,7 +74,9 @@ void Element::RegisterInflater() { Element::Element() = default; Element::~Element() { - assert(!m_parent); // A element must be removed from parent before deleted. + // A element must be removed from parent before deleted. + RemoveFromParent(); + assert(!m_parent); m_packed.is_dying = true; if (this == hovered_element) { @@ -108,6 +110,12 @@ void Element::LoadNodeTree(parsing::ParseNode* node) { return parsing::ElementFactory::get()->LoadNodeTree(this, node); } +void Element::LoadNodeTree(const dsl::Node& node) { + parsing::ParseNode parse_node; + parse_node.Add(node.parse_node()); + LoadNodeTree(&parse_node); +} + // Sets the id from the given node. void Element::SetIdFromNode(TBID& id, parsing::ParseNode* node) { if (!node) return; diff --git a/src/el/element.h b/src/el/element.h index 52d8b4a..cb6e19c 100644 --- a/src/el/element.h +++ b/src/el/element.h @@ -15,6 +15,7 @@ #include #include +#include "el/dsl.h" #include "el/element_value.h" #include "el/event.h" #include "el/font_description.h" @@ -44,6 +45,8 @@ namespace text { class FontFace; } // namespace text +using util::kInvalidDimension; + enum class Align { kLeft, kTop, @@ -277,6 +280,7 @@ class Element : public util::TypedObject, return LoadData(data.c_str(), data.size()); } void LoadNodeTree(parsing::ParseNode* node); + void LoadNodeTree(const dsl::Node& node); inline Rect rect() const { return m_rect; } // Sets the rect for this element in its parent. @@ -1125,6 +1129,196 @@ class ElementSkinConditionContext : public SkinConditionContext { Element* m_element = nullptr; }; +namespace dsl { + +using el::Align; +using el::Axis; +using ElementState = el::Element::State; +using el::Gravity; +using el::Rect; +using el::TextAlign; +using el::Visibility; + +template +struct ElementNode : public Node { + using R = T; + ElementNode(const char* name, + std::vector> properties = {}, + std::vector children = {}) + : Node(name, std::move(properties), std::move(children)) {} + + // TBID + R& id(Id value) { + value.set(this, "id"); + return *reinterpret_cast(this); + } + // TBID + R& group_id(Id value) { + value.set(this, "group-id"); + return *reinterpret_cast(this); + } + // TBID + R& skin(Id value) { + value.set(this, "skin"); + return *reinterpret_cast(this); + } + // Number + R& data(int32_t value) { + set("data", value); + return *reinterpret_cast(this); + } + R& data(float value) { + set("data", value); + return *reinterpret_cast(this); + } + // + R& is_group_root(bool value) { + set("is-group-root", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_focusable(bool value) { + set("is-focusable", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_long_clickable(bool value) { + set("want-long-click", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& ignore_input(bool value) { + set("ignore-input", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& opacity(float value) { + set("opacity", value); + return *reinterpret_cast(this); + } + // + R& connection(std::string value) { + set("connection", value); + return *reinterpret_cast(this); + } + // + R& axis(Axis value) { + set("axis", el::to_string(value)); + return *reinterpret_cast(this); + } + // + R& gravity(Gravity value) { + set("gravity", el::to_string(value)); + return *reinterpret_cast(this); + } + // + R& visibility(Visibility value) { + set("visibility", el::to_string(value)); + return *reinterpret_cast(this); + } + // + R& state(ElementState value) { + set("state", el::to_string(value)); + return *reinterpret_cast(this); + } + // + R& is_enabled(bool value) { + set("state", + to_string(value ? ElementState::kNone : ElementState::kDisabled)); + return *reinterpret_cast(this); + } + // Rect + R& rect(Rect value) { + set("rect", std::move(value)); + return *reinterpret_cast(this); + } + // + R& width(Dimension value) { + set("lp>width", value); + return *reinterpret_cast(this); + } + // + R& min_width(Dimension value) { + set("lp>min-width", value); + return *reinterpret_cast(this); + } + // + R& max_width(Dimension value) { + set("lp>max-width", value); + return *reinterpret_cast(this); + } + // + R& preferred_width(Dimension value) { + set("lp>pref-width", value); + return *reinterpret_cast(this); + } + // + R& height(Dimension value) { + set("lp>height", value); + return *reinterpret_cast(this); + } + // + R& min_height(Dimension value) { + set("lp>min-height", value); + return *reinterpret_cast(this); + } + // + R& max_height(Dimension value) { + set("lp>max-height", value); + return *reinterpret_cast(this); + } + // + R& preferred_height(Dimension value) { + set("lp>pref-height", value); + return *reinterpret_cast(this); + } + // + R& tooltip(std::string value) { + set("tooltip", value); + return *reinterpret_cast(this); + } + // The Element will be focused automatically the first time its Window is + // activated. + R& autofocus(bool value) { + set("autofocus", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& font(const char* name, int32_t size_px) { + set("font>name", name); + set("font>size", size_px); + return *reinterpret_cast(this); + } + // + R& font_name(std::string value) { + set("font>name", value); + return *reinterpret_cast(this); + } + // + R& font_size(Dimension value) { + set("font>size", value); + return *reinterpret_cast(this); + } + + R& clone(Node node) { + parse_node_->Add(CloneNode(node).parse_node()); + return *reinterpret_cast(this); + } + R& child(Node node) { + parse_node_->Add(node.parse_node()); + return *reinterpret_cast(this); + } + R& children() { return *reinterpret_cast(this); } + template + R& children(Node child_node, C... other_children) { + child(child_node); + children(other_children...); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl + } // namespace el #endif // EL_ELEMENT_H_ diff --git a/src/el/elements/box.h b/src/el/elements/box.h index 4660149..5cea259 100644 --- a/src/el/elements/box.h +++ b/src/el/elements/box.h @@ -26,6 +26,14 @@ class Box : public Element { }; } // namespace elements +namespace dsl { + +struct BoxNode : public ElementNode { + using R = BoxNode; + BoxNode() : ElementNode("Box") {} +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_BOX_H_ diff --git a/src/el/elements/button.h b/src/el/elements/button.h index e2ee5ff..c398f04 100644 --- a/src/el/elements/button.h +++ b/src/el/elements/button.h @@ -94,6 +94,28 @@ class Button : public Element, protected MessageHandler { }; } // namespace elements +namespace dsl { + +struct ButtonNode : public ElementNode { + using R = ButtonNode; + ButtonNode(const char* text = nullptr) : ElementNode("Button") { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } + // + R& toggle_mode(bool value) { + set("toggle-mode", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_BUTTON_H_ diff --git a/src/el/elements/check_box.h b/src/el/elements/check_box.h index 2171efb..21542fd 100644 --- a/src/el/elements/check_box.h +++ b/src/el/elements/check_box.h @@ -29,6 +29,19 @@ class CheckBox : public parts::BaseRadioCheckBox { }; } // namespace elements +namespace dsl { + +struct CheckBoxNode : public ElementNode { + using R = CheckBoxNode; + CheckBoxNode() : ElementNode("CheckBox") {} + // + R& value(bool value) { + set("value", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_CHECK_BOX_H_ diff --git a/src/el/elements/drop_down_button.h b/src/el/elements/drop_down_button.h index 9a62bb4..203e8af 100644 --- a/src/el/elements/drop_down_button.h +++ b/src/el/elements/drop_down_button.h @@ -72,6 +72,19 @@ class DropDownButton : public Button, public ListItemObserver { }; } // namespace elements +namespace dsl { + +struct DropDownButtonNode : public ItemListElementNode { + using R = DropDownButtonNode; + DropDownButtonNode() : ItemListElementNode("DropDownButton") {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_DROP_DOWN_BUTTON_H_ diff --git a/src/el/elements/group_box.h b/src/el/elements/group_box.h index 6731c1b..5dbbe39 100644 --- a/src/el/elements/group_box.h +++ b/src/el/elements/group_box.h @@ -75,6 +75,29 @@ class GroupBox : public Element { }; } // namespace elements +namespace dsl { + +struct GroupBoxNode : public ElementNode { + using R = GroupBoxNode; + GroupBoxNode(const char* text = nullptr) : ElementNode("GroupBox") { + if (text) { + this->text(text); + } + } + R& content(Node child) { return this->child(child); } + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_GROUP_BOX_H_ diff --git a/src/el/elements/icon_box.h b/src/el/elements/icon_box.h index a371e57..99bc902 100644 --- a/src/el/elements/icon_box.h +++ b/src/el/elements/icon_box.h @@ -31,6 +31,14 @@ class IconBox : public Element { }; } // namespace elements +namespace dsl { + +struct IconBoxNode : public ElementNode { + using R = IconBoxNode; + IconBoxNode() : ElementNode("IconBox") {} +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_ICON_BOX_H_ diff --git a/src/el/elements/image_box.h b/src/el/elements/image_box.h index 8e4bfa5..e659183 100644 --- a/src/el/elements/image_box.h +++ b/src/el/elements/image_box.h @@ -50,6 +50,23 @@ class ImageBox : public Element { }; } // namespace elements +namespace dsl { + +struct ImageBoxNode : public ElementNode { + using R = ImageBoxNode; + ImageBoxNode(const char* filename = nullptr) : ElementNode("ImageBox") { + if (filename) { + this->filename(filename); + } + } + // + R& filename(std::string value) { + set("filename", value); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_IMAGE_BOX_H_ diff --git a/src/el/elements/label.h b/src/el/elements/label.h index 26d3d20..7dc59f0 100644 --- a/src/el/elements/label.h +++ b/src/el/elements/label.h @@ -82,6 +82,28 @@ class Label : public Element { }; } // namespace elements +namespace dsl { + +struct LabelNode : public ElementNode { + using R = LabelNode; + LabelNode(const char* text = nullptr) : ElementNode("Label") { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } + // + R& text_align(TextAlign value) { + set("text-align", el::to_string(value)); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_LABEL_H_ diff --git a/src/el/elements/label_container.h b/src/el/elements/label_container.h index 648d34f..cc98c7f 100644 --- a/src/el/elements/label_container.h +++ b/src/el/elements/label_container.h @@ -52,6 +52,24 @@ class LabelContainer : public Element { }; } // namespace elements +namespace dsl { + +struct LabelContainerNode : public ElementNode { + using R = LabelContainerNode; + LabelContainerNode(const char* text, std::vector children = {}) + : ElementNode("LabelContainer", {}, std::move(children)) { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_LABEL_CONTAINER_H_ diff --git a/src/el/elements/layout_box.h b/src/el/elements/layout_box.h index b3452d6..5987458 100644 --- a/src/el/elements/layout_box.h +++ b/src/el/elements/layout_box.h @@ -177,6 +177,52 @@ class LayoutBox : public Element { }; } // namespace elements +namespace dsl { + +using el::elements::LayoutSize; +using el::elements::LayoutPosition; +using el::elements::LayoutDistribution; +using el::elements::LayoutDistributionPosition; +using el::elements::LayoutOrder; +using el::elements::LayoutOverflow; + +struct LayoutBoxNode : public ElementNode { + using R = LayoutBoxNode; + LayoutBoxNode(std::vector children = {}) + : ElementNode("LayoutBox", {}, std::move(children)) {} + // + R& spacing(Dimension value) { + set("spacing", value); + return *reinterpret_cast(this); + } + // + R& size(LayoutSize value) { + set("size", el::elements::to_string(value)); + return *reinterpret_cast(this); + } + // + R& position(LayoutPosition value) { + set("position", el::elements::to_string(value)); + return *reinterpret_cast(this); + } + // + R& overflow(LayoutOverflow value) { + set("overflow", el::elements::to_string(value)); + return *reinterpret_cast(this); + } + // + R& distribution(LayoutDistribution value) { + set("distribution", el::elements::to_string(value)); + return *reinterpret_cast(this); + } + // + R& distribution_position(LayoutDistributionPosition value) { + set("distribution-position", el::elements::to_string(value)); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_LAYOUT_BOX_H_ diff --git a/src/el/elements/list_box.h b/src/el/elements/list_box.h index 4a1ed25..344ce5d 100644 --- a/src/el/elements/list_box.h +++ b/src/el/elements/list_box.h @@ -112,6 +112,19 @@ class ListBox : public Element, public ListItemObserver { }; } // namespace elements +namespace dsl { + +struct ListBoxNode : public ItemListElementNode { + using R = ListBoxNode; + ListBoxNode() : ItemListElementNode("ListBox") {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_LIST_BOX_H_ diff --git a/src/el/elements/progress_spinner.h b/src/el/elements/progress_spinner.h index 3d01137..6329aff 100644 --- a/src/el/elements/progress_spinner.h +++ b/src/el/elements/progress_spinner.h @@ -56,6 +56,19 @@ class ProgressSpinner : public Element, protected MessageHandler { }; } // namespace elements +namespace dsl { + +struct ProgressSpinnerNode : public ElementNode { + using R = ProgressSpinnerNode; + ProgressSpinnerNode() : ElementNode("ProgressSpinner") {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_PROGRESS_SPINNER_H_ diff --git a/src/el/elements/radio_button.h b/src/el/elements/radio_button.h index 87eb445..d68168b 100644 --- a/src/el/elements/radio_button.h +++ b/src/el/elements/radio_button.h @@ -30,6 +30,19 @@ class RadioButton : public parts::BaseRadioCheckBox { }; } // namespace elements +namespace dsl { + +struct RadioButtonNode : public ElementNode { + using R = RadioButtonNode; + RadioButtonNode() : ElementNode("RadioButton") {} + // + R& value(bool value) { + set("value", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_RADIO_BUTTON_H_ diff --git a/src/el/elements/scroll_bar.h b/src/el/elements/scroll_bar.h index 31cb072..20612aa 100644 --- a/src/el/elements/scroll_bar.h +++ b/src/el/elements/scroll_bar.h @@ -73,6 +73,24 @@ class ScrollBar : public Element { }; } // namespace elements +namespace dsl { + +struct ScrollBarNode : public ElementNode { + using R = ScrollBarNode; + ScrollBarNode() : ElementNode("ScrollBar") {} + // + R& value(float value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& axis(Axis value) { + set("axis", el::to_string(value)); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_SCROLL_BAR_H_ diff --git a/src/el/elements/scroll_container.h b/src/el/elements/scroll_container.h index e79863d..b3db2a6 100644 --- a/src/el/elements/scroll_container.h +++ b/src/el/elements/scroll_container.h @@ -83,6 +83,32 @@ class ScrollContainer : public Element { }; } // namespace elements +namespace dsl { + +using el::elements::ScrollMode; + +struct ScrollContainerNode : public ElementNode { + using R = ScrollContainerNode; + ScrollContainerNode(std::vector children = {}) + : ElementNode("ScrollContainer", {}, std::move(children)) {} + // + R& adapt_content(bool value) { + set("adapt-content", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& adapt_to_content(bool value) { + set("adapt-to-content", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& scroll_mode(ScrollMode value) { + set("scroll-mode", el::elements::to_string(value)); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_SCROLL_CONTAINER_H_ diff --git a/src/el/elements/separator.h b/src/el/elements/separator.h index 4fa56a7..ed7886e 100644 --- a/src/el/elements/separator.h +++ b/src/el/elements/separator.h @@ -26,6 +26,14 @@ class Separator : public Element { }; } // namespace elements +namespace dsl { + +struct SeparatorNode : public ElementNode { + using R = SeparatorNode; + SeparatorNode() : ElementNode("Separator") {} +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_SEPARATOR_H_ diff --git a/src/el/elements/slider.h b/src/el/elements/slider.h index ba731b8..61fbd4a 100644 --- a/src/el/elements/slider.h +++ b/src/el/elements/slider.h @@ -62,6 +62,34 @@ class Slider : public Element { }; } // namespace elements +namespace dsl { + +struct SliderNode : public ElementNode { + using R = SliderNode; + SliderNode() : ElementNode("Slider") {} + // + R& value(float value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& min(float value) { + set("min", value); + return *reinterpret_cast(this); + } + // + R& max(float value) { + set("max", value); + return *reinterpret_cast(this); + } + // + R& axis(Axis value) { + set("axis", el::to_string(value)); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_SLIDER_H_ diff --git a/src/el/elements/spin_box.h b/src/el/elements/spin_box.h index 5fae375..48c8f32 100644 --- a/src/el/elements/spin_box.h +++ b/src/el/elements/spin_box.h @@ -59,6 +59,35 @@ class SpinBox : public Element { }; } // namespace elements +namespace dsl { + +struct SpinBoxNode : public ElementNode { + using R = SpinBoxNode; + SpinBoxNode() : ElementNode("SpinBox") {} + SpinBoxNode(int32_t default_value, int32_t min_value, int32_t max_value) + : ElementNode("SpinBox") { + value(default_value); + min(min_value); + max(max_value); + } + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& min(int32_t value) { + set("min", value); + return *reinterpret_cast(this); + } + // + R& max(int32_t value) { + set("max", value); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_SPIN_BOX_H_ diff --git a/src/el/elements/split_container.h b/src/el/elements/split_container.h index 6077f41..b182b35 100644 --- a/src/el/elements/split_container.h +++ b/src/el/elements/split_container.h @@ -85,6 +85,46 @@ class SplitContainer : public Element { }; } // namespace elements +namespace dsl { + +using el::elements::FixedPane; + +struct SplitContainerNode : public ElementNode { + using R = SplitContainerNode; + SplitContainerNode(std::vector children = {}) + : ElementNode("SplitContainer", {}, std::move(children)) {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& min(int32_t value) { + set("min", value); + return *reinterpret_cast(this); + } + // + R& max(int32_t value) { + set("max", value); + return *reinterpret_cast(this); + } + // + R& axis(Axis value) { + set("axis", el::to_string(value)); + return *reinterpret_cast(this); + } + // + R& fixed_pane(FixedPane value) { + set("fixed", el::elements::to_string(value)); + return *reinterpret_cast(this); + } + SplitContainerNode& pane(Node content) { + parse_node_->Add(content.parse_node()); + return *this; + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_SPLIT_CONTAINER_H_ diff --git a/src/el/elements/tab_container.h b/src/el/elements/tab_container.h index 020f06f..3bcb688 100644 --- a/src/el/elements/tab_container.h +++ b/src/el/elements/tab_container.h @@ -75,6 +75,33 @@ class TabContainer : public Element { }; } // namespace elements +namespace dsl { + +struct TabContainerNode : public ElementNode { + using R = TabContainerNode; + TabContainerNode(std::vector children = {}) + : ElementNode("TabContainer", {}, std::move(children)) {} + // + R& value(int32_t value) { + set("value", value); + return *reinterpret_cast(this); + } + // + R& align(Align value) { + set("align", el::to_string(value)); + return *reinterpret_cast(this); + } + // content? + // root? + TabContainerNode& tab(Node tab_button, Node tab_content) { + auto tabs_node = GetOrCreateNode("tabs"); + tabs_node->Add(tab_button.parse_node()); + parse_node_->Add(tab_content.parse_node()); + return *this; + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_TAB_CONTAINER_H_ diff --git a/src/el/elements/text_box.h b/src/el/elements/text_box.h index 0dee4ea..6af4957 100644 --- a/src/el/elements/text_box.h +++ b/src/el/elements/text_box.h @@ -218,6 +218,65 @@ class TextBox : public Element, }; } // namespace elements +namespace dsl { + +using el::elements::EditType; + +struct TextBoxNode : public ElementNode { + using R = TextBoxNode; + TextBoxNode(const char* text = nullptr) : ElementNode("TextBox") { + if (text) { + this->text(text); + } + } + // + R& text(std::string value) { + set("text", value); + return *reinterpret_cast(this); + } + // + R& is_multiline(bool value) { + set("multiline", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_styling(bool value) { + set("styling", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_read_only(bool value) { + set("readonly", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& is_wrapping(bool value) { + set("wrap", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& adapt_to_content(bool value) { + set("adapt-to-content", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& virtual_width(Dimension value) { + set("virtual-width", value); + return *reinterpret_cast(this); + } + // + R& placeholder(std::string value) { + set("placeholder", value); + return *reinterpret_cast(this); + } + // + R& type(EditType value) { + set("type", el::elements::to_string(value)); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_EDITFIELD_H diff --git a/src/el/elements/toggle_container.h b/src/el/elements/toggle_container.h index 97c4d87..d372727 100644 --- a/src/el/elements/toggle_container.h +++ b/src/el/elements/toggle_container.h @@ -64,6 +64,31 @@ class ToggleContainer : public Element { }; } // namespace elements +namespace dsl { + +using el::elements::ToggleAction; + +struct ToggleContainerNode : public ElementNode { + using R = ToggleContainerNode; + ToggleContainerNode() : ElementNode("ToggleContainer") {} + // + R& value(bool value) { + set("value", value ? 1 : 0); + return *reinterpret_cast(this); + } + // + R& toggle_action(ToggleAction value) { + set("toggle", el::elements::to_string(value)); + return *reinterpret_cast(this); + } + // + R& invert(bool value) { + set("invert", value ? 1 : 0); + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_ELEMENTS_TOGGLE_CONTAINER_H_ diff --git a/src/el/io/memory_file_system.cc b/src/el/io/memory_file_system.cc index 72a6e34..d39c610 100644 --- a/src/el/io/memory_file_system.cc +++ b/src/el/io/memory_file_system.cc @@ -43,7 +43,7 @@ class MemoryFile : public File { MemoryFileSystem::MemoryFileSystem() = default; void MemoryFileSystem::AddFile(std::string filename, const void* data, - size_t length) { + size_t length) { file_entries_[filename] = {reinterpret_cast(data), length}; } @@ -53,7 +53,7 @@ std::unique_ptr MemoryFileSystem::OpenRead(std::string filename) { return nullptr; } return std::make_unique(it->first, it->second.first, - it->second.second); + it->second.second); } } // namespace io diff --git a/src/el/list_item.h b/src/el/list_item.h index 382dbe8..0288691 100644 --- a/src/el/list_item.h +++ b/src/el/list_item.h @@ -13,6 +13,7 @@ #include #include +#include "el/dsl.h" #include "el/id.h" #include "el/util/intrusive_list.h" #include "el/value.h" @@ -237,6 +238,101 @@ class GenericStringItemSource : public ListItemSourceList { GenericStringItemSource* target_source); }; +namespace dsl { + +struct Item { + std::string id; + std::string text; + Item(std::string id, std::string text) : id(id), text(text) {} +}; + +template +struct ItemListElementNode : public ElementNode { + protected: + ItemListElementNode(const char* name) : ElementNode(name) {} + + public: + T& item(std::string text) { + using parsing::ParseNode; + auto items_node = GetOrCreateNode("items"); + auto node = ParseNode::Create("item"); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(el::Value(text.c_str())); + node->Add(text_node); + items_node->Add(node); + return *reinterpret_cast(this); + } + T& item(std::string id, std::string text) { + using parsing::ParseNode; + auto items_node = GetOrCreateNode("items"); + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(el::Value(id.c_str())); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(el::Value(text.c_str())); + node->Add(text_node); + items_node->Add(node); + return *reinterpret_cast(this); + } + T& item(int32_t id, std::string text) { + using parsing::ParseNode; + auto items_node = GetOrCreateNode("items"); + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(el::Value(id)); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(el::Value(text.c_str())); + node->Add(text_node); + items_node->Add(node); + return *reinterpret_cast(this); + } + T& items(std::initializer_list items) { + using parsing::ParseNode; + auto items_node = GetOrCreateNode("items"); + for (auto& item : items) { + auto node = ParseNode::Create("item"); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(el::Value(item.c_str())); + node->Add(text_node); + items_node->Add(node); + } + return *reinterpret_cast(this); + } + T& items(std::initializer_list> items) { + using parsing::ParseNode; + auto items_node = GetOrCreateNode("items"); + for (auto& item : items) { + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(el::Value(item.first)); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(el::Value(item.second.c_str())); + node->Add(text_node); + items_node->Add(node); + } + return *reinterpret_cast(this); + } + T& items(std::initializer_list items) { + using parsing::ParseNode; + auto items_node = GetOrCreateNode("items"); + for (auto& item : items) { + auto node = ParseNode::Create("item"); + auto id_node = ParseNode::Create("id"); + id_node->TakeValue(el::Value(item.id.c_str())); + node->Add(id_node); + auto text_node = ParseNode::Create("text"); + text_node->TakeValue(el::Value(item.text.c_str())); + node->Add(text_node); + items_node->Add(node); + } + return *reinterpret_cast(this); + } +}; + +} // namespace dsl } // namespace el #endif // EL_LIST_ITEM_H_ diff --git a/src/el/util/object.h b/src/el/util/object.h index 0daa55d..d6c9c9f 100644 --- a/src/el/util/object.h +++ b/src/el/util/object.h @@ -67,7 +67,7 @@ const T* SafeCast(const TypedObject* obj) { // Implements the methods for safe typecasting without requiring RTTI. #define TBOBJECT_SUBCLASS(clazz, baseclazz) \ - const char* GetTypeName() const override { return #clazz; } \ + const char* GetTypeName() const override { return #clazz; } \ bool IsOfTypeId(const el::util::tb_type_id_t type_id) const override { \ return el::util::TypedObject::GetTypeId() == type_id \ ? true \ diff --git a/testbed/resources/test_ui.tb.txt b/testbed/resources/test_ui.tb.txt index 039a9e2..f8ee673 100644 --- a/testbed/resources/test_ui.tb.txt +++ b/testbed/resources/test_ui.tb.txt @@ -23,6 +23,7 @@ LayoutBox: axis: y, distribution-position: "left top", distribution: "available" Button: skin: "Button.flat", text: "Close with dim & alert", id: "Window.close" Button: skin: "Button.flat", text: "Fullscreen Window", id: "test-fullscreen-window" Button: skin: "Button.flat", text: "ResourceEditWindow", id: "test-resource-edit" + Button: skin: "Button.flat", text: "DSL", id: "test-dsl" GroupBox: value: 0, text: "Layout tests" LayoutBox: axis: y, spacing: 0, size: available diff --git a/testbed/testbed_application.cc b/testbed/testbed_application.cc index bbb427b..2dc54b8 100644 --- a/testbed/testbed_application.cc +++ b/testbed/testbed_application.cc @@ -734,6 +734,87 @@ class FullScreenWindow : public DemoWindow { } }; +class DslWindow : public DemoWindow { + public: + DslWindow() { + set_size(640, 480); + + using namespace el::dsl; + auto node = LayoutBoxNode() + .axis(Axis::kX) + .child(LabelNode("foo")) + .child(LabelNode("bar")); + LoadNodeTree(node); + + LoadNodeTree(BuildUI()); + } + + dsl::Node BuildSomeControl() { + using namespace el::dsl; + return LayoutBoxNode() + .axis(Axis::kX) + .child(LabelNode("foo")) + .child(LabelNode("bar")); + } + dsl::Node BuildUI() { + using namespace el::dsl; + auto rep_tree = LayoutBoxNode().axis(Axis::kX).child_list({ + LabelNode("item"), ButtonNode("button"), + }); + return LayoutBoxNode() + .id("foo") + .position(LayoutPosition::kLeftTop) + .axis(Axis::kY) + .children( + TabContainerNode() + .gravity(Gravity::kAll) + .axis(Axis::kX) + .tab(ButtonNode("Foo"), CloneNode(rep_tree)) + .tab(ButtonNode("Foo0"), CloneNode(rep_tree)) + .tab(ButtonNode("Foo1"), LabelNode("bar1")), + GroupBoxNode("controls:") + .content(LayoutBoxNode() + .child(BuildSomeControl()) + .child(BuildSomeControl())), + LabelNode("distribution: preferred").width(32_dp).font_size(3_mm), + LayoutBoxNode() + .distribution(LayoutDistribution::kPreferred) + .child(ButtonNode("tab 0")) + .child(TextBoxNode("foo").type(EditType::kPassword)) + .children(ToggleContainerNode() + .value(true) + .toggle_action(ToggleAction::kExpanded) + .child(TextBoxNode() + .placeholder("@search") + .gravity(Gravity::kLeftRight) + .type(EditType::kSearch)), + ButtonNode("fffoo").is_enabled(false)) + .child(ButtonNode("tab 0")) + .child(ButtonNode("tab 0")) + .clone(rep_tree) + .children(ButtonNode("tab 1"), ButtonNode("tab 2"), + ButtonNode("tab 3"), ButtonNode("tab 4")), + SpinBoxNode(4, 0, 40), ListBoxNode().items({ + {1, "a"}, {2, "b"}, + }), + ListBoxNode().item("a").item("id", "b").item(5, "c"), + DropDownButtonNode().value(1).items({ + Item("1", "a"), Item("2", "b"), + }), + DropDownButtonNode().value(1).items({ + {1, "a"}, {2, "b"}, + }), + DropDownButtonNode().value(1).items({ + "a", "b", "c", + })); + } + + bool OnEvent(const Event& ev) override { + // + return DemoWindow::OnEvent(ev); + } +}; + // == MainWindow ============================================================== MainWindow::MainWindow() { @@ -865,6 +946,9 @@ bool MainWindow::OnEvent(const Event& ev) { res_edit_win->Load("resource_edit_test.tb.txt"); parent()->AddChild(res_edit_win); return true; + } else if (ev.target->id() == TBIDC("test-dsl")) { + new DslWindow(); + return true; } else if (ev.type == EventType::kClick && ev.target->id() == TBIDC("debug settings")) { #ifdef EL_RUNTIME_DEBUG_INFO