Basic incomplete DSL-like thing for crafting UIs in C++.

This commit is contained in:
Ben Vanik 2015-07-10 23:08:48 -07:00
parent a03a308995
commit 3c803dd71c
32 changed files with 1014 additions and 4 deletions

View File

@ -20,6 +20,7 @@
<ClInclude Include="src\el\color.h" />
<ClInclude Include="src\el\config.h" />
<ClInclude Include="src\el\design\designer_window.h" />
<ClInclude Include="src\el\dsl.h" />
<ClInclude Include="src\el\element.h" />
<ClInclude Include="src\el\elemental_forms.h" />
<ClInclude Include="src\el\elements.h" />

View File

@ -301,6 +301,9 @@
<ClInclude Include="src\el\elements\split_container.h">
<Filter>src\el\elements</Filter>
</ClInclude>
<ClInclude Include="src\el\dsl.h">
<Filter>src\el</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="src">

134
src/el/dsl.h Normal file
View File

@ -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 <cassert>
#include <cstdint>
#include <string>
#include <vector>
#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<Node> children) {
for (auto& child : children) {
parse_node_->Add(child.parse_node_);
}
return *this;
}
protected:
Node(const char* name,
std::vector<std::pair<const char*, const char*>> properties = {},
std::vector<Node> 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_

View File

@ -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;

View File

@ -15,6 +15,7 @@
#include <string>
#include <vector>
#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 <typename T>
struct ElementNode : public Node {
using R = T;
ElementNode(const char* name,
std::vector<std::pair<const char*, const char*>> properties = {},
std::vector<Node> children = {})
: Node(name, std::move(properties), std::move(children)) {}
// TBID
R& id(Id value) {
value.set(this, "id");
return *reinterpret_cast<R*>(this);
}
// TBID
R& group_id(Id value) {
value.set(this, "group-id");
return *reinterpret_cast<R*>(this);
}
// TBID
R& skin(Id value) {
value.set(this, "skin");
return *reinterpret_cast<R*>(this);
}
// Number
R& data(int32_t value) {
set("data", value);
return *reinterpret_cast<R*>(this);
}
R& data(float value) {
set("data", value);
return *reinterpret_cast<R*>(this);
}
//
R& is_group_root(bool value) {
set("is-group-root", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& is_focusable(bool value) {
set("is-focusable", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& is_long_clickable(bool value) {
set("want-long-click", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& ignore_input(bool value) {
set("ignore-input", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& opacity(float value) {
set("opacity", value);
return *reinterpret_cast<R*>(this);
}
//
R& connection(std::string value) {
set("connection", value);
return *reinterpret_cast<R*>(this);
}
//
R& axis(Axis value) {
set("axis", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& gravity(Gravity value) {
set("gravity", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& visibility(Visibility value) {
set("visibility", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& state(ElementState value) {
set("state", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& is_enabled(bool value) {
set("state",
to_string(value ? ElementState::kNone : ElementState::kDisabled));
return *reinterpret_cast<R*>(this);
}
// Rect
R& rect(Rect value) {
set("rect", std::move(value));
return *reinterpret_cast<R*>(this);
}
//
R& width(Dimension value) {
set("lp>width", value);
return *reinterpret_cast<R*>(this);
}
//
R& min_width(Dimension value) {
set("lp>min-width", value);
return *reinterpret_cast<R*>(this);
}
//
R& max_width(Dimension value) {
set("lp>max-width", value);
return *reinterpret_cast<R*>(this);
}
//
R& preferred_width(Dimension value) {
set("lp>pref-width", value);
return *reinterpret_cast<R*>(this);
}
//
R& height(Dimension value) {
set("lp>height", value);
return *reinterpret_cast<R*>(this);
}
//
R& min_height(Dimension value) {
set("lp>min-height", value);
return *reinterpret_cast<R*>(this);
}
//
R& max_height(Dimension value) {
set("lp>max-height", value);
return *reinterpret_cast<R*>(this);
}
//
R& preferred_height(Dimension value) {
set("lp>pref-height", value);
return *reinterpret_cast<R*>(this);
}
//
R& tooltip(std::string value) {
set("tooltip", value);
return *reinterpret_cast<R*>(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<R*>(this);
}
//
R& font(const char* name, int32_t size_px) {
set("font>name", name);
set("font>size", size_px);
return *reinterpret_cast<R*>(this);
}
//
R& font_name(std::string value) {
set("font>name", value);
return *reinterpret_cast<R*>(this);
}
//
R& font_size(Dimension value) {
set("font>size", value);
return *reinterpret_cast<R*>(this);
}
R& clone(Node node) {
parse_node_->Add(CloneNode(node).parse_node());
return *reinterpret_cast<R*>(this);
}
R& child(Node node) {
parse_node_->Add(node.parse_node());
return *reinterpret_cast<R*>(this);
}
R& children() { return *reinterpret_cast<R*>(this); }
template <typename... C>
R& children(Node child_node, C... other_children) {
child(child_node);
children(other_children...);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENT_H_

View File

@ -26,6 +26,14 @@ class Box : public Element {
};
} // namespace elements
namespace dsl {
struct BoxNode : public ElementNode<BoxNode> {
using R = BoxNode;
BoxNode() : ElementNode("Box") {}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_BOX_H_

View File

@ -94,6 +94,28 @@ class Button : public Element, protected MessageHandler {
};
} // namespace elements
namespace dsl {
struct ButtonNode : public ElementNode<ButtonNode> {
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<R*>(this);
}
//
R& toggle_mode(bool value) {
set("toggle-mode", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_BUTTON_H_

View File

@ -29,6 +29,19 @@ class CheckBox : public parts::BaseRadioCheckBox {
};
} // namespace elements
namespace dsl {
struct CheckBoxNode : public ElementNode<CheckBoxNode> {
using R = CheckBoxNode;
CheckBoxNode() : ElementNode("CheckBox") {}
//
R& value(bool value) {
set("value", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_CHECK_BOX_H_

View File

@ -72,6 +72,19 @@ class DropDownButton : public Button, public ListItemObserver {
};
} // namespace elements
namespace dsl {
struct DropDownButtonNode : public ItemListElementNode<DropDownButtonNode> {
using R = DropDownButtonNode;
DropDownButtonNode() : ItemListElementNode("DropDownButton") {}
//
R& value(int32_t value) {
set("value", value);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_DROP_DOWN_BUTTON_H_

View File

@ -75,6 +75,29 @@ class GroupBox : public Element {
};
} // namespace elements
namespace dsl {
struct GroupBoxNode : public ElementNode<GroupBoxNode> {
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<R*>(this);
}
//
R& text(std::string value) {
set("text", value);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_GROUP_BOX_H_

View File

@ -31,6 +31,14 @@ class IconBox : public Element {
};
} // namespace elements
namespace dsl {
struct IconBoxNode : public ElementNode<IconBoxNode> {
using R = IconBoxNode;
IconBoxNode() : ElementNode("IconBox") {}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_ICON_BOX_H_

View File

@ -50,6 +50,23 @@ class ImageBox : public Element {
};
} // namespace elements
namespace dsl {
struct ImageBoxNode : public ElementNode<ImageBoxNode> {
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<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_IMAGE_BOX_H_

View File

@ -82,6 +82,28 @@ class Label : public Element {
};
} // namespace elements
namespace dsl {
struct LabelNode : public ElementNode<LabelNode> {
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<R*>(this);
}
//
R& text_align(TextAlign value) {
set("text-align", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_LABEL_H_

View File

@ -52,6 +52,24 @@ class LabelContainer : public Element {
};
} // namespace elements
namespace dsl {
struct LabelContainerNode : public ElementNode<LabelContainerNode> {
using R = LabelContainerNode;
LabelContainerNode(const char* text, std::vector<Node> children = {})
: ElementNode("LabelContainer", {}, std::move(children)) {
if (text) {
this->text(text);
}
}
//
R& text(std::string value) {
set("text", value);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_LABEL_CONTAINER_H_

View File

@ -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<LayoutBoxNode> {
using R = LayoutBoxNode;
LayoutBoxNode(std::vector<Node> children = {})
: ElementNode("LayoutBox", {}, std::move(children)) {}
//
R& spacing(Dimension value) {
set("spacing", value);
return *reinterpret_cast<R*>(this);
}
//
R& size(LayoutSize value) {
set("size", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& position(LayoutPosition value) {
set("position", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& overflow(LayoutOverflow value) {
set("overflow", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& distribution(LayoutDistribution value) {
set("distribution", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& distribution_position(LayoutDistributionPosition value) {
set("distribution-position", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_LAYOUT_BOX_H_

View File

@ -112,6 +112,19 @@ class ListBox : public Element, public ListItemObserver {
};
} // namespace elements
namespace dsl {
struct ListBoxNode : public ItemListElementNode<ListBoxNode> {
using R = ListBoxNode;
ListBoxNode() : ItemListElementNode("ListBox") {}
//
R& value(int32_t value) {
set("value", value);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_LIST_BOX_H_

View File

@ -56,6 +56,19 @@ class ProgressSpinner : public Element, protected MessageHandler {
};
} // namespace elements
namespace dsl {
struct ProgressSpinnerNode : public ElementNode<ProgressSpinnerNode> {
using R = ProgressSpinnerNode;
ProgressSpinnerNode() : ElementNode("ProgressSpinner") {}
//
R& value(int32_t value) {
set("value", value);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_PROGRESS_SPINNER_H_

View File

@ -30,6 +30,19 @@ class RadioButton : public parts::BaseRadioCheckBox {
};
} // namespace elements
namespace dsl {
struct RadioButtonNode : public ElementNode<RadioButtonNode> {
using R = RadioButtonNode;
RadioButtonNode() : ElementNode("RadioButton") {}
//
R& value(bool value) {
set("value", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_RADIO_BUTTON_H_

View File

@ -73,6 +73,24 @@ class ScrollBar : public Element {
};
} // namespace elements
namespace dsl {
struct ScrollBarNode : public ElementNode<ScrollBarNode> {
using R = ScrollBarNode;
ScrollBarNode() : ElementNode("ScrollBar") {}
//
R& value(float value) {
set("value", value);
return *reinterpret_cast<R*>(this);
}
//
R& axis(Axis value) {
set("axis", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_SCROLL_BAR_H_

View File

@ -83,6 +83,32 @@ class ScrollContainer : public Element {
};
} // namespace elements
namespace dsl {
using el::elements::ScrollMode;
struct ScrollContainerNode : public ElementNode<ScrollContainerNode> {
using R = ScrollContainerNode;
ScrollContainerNode(std::vector<Node> children = {})
: ElementNode("ScrollContainer", {}, std::move(children)) {}
//
R& adapt_content(bool value) {
set("adapt-content", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& adapt_to_content(bool value) {
set("adapt-to-content", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& scroll_mode(ScrollMode value) {
set("scroll-mode", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_SCROLL_CONTAINER_H_

View File

@ -26,6 +26,14 @@ class Separator : public Element {
};
} // namespace elements
namespace dsl {
struct SeparatorNode : public ElementNode<SeparatorNode> {
using R = SeparatorNode;
SeparatorNode() : ElementNode("Separator") {}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_SEPARATOR_H_

View File

@ -62,6 +62,34 @@ class Slider : public Element {
};
} // namespace elements
namespace dsl {
struct SliderNode : public ElementNode<SliderNode> {
using R = SliderNode;
SliderNode() : ElementNode("Slider") {}
//
R& value(float value) {
set("value", value);
return *reinterpret_cast<R*>(this);
}
//
R& min(float value) {
set("min", value);
return *reinterpret_cast<R*>(this);
}
//
R& max(float value) {
set("max", value);
return *reinterpret_cast<R*>(this);
}
//
R& axis(Axis value) {
set("axis", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_SLIDER_H_

View File

@ -59,6 +59,35 @@ class SpinBox : public Element {
};
} // namespace elements
namespace dsl {
struct SpinBoxNode : public ElementNode<SpinBoxNode> {
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<R*>(this);
}
//
R& min(int32_t value) {
set("min", value);
return *reinterpret_cast<R*>(this);
}
//
R& max(int32_t value) {
set("max", value);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_SPIN_BOX_H_

View File

@ -85,6 +85,46 @@ class SplitContainer : public Element {
};
} // namespace elements
namespace dsl {
using el::elements::FixedPane;
struct SplitContainerNode : public ElementNode<SplitContainerNode> {
using R = SplitContainerNode;
SplitContainerNode(std::vector<Node> children = {})
: ElementNode("SplitContainer", {}, std::move(children)) {}
//
R& value(int32_t value) {
set("value", value);
return *reinterpret_cast<R*>(this);
}
//
R& min(int32_t value) {
set("min", value);
return *reinterpret_cast<R*>(this);
}
//
R& max(int32_t value) {
set("max", value);
return *reinterpret_cast<R*>(this);
}
//
R& axis(Axis value) {
set("axis", el::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& fixed_pane(FixedPane value) {
set("fixed", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
SplitContainerNode& pane(Node content) {
parse_node_->Add(content.parse_node());
return *this;
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_SPLIT_CONTAINER_H_

View File

@ -75,6 +75,33 @@ class TabContainer : public Element {
};
} // namespace elements
namespace dsl {
struct TabContainerNode : public ElementNode<TabContainerNode> {
using R = TabContainerNode;
TabContainerNode(std::vector<Node> children = {})
: ElementNode("TabContainer", {}, std::move(children)) {}
//
R& value(int32_t value) {
set("value", value);
return *reinterpret_cast<R*>(this);
}
//
R& align(Align value) {
set("align", el::to_string(value));
return *reinterpret_cast<R*>(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_

View File

@ -218,6 +218,65 @@ class TextBox : public Element,
};
} // namespace elements
namespace dsl {
using el::elements::EditType;
struct TextBoxNode : public ElementNode<TextBoxNode> {
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<R*>(this);
}
//
R& is_multiline(bool value) {
set("multiline", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& is_styling(bool value) {
set("styling", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& is_read_only(bool value) {
set("readonly", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& is_wrapping(bool value) {
set("wrap", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& adapt_to_content(bool value) {
set("adapt-to-content", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& virtual_width(Dimension value) {
set("virtual-width", value);
return *reinterpret_cast<R*>(this);
}
//
R& placeholder(std::string value) {
set("placeholder", value);
return *reinterpret_cast<R*>(this);
}
//
R& type(EditType value) {
set("type", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_EDITFIELD_H

View File

@ -64,6 +64,31 @@ class ToggleContainer : public Element {
};
} // namespace elements
namespace dsl {
using el::elements::ToggleAction;
struct ToggleContainerNode : public ElementNode<ToggleContainerNode> {
using R = ToggleContainerNode;
ToggleContainerNode() : ElementNode("ToggleContainer") {}
//
R& value(bool value) {
set("value", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
//
R& toggle_action(ToggleAction value) {
set("toggle", el::elements::to_string(value));
return *reinterpret_cast<R*>(this);
}
//
R& invert(bool value) {
set("invert", value ? 1 : 0);
return *reinterpret_cast<R*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_ELEMENTS_TOGGLE_CONTAINER_H_

View File

@ -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<const uint8_t*>(data), length};
}
@ -53,7 +53,7 @@ std::unique_ptr<File> MemoryFileSystem::OpenRead(std::string filename) {
return nullptr;
}
return std::make_unique<MemoryFile>(it->first, it->second.first,
it->second.second);
it->second.second);
}
} // namespace io

View File

@ -13,6 +13,7 @@
#include <memory>
#include <vector>
#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<GenericStringItem> {
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 <typename T>
struct ItemListElementNode : public ElementNode<T> {
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<T*>(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<T*>(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<T*>(this);
}
T& items(std::initializer_list<std::string> 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<T*>(this);
}
T& items(std::initializer_list<std::pair<int32_t, std::string>> 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<T*>(this);
}
T& items(std::initializer_list<Item> 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<T*>(this);
}
};
} // namespace dsl
} // namespace el
#endif // EL_LIST_ITEM_H_

View File

@ -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<clazz>() == type_id \
? true \

View File

@ -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

View File

@ -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