diff --git a/engine/includes/editor/viewport/viewport.h b/engine/includes/editor/viewport/viewport.h index 2b346f15f..f703687d5 100644 --- a/engine/includes/editor/viewport/viewport.h +++ b/engine/includes/editor/viewport/viewport.h @@ -42,6 +42,7 @@ class ENGINE_EXPORT Viewport : public QWidget { void setLiveUpdate(bool update); void setGameView(bool enabled); + void setSceneView(bool enabled); void setGridEnabled(bool enabled); void setGizmoEnabled(bool enabled); diff --git a/engine/includes/resources/text.h b/engine/includes/resources/text.h index 5193c2229..aa0376cb5 100644 --- a/engine/includes/resources/text.h +++ b/engine/includes/resources/text.h @@ -7,7 +7,7 @@ class ENGINE_EXPORT Text : public Resource { A_REGISTER(Text, Resource, Resources) A_PROPERTIES( - A_PROPERTY(int, size, Text::size, Text::setSize) + A_PROPERTY(int, size, Text::size, Text::setSize) ) A_METHODS( diff --git a/engine/src/components/textrender.cpp b/engine/src/components/textrender.cpp index f08ebbdba..4494f4d58 100644 --- a/engine/src/components/textrender.cpp +++ b/engine/src/components/textrender.cpp @@ -38,7 +38,11 @@ TextRender::TextRender() : Material *material = Engine::loadResource(".embedded/DefaultFont.shader"); if(material) { - m_materials.push_back(material->createInstance()); + MaterialInstance *instance = material->createInstance(); + float fontWeight = 0.5f; + instance->setFloat("weight", &fontWeight); + + m_materials.push_back(instance); } } diff --git a/engine/src/editor/viewport/viewport.cpp b/engine/src/editor/viewport/viewport.cpp index 9d36d6103..b355edb4c 100644 --- a/engine/src/editor/viewport/viewport.cpp +++ b/engine/src/editor/viewport/viewport.cpp @@ -486,9 +486,7 @@ void Viewport::init() { PipelineTask *lastLayer = pipelineContext->renderTasks().back(); if(!m_gameView) { - for(auto it : pipelineContext->renderTasks()) { - it->setProperty("sceneView", true); - } + setSceneView(true); m_gridRender = new GridRender; m_gridRender->setInput(0, lastLayer->output(0)); @@ -667,6 +665,14 @@ void Viewport::setGameView(bool enabled) { m_gameView = enabled; } +void Viewport::setSceneView(bool enabled) { + PipelineContext *pipelineContext = m_renderSystem->pipelineContext(); + + for(auto it : pipelineContext->renderTasks()) { + it->setProperty("sceneView", enabled); + } +} + void Viewport::setGridEnabled(bool enabled) { m_gridRender->setEnabled(enabled); } diff --git a/engine/src/gizmos.cpp b/engine/src/gizmos.cpp index 5c516742b..6bc43a10b 100644 --- a/engine/src/gizmos.cpp +++ b/engine/src/gizmos.cpp @@ -113,7 +113,7 @@ void Gizmos::drawBox(const Vector3 ¢er, const Vector3 &size, const Vector4 & Vector3 min(center - size * 0.5f); Vector3 max(center + size * 0.5f); - Mesh mesh; + static Mesh mesh; mesh.setVertices({ Vector3(min.x, min.y, min.z), Vector3(max.x, min.y, min.z), @@ -144,7 +144,7 @@ void Gizmos::drawIcon(const Vector3 ¢er, const Vector2 &size, const string & Matrix4 model(center, Quaternion(), Vector3(size, size.x)); Matrix4 q = model * Matrix4(Camera::current()->transform()->quaternion().toMatrix()); - Mesh mesh; + static Mesh mesh; mesh.setIndices({0, 1, 2, 0, 2, 3}); mesh.setVertices({Vector3(-0.5f,-0.5f, 0.0f), Vector3(-0.5f, 0.5f, 0.0f), @@ -175,7 +175,7 @@ void Gizmos::drawIcon(const Vector3 ¢er, const Vector2 &size, const string & Draws a \a mesh with a specified \a color and \a transform. */ void Gizmos::drawMesh(Mesh &mesh, const Vector4 &color, const Matrix4 &transform) { - Mesh m; + static Mesh m; m.setVertices(mesh.vertices()); m.setIndices(mesh.indices()); m.setNormals(mesh.normals()); @@ -230,7 +230,7 @@ void Gizmos::drawSphere(const Vector3 ¢er, float radius, const Vector4 &colo } } - Mesh mesh; + static Mesh mesh; mesh.setVertices(vertices); mesh.setIndices(indices); mesh.setColors(Vector4Vector(mesh.vertices().size(), color)); @@ -243,7 +243,7 @@ void Gizmos::drawSphere(const Vector3 ¢er, float radius, const Vector4 &colo Parameter \a transform can be used to move, rotate and scale this arc. */ void Gizmos::drawSolidArc(const Vector3 ¢er, float radius, float start, float angle, const Vector4 &color, const Matrix4 &transform) { - Mesh mesh; + static Mesh mesh; mesh.setVertices(Mathf::pointsArc(Quaternion(), radius, start, angle, 180, true)); size_t size = mesh.vertices().size(); @@ -270,7 +270,7 @@ void Gizmos::drawSolidArc(const Vector3 ¢er, float radius, float start, floa Parameter \a transform can be used to move, rotate and scale this structure. */ void Gizmos::drawLines(const Vector3Vector &points, const IndexVector &indices, const Vector4 &color, const Matrix4 &transform) { - Mesh mesh; + static Mesh mesh; mesh.setVertices(points); mesh.setIndices(indices); mesh.setColors(Vector4Vector(points.size(), color)); @@ -283,7 +283,7 @@ void Gizmos::drawLines(const Vector3Vector &points, const IndexVector &indices, Parameter \a transform can be used to move, rotate and scale this arc. */ void Gizmos::drawArc(const Vector3 ¢er, float radius, float start, float angle, const Vector4 &color, const Matrix4 &transform) { - Mesh mesh; + static Mesh mesh; mesh.setVertices(Mathf::pointsArc(Quaternion(), radius, start, angle, 180)); size_t size = mesh.vertices().size(); @@ -318,7 +318,7 @@ void Gizmos::drawRectangle(const Vector3 ¢er, const Vector2 &size, const Vec Vector2 min(Vector2(center.x, center.y) - size * 0.5f); Vector2 max(Vector2(center.x, center.y) + size * 0.5f); - Mesh mesh; + static Mesh mesh; mesh.setVertices({ Vector3(min.x, min.y, center.z), Vector3(max.x, min.y, center.z), @@ -340,7 +340,7 @@ void Gizmos::drawWireBox(const Vector3 ¢er, const Vector3 &size, const Vecto Vector3 min(center - size * 0.5f); Vector3 max(center + size * 0.5f); - Mesh mesh; + static Mesh mesh; mesh.setVertices({ Vector3(min.x, min.y, min.z), Vector3(max.x, min.y, min.z), @@ -364,7 +364,7 @@ void Gizmos::drawWireBox(const Vector3 ¢er, const Vector3 &size, const Vecto Parameter \a transform can be used to move, rotate and scale this mesh. */ void Gizmos::drawWireMesh(Mesh &mesh, const Vector4 &color, const Matrix4 &transform) { - Mesh m; + static Mesh m; m.setVertices(mesh.vertices()); m.setIndices(mesh.indices()); m.setColors(Vector4Vector(m.vertices().size(), color)); diff --git a/engine/src/resources/text.cpp b/engine/src/resources/text.cpp index 739c98523..67f4e1ff2 100644 --- a/engine/src/resources/text.cpp +++ b/engine/src/resources/text.cpp @@ -2,8 +2,6 @@ #include -#include - namespace { const char *gData = "Data"; } @@ -57,7 +55,7 @@ void Text::setSize(uint32_t size) { m_data.resize(size); } /*! - Returns text content as a tring. + Returns text content as string. */ string Text::text() { return string(reinterpret_cast(m_data.data()), size()); diff --git a/modules/editor/grapheditor/editor/graph/graphcontroller.cpp b/modules/editor/grapheditor/editor/graph/graphcontroller.cpp index 6515ba48d..744a54bb8 100644 --- a/modules/editor/grapheditor/editor/graph/graphcontroller.cpp +++ b/modules/editor/grapheditor/editor/graph/graphcontroller.cpp @@ -87,7 +87,8 @@ void GraphController::update() { rect->setPosition(Vector3(m_rubberOrigin, 0.0f)); rect->setSize(Vector2()); } - } else if((Input::isMouseButtonUp(Input::MOUSE_LEFT) && m_view->isCreationLink()) || Input::isMouseButtonUp(Input::MOUSE_RIGHT)) { + } else if(Input::isMouseButtonUp(Input::MOUSE_RIGHT) || + (Input::isMouseButtonUp(Input::MOUSE_LEFT) && m_view->isCreationLink())) { m_view->showMenu(); } diff --git a/modules/editor/grapheditor/editor/graph/graphwidgets/nodewidget.cpp b/modules/editor/grapheditor/editor/graph/graphwidgets/nodewidget.cpp index cb381d6a1..f2b8ada84 100644 --- a/modules/editor/grapheditor/editor/graph/graphwidgets/nodewidget.cpp +++ b/modules/editor/grapheditor/editor/graph/graphwidgets/nodewidget.cpp @@ -57,8 +57,6 @@ void NodeWidget::setGraphNode(GraphNode *node) { if(m_title) { m_title->setColor(m_node->color()); if(m_node->isState()) { - rectTransform()->layout()->setMargins(0.0f, 10.0f, 10.0f, 0.0f); - RectTransform *rect = m_title->rectTransform(); rect->setMargin(Vector4(10.0f)); } @@ -194,7 +192,7 @@ void NodeWidget::update() { if(m_hovered != hover) { m_hovered = hover; - Vector4 color(m_frameColor); + Vector4 color(m_backgroundColor); if(m_hovered) { color.x = CLAMP(color.x + 0.25f, 0.0f, 1.0f); color.y = CLAMP(color.y + 0.25f, 0.0f, 1.0f); @@ -228,23 +226,22 @@ void NodeWidget::composeComponent() { Layout *layout = new Layout; layout->setSpacing(2.0f); - layout->setMargins(0.0f, 0.0f, 0.0f, corners().x); rectTransform()->setLayout(layout); Actor *title = Engine::composeActor(gFrame, "Title", actor()); if(title) { m_title = static_cast(title->component(gFrame)); if(m_title) { - RectTransform *rect = m_title->rectTransform(); - rect->setAnchors(Vector2(0.0f, 1.0f), Vector2(1.0f, 1.0f)); - rect->setSize(Vector2(0, row)); - rect->setPivot(Vector2(0.0f, 1.0f)); + RectTransform *titleRect = m_title->rectTransform(); + layout->addTransform(titleRect); - layout->addTransform(rect); + titleRect->setSize(Vector2(0, row)); + titleRect->setPivot(Vector2(0.0f, 1.0f)); + titleRect->setAnchors(Vector2(0.0f, 1.0f), Vector2(1.0f, 1.0f)); Vector4 corn(corners()); - corn.x = corn.y = 0.0f; - corn.w = corn.z; + corn.x = corn.y; + corn.w = corn.z = 0.0f; m_title->setCorners(corn); m_title->setBorderColor(Vector4()); @@ -261,13 +258,13 @@ void NodeWidget::composeComponent() { m_previewBtn->setSprite(Engine::loadResource(".embedded/ui.png")); m_previewBtn->setItem("Arrow"); - RectTransform *t = m_previewBtn->rectTransform(); - if(t) { - t->setSize(Vector2(16.0f, 8.0f)); - t->setMargin(Vector4(0.0f, 10.0f, 0.0f, 10.0f)); - t->setAnchors(Vector2(1.0f, 0.5f), Vector2(1.0f, 0.5f)); - t->setPivot(Vector2(1.0f, 0.5f)); - t->setRotation(Vector3(0.0f, 0.0f, 90.0f)); + RectTransform *previewRect = m_previewBtn->rectTransform(); + if(previewRect) { + previewRect->setSize(Vector2(16.0f, 8.0f)); + previewRect->setMargin(Vector4(0.0f, 10.0f, 0.0f, 10.0f)); + previewRect->setAnchors(Vector2(1.0f, 0.5f), Vector2(1.0f, 0.5f)); + previewRect->setPivot(Vector2(1.0f, 0.5f)); + previewRect->setRotation(Vector3(0.0f, 0.0f, 90.0f)); } } } @@ -276,28 +273,31 @@ void NodeWidget::composeComponent() { void NodeWidget::composePort(NodePort &port) { Actor *portActor = Engine::composeActor(gPortWidget, port.m_name.c_str(), actor()); if(portActor) { + PortWidget *portWidget = static_cast(portActor->component(gPortWidget)); if(portWidget) { - RectTransform *r = portWidget->rectTransform(); - r->setSize(Vector2(0, row)); + RectTransform *portRect = portWidget->rectTransform(); + portRect->setSize(Vector2(0, row)); portWidget->setNodePort(&port); + Layout *layout = rectTransform()->layout(); if(layout) { if(port.m_call) { if(m_callLayout) { if(port.m_out) { - m_callLayout->insertTransform(-1, r); + m_callLayout->insertTransform(-1, portRect); } else { - m_callLayout->insertTransform(0, r); + m_callLayout->insertTransform(0, portRect); } } else { m_callLayout = new Layout; m_callLayout->setDirection(Layout::Horizontal); - m_callLayout->addTransform(r); + m_callLayout->addTransform(portRect); layout->addLayout(m_callLayout); } } else { - layout->addTransform(r); + layout->addTransform(portRect); + portRect->setAnchors(Vector2(0.0f, 1.0f), Vector2(1.0f, 1.0f)); } } connect(portWidget, _SIGNAL(pressed(int)), this, _SIGNAL(portPressed(int))); diff --git a/modules/editor/grapheditor/editor/graph/graphwidgets/portwidget.cpp b/modules/editor/grapheditor/editor/graph/graphwidgets/portwidget.cpp index eec4623d1..8643bc107 100644 --- a/modules/editor/grapheditor/editor/graph/graphwidgets/portwidget.cpp +++ b/modules/editor/grapheditor/editor/graph/graphwidgets/portwidget.cpp @@ -53,6 +53,7 @@ void PortWidget::setNodePort(NodePort *port) { if(m_knob) { float knobSize = rect->size().y - 6.0f; RectTransform *knobRect = m_knob->rectTransform(); + knobRect->setBorder(0.0f); knobRect->setSize(Vector2(port->m_call ? knobSize + 4 : knobSize, knobSize)); knobRect->setAnchors(Vector2(m_port->m_out ? 1.0f : 0.0f, 0.5f), Vector2(m_port->m_out ? 1.0f : 0.0f, 0.5f)); @@ -62,7 +63,6 @@ void PortWidget::setNodePort(NodePort *port) { m_knob->setCorners(Vector4(knobSize * 0.5f)); } m_knob->setColor(m_port->m_color); - m_knob->setBorderWidth(0.0f); } // Create editor (only for inputs) diff --git a/modules/uikit/includes/components/abstractbutton.h b/modules/uikit/includes/components/abstractbutton.h index 67ab32173..cd593d09e 100644 --- a/modules/uikit/includes/components/abstractbutton.h +++ b/modules/uikit/includes/components/abstractbutton.h @@ -7,7 +7,7 @@ class Image; class Frame; class Label; -class ENGINE_EXPORT AbstractButton : public Widget { +class UIKIT_EXPORT AbstractButton : public Widget { A_REGISTER(AbstractButton, Widget, General) A_PROPERTIES( diff --git a/modules/uikit/includes/components/button.h b/modules/uikit/includes/components/button.h index 3e826447f..498d09209 100644 --- a/modules/uikit/includes/components/button.h +++ b/modules/uikit/includes/components/button.h @@ -3,7 +3,7 @@ #include "abstractbutton.h" -class ENGINE_EXPORT Button : public AbstractButton { +class UIKIT_EXPORT Button : public AbstractButton { A_REGISTER(Button, AbstractButton, Components/UI) A_NOPROPERTIES() diff --git a/modules/uikit/includes/components/floatinput.h b/modules/uikit/includes/components/floatinput.h index e7e7b8951..d57ee414a 100644 --- a/modules/uikit/includes/components/floatinput.h +++ b/modules/uikit/includes/components/floatinput.h @@ -6,7 +6,7 @@ class TextInput; class Button; -class ENGINE_EXPORT FloatInput : public Widget { +class UIKIT_EXPORT FloatInput : public Widget { A_REGISTER(FloatInput, Widget, Components/UI) A_NOPROPERTIES() diff --git a/modules/uikit/includes/components/frame.h b/modules/uikit/includes/components/frame.h index eb6329fe8..b66e056a4 100644 --- a/modules/uikit/includes/components/frame.h +++ b/modules/uikit/includes/components/frame.h @@ -6,12 +6,11 @@ class Mesh; class MaterialInstance; -class ENGINE_EXPORT Frame : public Widget { +class UIKIT_EXPORT Frame : public Widget { A_REGISTER(Frame, Widget, Components/UI) A_PROPERTIES( A_PROPERTY(Vector4, corners, Frame::corners, Frame::setCorners), - A_PROPERTY(float, borderWidth, Frame::borderWidth, Frame::setBorderWidth), A_PROPERTYEX(Vector4, color, Frame::color, Frame::setColor, "editor=Color"), A_PROPERTYEX(Vector4, topColor, Frame::topColor, Frame::setTopColor, "editor=Color"), A_PROPERTYEX(Vector4, rightColor, Frame::rightColor, Frame::setRightColor, "editor=Color"), @@ -27,9 +26,6 @@ class ENGINE_EXPORT Frame : public Widget { Vector4 corners() const; void setCorners(Vector4 corners); - float borderWidth() const; - void setBorderWidth(float width); - Vector4 color() const; void setColor(const Vector4 color); @@ -52,9 +48,11 @@ class ENGINE_EXPORT Frame : public Widget { void draw(CommandBuffer &buffer) override; + void applyStyle() override; + protected: - Vector4 m_cornerRadius; - Vector4 m_frameColor; + Vector4 m_borderRadius; + Vector4 m_backgroundColor; Vector4 m_topColor; Vector4 m_rightColor; Vector4 m_bottomColor; @@ -66,8 +64,6 @@ class ENGINE_EXPORT Frame : public Widget { MaterialInstance *m_material; - float m_borderWidth; - }; #endif // FRAME_H diff --git a/modules/uikit/includes/components/image.h b/modules/uikit/includes/components/image.h index 88fa76fa8..988e04aa7 100644 --- a/modules/uikit/includes/components/image.h +++ b/modules/uikit/includes/components/image.h @@ -10,7 +10,7 @@ class Mesh; class Texture; class MaterialInstance; -class ENGINE_EXPORT Image : public Widget { +class UIKIT_EXPORT Image : public Widget { A_REGISTER(Image, Widget, Components/UI) A_PROPERTIES( diff --git a/modules/uikit/includes/components/label.h b/modules/uikit/includes/components/label.h index 3e0ec69d7..a43c0de9d 100644 --- a/modules/uikit/includes/components/label.h +++ b/modules/uikit/includes/components/label.h @@ -9,7 +9,7 @@ class Mesh; class Material; class MaterialInstance; -class ENGINE_EXPORT Label : public Widget { +class UIKIT_EXPORT Label : public Widget { A_REGISTER(Label, Widget, Components/UI) A_PROPERTIES( @@ -54,6 +54,7 @@ class ENGINE_EXPORT Label : public Widget { private: void draw(CommandBuffer &buffer) override; + void applyStyle() override; void loadData(const VariantList &data) override; void loadUserData(const VariantMap &data) override; @@ -86,6 +87,8 @@ class ENGINE_EXPORT Label : public Widget { int m_alignment; + float m_fontWeight; + bool m_kerning; bool m_wrap; diff --git a/modules/uikit/includes/components/layout.h b/modules/uikit/includes/components/layout.h index 66e549a05..b050f4341 100644 --- a/modules/uikit/includes/components/layout.h +++ b/modules/uikit/includes/components/layout.h @@ -3,7 +3,7 @@ #include "widget.h" -class ENGINE_EXPORT Layout { +class UIKIT_EXPORT Layout { public: enum Direction { Vertical, @@ -34,8 +34,6 @@ class ENGINE_EXPORT Layout { float spacing() const; void setSpacing(float spacing); - void setMargins(float left, float top, float right, float bottom); - int direction() const; void setDirection(int direction); @@ -48,8 +46,6 @@ class ENGINE_EXPORT Layout { protected: list m_items; - Vector4 m_margins; - Vector2 m_position; Layout *m_parentLayout; diff --git a/modules/uikit/includes/components/menu.h b/modules/uikit/includes/components/menu.h index 80a0f4846..67d69831c 100644 --- a/modules/uikit/includes/components/menu.h +++ b/modules/uikit/includes/components/menu.h @@ -3,7 +3,7 @@ #include "frame.h" -class ENGINE_EXPORT Menu : public Frame { +class UIKIT_EXPORT Menu : public Frame { A_REGISTER(Menu, Frame, Components/UI) A_NOPROPERTIES() diff --git a/modules/uikit/includes/components/progressbar.h b/modules/uikit/includes/components/progressbar.h index f57cbaf19..028e31fa1 100644 --- a/modules/uikit/includes/components/progressbar.h +++ b/modules/uikit/includes/components/progressbar.h @@ -3,7 +3,7 @@ #include "frame.h" -class ENGINE_EXPORT ProgressBar : public Widget { +class UIKIT_EXPORT ProgressBar : public Widget { A_REGISTER(ProgressBar, Widget, Components/UI) A_PROPERTIES( diff --git a/modules/uikit/includes/components/recttransform.h b/modules/uikit/includes/components/recttransform.h index d87fcac5e..6deb5255e 100644 --- a/modules/uikit/includes/components/recttransform.h +++ b/modules/uikit/includes/components/recttransform.h @@ -2,11 +2,12 @@ #define RECTTRANSFORM_H #include +#include class Widget; class Layout; -class ENGINE_EXPORT RectTransform : public Transform { +class UIKIT_EXPORT RectTransform : public Transform { A_REGISTER(RectTransform, Transform, General) A_PROPERTIES( @@ -14,7 +15,9 @@ class ENGINE_EXPORT RectTransform : public Transform { A_PROPERTY(Vector2, minAnchors, RectTransform::minAnchors, RectTransform::setMinAnchors), A_PROPERTY(Vector2, maxAnchors, RectTransform::maxAnchors, RectTransform::setMaxAnchors), A_PROPERTY(Vector2, pivot, RectTransform::pivot, RectTransform::setPivot), - A_PROPERTY(Vector4, margin, RectTransform::margin, RectTransform::setMargin) + A_PROPERTY(Vector4, margin, RectTransform::margin, RectTransform::setMargin), + A_PROPERTY(Vector4, boder, RectTransform::border, RectTransform::setBorder), + A_PROPERTY(Vector4, padding, RectTransform::padding, RectTransform::setPadding) ) A_NOMETHODS() @@ -38,6 +41,12 @@ class ENGINE_EXPORT RectTransform : public Transform { Vector4 margin() const; void setMargin(const Vector4 margin); + Vector4 border() const; + void setBorder(const Vector4 border); + + Vector4 padding() const; + void setPadding(const Vector4 padding); + bool isHovered(float x, float y) const; void subscribe(Widget *widget); @@ -46,8 +55,6 @@ class ENGINE_EXPORT RectTransform : public Transform { Layout *layout() const; void setLayout(Layout *layout); - Matrix4 worldTransform() const override; - AABBox bound() const; private: @@ -66,6 +73,8 @@ class ENGINE_EXPORT RectTransform : public Transform { list m_subscribers; Vector4 m_margin; + Vector4 m_border; + Vector4 m_padding; Vector2 m_bottomLeft; Vector2 m_topRight; diff --git a/modules/uikit/includes/components/switch.h b/modules/uikit/includes/components/switch.h index bb655d652..a3e2c5a7a 100644 --- a/modules/uikit/includes/components/switch.h +++ b/modules/uikit/includes/components/switch.h @@ -3,7 +3,7 @@ #include "abstractbutton.h" -class ENGINE_EXPORT Switch : public AbstractButton { +class UIKIT_EXPORT Switch : public AbstractButton { A_REGISTER(Switch, AbstractButton, Components/UI) A_PROPERTIES( diff --git a/modules/uikit/includes/components/textinput.h b/modules/uikit/includes/components/textinput.h index 4c16b7180..eb11e5474 100644 --- a/modules/uikit/includes/components/textinput.h +++ b/modules/uikit/includes/components/textinput.h @@ -6,7 +6,7 @@ class Label; class Frame; -class ENGINE_EXPORT TextInput : public Widget { +class UIKIT_EXPORT TextInput : public Widget { A_REGISTER(TextInput, Widget, Components/UI) A_PROPERTIES( diff --git a/modules/uikit/includes/components/toolbutton.h b/modules/uikit/includes/components/toolbutton.h index 7c07305fa..2c8e962af 100644 --- a/modules/uikit/includes/components/toolbutton.h +++ b/modules/uikit/includes/components/toolbutton.h @@ -5,7 +5,7 @@ class Menu; -class ENGINE_EXPORT ToolButton : public AbstractButton { +class UIKIT_EXPORT ToolButton : public AbstractButton { A_REGISTER(ToolButton, AbstractButton, Components/UI) A_NOPROPERTIES() diff --git a/modules/uikit/includes/components/uiloader.h b/modules/uikit/includes/components/uiloader.h new file mode 100644 index 000000000..90fccc2dd --- /dev/null +++ b/modules/uikit/includes/components/uiloader.h @@ -0,0 +1,45 @@ +#ifndef UILOADER_H +#define UILOADER_H + +#include "widget.h" + +#include "uidocument.h" +#include "stylesheet.h" + +class UIKIT_EXPORT UiLoader : public Widget { + A_REGISTER(UiLoader, Widget, Components/UI) + + A_PROPERTIES( + A_PROPERTYEX(UiDocument *, document, UiLoader::styleSheet, UiLoader::setStyleSheet, "editor=Asset"), + A_PROPERTYEX(StyleSheet *, styleSheet, UiLoader::styleSheet, UiLoader::setStyleSheet, "editor=Asset") + ) + A_METHODS( + A_METHOD(void, UiLoader::loadFromBuffer) + ) + +public: + UiLoader(); + + UiDocument *document() const; + void setUiDocument(UiDocument *document); + + StyleSheet *styleSheet() const; + void setStyleSheet(StyleSheet *style); + + void loadFromBuffer(const string &buffer); + +private: + void resolveStyleSheet(Widget *widget); + + void cleanHierarchy(Widget *widget); + +private: + string m_documentStyle; + + UiDocument *m_document; + + StyleSheet *m_styleSheet; + +}; + +#endif // UILOADER_H diff --git a/modules/uikit/includes/components/widget.h b/modules/uikit/includes/components/widget.h index 27a592724..366137378 100644 --- a/modules/uikit/includes/components/widget.h +++ b/modules/uikit/includes/components/widget.h @@ -1,12 +1,14 @@ #ifndef WIDGET_H #define WIDGET_H -#include "nativebehaviour.h" +#include +#include class RectTransform; class CommandBuffer; +class StyleSheet; -class ENGINE_EXPORT Widget : public NativeBehaviour { +class UIKIT_EXPORT Widget : public NativeBehaviour { A_REGISTER(Widget, NativeBehaviour, Components/UI) A_NOPROPERTIES() @@ -23,7 +25,11 @@ class ENGINE_EXPORT Widget : public NativeBehaviour { string style() const; void setStyle(const string style); + const list &classes() const; + void addClass(const string &name); + Widget *parentWidget(); + list childWidgets() const; RectTransform *rectTransform() const; @@ -43,6 +49,8 @@ class ENGINE_EXPORT Widget : public NativeBehaviour { virtual void boundChanged(const Vector2 &size); + virtual void applyStyle(); + void setRectTransform(RectTransform *transform); void update() override; @@ -55,19 +63,31 @@ class ENGINE_EXPORT Widget : public NativeBehaviour { void drawGizmosSelected() override; + float styleLength(const string &key, float value, bool &pixels); + Vector4 styleBlockLength(const string &property, const Vector4 &value, bool &pixels); + static void setFocusWidget(Widget *widget); private: void setSystem(ObjectSystem *system) override; + void addStyleRules(const map &rules, uint32_t weight); + +protected: + map> m_styleRules; + private: friend class GuiLayer; friend class RectTransform; friend class UiLoader; + friend class StyleSheet; + + list m_classes; string m_style; Widget *m_parent; + RectTransform *m_transform; static Widget *m_focusWidget; diff --git a/modules/uikit/includes/resources/stylesheet.h b/modules/uikit/includes/resources/stylesheet.h new file mode 100644 index 000000000..bab313cf9 --- /dev/null +++ b/modules/uikit/includes/resources/stylesheet.h @@ -0,0 +1,29 @@ +#ifndef STYLESHEET_H +#define STYLESHEET_H + +#include +#include + +class Widget; + +class UIKIT_EXPORT StyleSheet : public Resource { + A_REGISTER(StyleSheet, Resource, Resources) + +public: + StyleSheet(); + + bool addRawData(const string &data); + + void resolve(Widget *widget); + + static void resolveInline(Widget *widget); + + static Vector4 toColor(const string &value); + static float toLength(const string &value, bool &pixels); + +private: + void *m_parser; + +}; + +#endif // STYLESHEET_H diff --git a/modules/uikit/includes/resources/uidocument.h b/modules/uikit/includes/resources/uidocument.h new file mode 100644 index 000000000..2638ad6bf --- /dev/null +++ b/modules/uikit/includes/resources/uidocument.h @@ -0,0 +1,25 @@ +#ifndef UIDOCUMENT_H +#define UIDOCUMENT_H + +#include +#include + +class UIKIT_EXPORT UiDocument: public Resource { + A_REGISTER(UiDocument, Resource, Resources) + +public: + UiDocument(); + + string data() const; + void setData(const string &data); + +private: + void loadUserData(const VariantMap &data) override; + VariantMap saveUserData() const override; + +private: + string m_data; + +}; + +#endif // UIDOCUMENT_H diff --git a/modules/uikit/includes/uikit.h b/modules/uikit/includes/uikit.h index 34cef04b1..22a86cc88 100644 --- a/modules/uikit/includes/uikit.h +++ b/modules/uikit/includes/uikit.h @@ -3,6 +3,16 @@ #include +#if defined(SHARED_DEFINE) && defined(_WIN32) + #ifdef UIKIT_LIBRARY + #define UIKIT_EXPORT __declspec(dllexport) + #else + #define UIKIT_EXPORT __declspec(dllimport) + #endif +#else + #define UIKIT_EXPORT +#endif + class UiSystem; class UiKit : public Module { diff --git a/modules/uikit/includes/utils/attributeselector.h b/modules/uikit/includes/utils/attributeselector.h new file mode 100644 index 000000000..4f9e10e79 --- /dev/null +++ b/modules/uikit/includes/utils/attributeselector.h @@ -0,0 +1,33 @@ +#ifndef ATTRIBUTESELECTOR_H +#define ATTRIBUTESELECTOR_H + +#include "selector.h" + +class AttributeSelector: public Selector { +public: + enum AttributeFilterRule { + Prefix, + Suffix, + Include, + Equal, + Substring, + DashMatch, + NoRule + }; + +public: + AttributeSelector(const std::string &key, const std::string &value, AttributeFilterRule rule); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + +private: + std::string m_key; + std::string m_value; + + AttributeFilterRule m_filterRule; + +}; + +#endif /* ATTRIBUTESELECTOR_H */ diff --git a/modules/uikit/includes/utils/classselector.h b/modules/uikit/includes/utils/classselector.h new file mode 100644 index 000000000..f6238cbad --- /dev/null +++ b/modules/uikit/includes/utils/classselector.h @@ -0,0 +1,19 @@ +#ifndef CLASSSELECTOR_H +#define CLASSSELECTOR_H + +#include "selector.h" + +class ClassSelector: public Selector { +public: + ClassSelector(const std::string& cls); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + +private: + std::string m_class; + +}; + +#endif /* CLASSSELECTOR_H */ diff --git a/modules/uikit/includes/utils/combineselector.h b/modules/uikit/includes/utils/combineselector.h new file mode 100644 index 000000000..2a5a406e4 --- /dev/null +++ b/modules/uikit/includes/utils/combineselector.h @@ -0,0 +1,49 @@ +#ifndef COMBINESELECTOR_H +#define COMBINESELECTOR_H + +#include "selector.h" + +#include +#include + +class Widget; + +class CombineSelector: public Selector { +public: + enum CombineType { + InstanceSibling, + NormalSibling, + InstanceInherical, + NormalInherical, + NoCombine + }; + +public: + CombineSelector(); + ~CombineSelector(); + + void initialInstanceSiblingList(Selector *head, Selector *sibling); + void initialNormalSiblingList(Selector *head, Selector *sibling); + void initialInstanceInhericalList(Selector *root, Selector *child); + void initialNormalInhericalList(Selector *root, Selector *child); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + + Selector *before(); + Selector *after(); + + std::vector matchingWidgets; + +private: + std::list m_instanceSiblingList; + std::list m_normalSiblingList; + std::list m_instanceInhericalList; + std::list m_normalInhericalList; + + CombineType m_combineType; + +}; + +#endif /* COMBINESELECTOR_H */ diff --git a/modules/uikit/includes/utils/csslex.h b/modules/uikit/includes/utils/csslex.h new file mode 100644 index 000000000..ba363563d --- /dev/null +++ b/modules/uikit/includes/utils/csslex.h @@ -0,0 +1,47 @@ +#ifndef CSSLEX_H +#define CSSLEX_H + +#include +#include + +#include "csslexstatus.h" + +class Lex { +public: + struct CSSToken { + CSSTokenType type; + std::string data; + }; + +public: + Lex(); + ~Lex(); + CSSToken *token(); + void cleanResource(); + void setBufferString(const std::string &bufferString); + +private: + CSSToken *identToken(); + CSSToken *numberToken(); + CSSToken *textToken(char stringType); + + bool isDigitalCharacter(char); + bool isLetter(char); + bool isHexCharacter(char); + bool isWs(char); + + std::string createData(size_t start, size_t end); + +private: + std::set m_tokenCache; + std::string m_fileName; + + char *m_buffer; + + size_t m_bufferSize; + size_t m_firstPos; + size_t m_forwardPos; + +}; + +#endif /* CSSLEX_H */ diff --git a/modules/uikit/includes/utils/csslexstatus.h b/modules/uikit/includes/utils/csslexstatus.h new file mode 100644 index 000000000..7dbd05b08 --- /dev/null +++ b/modules/uikit/includes/utils/csslexstatus.h @@ -0,0 +1,113 @@ +// +// CSSLexStatus.h +// DDCSSParser +// +// Created by 1m0nster on 2018/8/1. +// Copyright © 2018 1m0nster. All rights reserved. +// + +#ifndef CSSLexStatus_h +#define CSSLexStatus_h + +enum CSSDFAStatus { + Start, + iDentStart, + iDent, + NMStart, + NMChar, + EscapeStartInNMStart, + EscapeStartInNMChar, + EscapeStartInHash, + EscapeStartInATKeyword, + HashStart, + Hash, + Ws, + AtKeyWordStart, + AtKeyWord, + include, + dot, + end, + blockStart, + blockEnd, + comma, + plus, + greater, + tidle, + dashMatch, + prefixMatch, + suffixMatch, + subStringMatch, + includes, + star, + colon, + semicolon, + leftSqureBracket, + rightSqureBracket, + equal, + string1Start, + string1End, + string2Start, + string2End, + annotationStart, + annotationEnd, + function, + numberStart, + num, + rightBracket, + minus, + LexError +}; +enum CSSTokenType { + INCLUDES, + DASHMATCH, + PREFIXMATCH, + SUFFIXMATCH, + SUBSTRINGMATCH, + IDENT, + STRING, + FUNCTION, + NUMBER, + HASH, + PLUS, + GREATER, + COMMA, + TIDLE, + ATKEYWORD, + STAR, + PERCENTAGE, + DIMENSION, + CDO, + CDC, + WS, + DOT, + ERROR, + BLOCKSTART, + BLOCKEND, + COLON, + LEFTSQUREBRACKET, + RIGHTSQUREBRACKET, + EQUAL, + ANNOTATION, + SYNTAXEND, + RIGHTBRACKET, + MINUS, + END +}; +#define IDENT_START_SIGN '-' +#define UNDER_LINE_SIGN '_' +#define BACK_SPLASH '\\' +#define HASH_SIGN '#' +#define KEYWORD_SIGN '@' +#define BLOCK_START_SIGN '{' +#define BLOCK_END_SIGN '}' +#define EQUAL_SIGN '=' +#define COMMA_SIGN ',' +#define PLUS_SIGN '+' +#define TIDLE_SIGN '~' +#define GREATER_SIGN '>' +#define COLON_SIGN ':' +#define LEFT_SQURE_BRACKET '[' +#define RIGHT_SQURE_BRACKET ']' + + +#endif /* CSSLexStatus_h */ diff --git a/modules/uikit/includes/utils/cssparser.h b/modules/uikit/includes/utils/cssparser.h new file mode 100644 index 000000000..dedce140b --- /dev/null +++ b/modules/uikit/includes/utils/cssparser.h @@ -0,0 +1,75 @@ +#ifndef CSSPARSER_H +#define CSSPARSER_H + +#include "csslex.h" +#include "cssparserstatus.h" +#include "keyworditem.h" +#include "pseudoselector.h" +#include "signselector.h" + +#include +#include +#include + +class Lex; +class Selector; + +class CSSParser { +public: + struct ASTNode { + Selector *head; + ASTNode *left; + ASTNode *right; + ASTNode() { + head = NULL; + left = NULL; + right = NULL; + } + }; +public: + CSSParser(); + ~CSSParser(); + + bool parseByString (const std::string& cssString); + + std::vector &selectors(); + std::vector &keywords(); + + void cleanRes(); + +private: + typedef void(*treeTranverseAction)(ASTNode *); + typedef CSSParser::ASTNode *(*treeTranverseWithUserDataAction)(std::stack *stack); + friend CSSParser::ASTNode *TreeTranverseCreateExpressionAction(std::stack *); + + static void initialASTNode(ASTNode *target, Selector *head, ASTNode *left, ASTNode *right); + static void pushOperatedElement(std::stack &, Selector *head); + + bool parse(); + void prepareByString(const std::string &cssString); + void clean(); + + bool startSelector(CSSTokenType); + bool tokenHasInfo(CSSTokenType); + bool topHaveSign(std::stack &); + Selector* getSelector(Lex::CSSToken* token); + PseudoSelector::Parameter *getFunctionParamenter(); + std::list createATS(std::stack&); + void pushSign(std::stack &, SignSelector::SignType); + void buildReversePolishNotation(std::stack& operatorStack, std::stack& operandStack); + + void RMLtranverseAST(ASTNode *root, treeTranverseAction action); + void LRMtranverseAST(ASTNode *root, treeTranverseAction action); + void LMRtranverseAST(ASTNode *root, treeTranverseAction action); + void MLRtranverseAST(ASTNode *root, treeTranverseWithUserDataAction action, void *userData); + +private: + Lex* m_lexer; + CSSParserStatus m_status; + std::string m_hostCssFile; + std::vector m_selectors; + std::vector m_keywords; + std::list m_signSelecors; +}; + +#endif /* CSSPARSER_H */ diff --git a/modules/uikit/includes/utils/cssparserstatus.h b/modules/uikit/includes/utils/cssparserstatus.h new file mode 100644 index 000000000..0a365ef4c --- /dev/null +++ b/modules/uikit/includes/utils/cssparserstatus.h @@ -0,0 +1,37 @@ +// +// CSSParserStatus.h +// DDCSSParser +// +// Created by 1m0nster on 2018/8/7. +// Copyright © 2018 1m0nster. All rights reserved. +// + +#ifndef CSSParserStatus_h +#define CSSParserStatus_h + +//enum CSSParserStatus { +// START, +// KEYWORD, +// TYPESELECTOR, +// UNIVERSIALSELECTOR, +// IDSELECTOR, +// ATTRIBUTSELECTOR, +// CLASSSELECTOR, +// SELECTORSEQUENCE, +// PSEUDOSELECOT, +// SELECTOR, +// SELECTORGROUP, +// RULESTART, +// RULEEND, +// PARSEERROR +//}; + +enum CSSParserStatus { + START, + INSELECTOR, + STARTBLOCK, + INATKEYWORD +}; +extern const int HTMLTAGMAXSIZE; +extern const char* HTMLTagNames[]; +#endif /* CSSParserStatus_h */ diff --git a/modules/uikit/includes/utils/idselector.h b/modules/uikit/includes/utils/idselector.h new file mode 100644 index 000000000..f133a4e41 --- /dev/null +++ b/modules/uikit/includes/utils/idselector.h @@ -0,0 +1,19 @@ +#ifndef IDSELECTOR_H +#define IDSELECTOR_H + +#include "selector.h" + +class IdSelector: public Selector { +public: + IdSelector(const std::string &id); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + +private: + std::string m_id; + +}; + +#endif /* IDSELECTOR_H */ diff --git a/modules/uikit/includes/utils/keyworditem.h b/modules/uikit/includes/utils/keyworditem.h new file mode 100644 index 000000000..bc4eac578 --- /dev/null +++ b/modules/uikit/includes/utils/keyworditem.h @@ -0,0 +1,28 @@ +#ifndef KEYWORDITEM_H +#define KEYWORDITEM_H + +#include + +class KeywordItem { +public: + KeywordItem(const std::string &name); + + void setData(const std::string &data) { + m_data = data; + }; + + std::string &data() { + return m_data; + } + + std::string name() { + return m_name; + } + +private: + std::string m_name; + std::string m_data; + +}; + +#endif /* KEYWORDITEM_H */ diff --git a/modules/uikit/includes/utils/pseudoselector.h b/modules/uikit/includes/utils/pseudoselector.h new file mode 100644 index 000000000..01da3ea1e --- /dev/null +++ b/modules/uikit/includes/utils/pseudoselector.h @@ -0,0 +1,58 @@ +#ifndef PSEUDOSELECTOR_H +#define PSEUDOSELECTOR_H + +#include "selector.h" + +class PseudoSelector: public Selector { +public: + enum ParameterType { + STRING, + NUMBER, + POLYNOMIAL, + IDENT, + NONE + }; + + struct Parameter { + struct polynomial { + int coefficient; + int constant; + int sign; + + polynomial() { + coefficient = 0; + constant = 0; + sign = 0; + } + } polynomial; + + std::string pString; + int pNumber; + ParameterType type; + + Parameter() { + type = ParameterType::NONE; + pNumber = 0; + pString = ""; + } + }; + +public: + PseudoSelector(const std::string& data); + ~PseudoSelector(); + + bool isMeet(Widget *) override; + bool isBaseSelector() const override; + int weight() override; + + Parameter* parameter(); + void setParameter(Parameter *); + +private: + std::string m_data; + + Parameter *m_parameter; + +}; + +#endif /* PSEUDOSELECTOR_H */ diff --git a/modules/uikit/includes/utils/selector.h b/modules/uikit/includes/utils/selector.h new file mode 100644 index 000000000..1e153c310 --- /dev/null +++ b/modules/uikit/includes/utils/selector.h @@ -0,0 +1,55 @@ +#ifndef SELECTOR_H +#define SELECTOR_H + +#include +#include + +class Widget; + +class Selector { +public: + enum SelectorType { + TypeSelector, + IDSelector, + ClassSelector, + UniversalSelector, + AttributeSelector, + PseudoSelector, + SelectorSequence, + CombineSelector, + SelectorGroup, + SignSelector + }; + +public: + Selector(); + virtual ~Selector(); + + inline const std::string &ruleData() const; + void setRuleData(const std::string &data); + + std::map &ruleDataMap(); + + SelectorType type(); + + void setHostCSSFilePath(const std::string& path); + + const std::string &hostCSSFilePath() const; + + virtual bool isMeet(Widget *) = 0; + virtual bool isBaseSelector() const = 0; + virtual int weight() = 0; + +protected: + std::string m_hostCSSFilePath; + std::string m_ruleData; + SelectorType m_selectorType; + + std::map m_ruleDataMap; + +private: + friend class CombineSelector; + +}; + +#endif /* SELECTOR_H */ diff --git a/modules/uikit/includes/utils/selectorgroup.h b/modules/uikit/includes/utils/selectorgroup.h new file mode 100644 index 000000000..045fc0a4c --- /dev/null +++ b/modules/uikit/includes/utils/selectorgroup.h @@ -0,0 +1,26 @@ +#ifndef SELECTORGROUP_H +#define SELECTORGROUP_H + +#include "selector.h" + +#include + +class GroupSelector: public Selector { +public: + GroupSelector(); + ~GroupSelector(); + + void addSelector(Selector *); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + +private: + std::vector m_selectors; + + unsigned int targetSelector = 0; + +}; + +#endif /* SELECTORGROUP_H */ diff --git a/modules/uikit/includes/utils/selectorsequence.h b/modules/uikit/includes/utils/selectorsequence.h new file mode 100644 index 000000000..62d36ee62 --- /dev/null +++ b/modules/uikit/includes/utils/selectorsequence.h @@ -0,0 +1,24 @@ +#ifndef SELECTORSEQUENCE_H +#define SELECTORSEQUENCE_H + +#include "selector.h" + +#include + +class SequenceSelector: public Selector { +public: + SequenceSelector(); + ~SequenceSelector(); + + void appendSelector(Selector *); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + +private: + std::list m_selectors; + +}; + +#endif /* SELECTORSEQUENCE_H */ diff --git a/modules/uikit/includes/utils/signselector.h b/modules/uikit/includes/utils/signselector.h new file mode 100644 index 000000000..74e221926 --- /dev/null +++ b/modules/uikit/includes/utils/signselector.h @@ -0,0 +1,32 @@ +#ifndef SIGNSELECTOR_H +#define SIGNSELECTOR_H + +#include "selector.h" + +class SignSelector: public Selector { +public: + enum SignType { + NormalInherit, + Plus, + Greater, + Tidle, + Concat, + Comma, + }; + + SignSelector(SignType type); + + inline SignType signType(); + + bool operator >(SignSelector *); + + bool isMeet(Widget *) override; + bool isBaseSelector() const override; + int weight() override; + +private: + SignType m_signType; + +}; + +#endif /* SIGNSELECTOR_H */ diff --git a/modules/uikit/includes/utils/stringutil.h b/modules/uikit/includes/utils/stringutil.h new file mode 100644 index 000000000..0c383c69f --- /dev/null +++ b/modules/uikit/includes/utils/stringutil.h @@ -0,0 +1,39 @@ +#ifndef STRINGUTIL_H +#define STRINGUTIL_H + +#include +#include + +// string switch +constexpr unsigned long long strhash(const char* str, int h = 0) +{ + return !str[h] ? 5381 : (strhash(str, h + 1) * 33) ^ str[h]; +} + +class StringUtil { +public: + StringUtil(); + virtual ~StringUtil(); + + static const char* ws; + + static std::string longlong2str(long long in); + static std::string int2str(int in); + static int str2int(const std::string &in); + static bool startWith(std::string &str,std::string &strWith); + static bool endWith(std::string &str,std::string &strWith); + static std::string tolower(const std::string &str); + static std::string toupper(const std::string &str); + static bool contains(const std::string str1,const std::string str2); + static std::string tostring(long long in); + static void replace(std::string &srcStr, const std::string &findStr, const std::string &replaceStr ); + static std::string& rtrim(std::string& s, const char* t = ws); + static std::string& ltrim(std::string& s, const char* t = ws); + static std::string& trim(std::string& s, const char* t = ws); + static std::vector split(const std::string& , char c); + static std::vector splitButSkipBrackets(const std::string& s, char separator); + static std::string join(std::vector& v, char separator); + static std::string deletechar(const std::string&, char); +}; + +#endif /* STRINGUTIL_H */ diff --git a/modules/uikit/includes/utils/typeselector.h b/modules/uikit/includes/utils/typeselector.h new file mode 100644 index 000000000..a99d354fe --- /dev/null +++ b/modules/uikit/includes/utils/typeselector.h @@ -0,0 +1,21 @@ +#ifndef TYPESELECTOR_H +#define TYPESELECTOR_H + +#include "selector.h" + +class TypeSelector: public Selector { +public: + TypeSelector(const std::string &typeName); + + inline std::string tagName(); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + +private: + std::string m_typeName; + +}; + +#endif /* TYPESELECTOR_H */ diff --git a/modules/uikit/includes/utils/universalselector.h b/modules/uikit/includes/utils/universalselector.h new file mode 100644 index 000000000..5dddc793a --- /dev/null +++ b/modules/uikit/includes/utils/universalselector.h @@ -0,0 +1,16 @@ +#ifndef UNIVERSALSELECTOR_H +#define UNIVERSALSELECTOR_H + +#include "selector.h" + +class UniversalSelector: public Selector { +public: + UniversalSelector(); + + bool isMeet(Widget *widget) override; + bool isBaseSelector() const override; + int weight() override; + +}; + +#endif /* UNIVERSALSELECTOR_H */ diff --git a/modules/uikit/src/components/frame.cpp b/modules/uikit/src/components/frame.cpp index 26cbce74e..d7e50c353 100644 --- a/modules/uikit/src/components/frame.cpp +++ b/modules/uikit/src/components/frame.cpp @@ -1,6 +1,7 @@ #include "components/frame.h" #include "components/recttransform.h" +#include "utils/stringutil.h" #include #include @@ -8,13 +9,14 @@ #include #include #include +#include #include namespace { - const char *gFrameColor = "frameColor"; + const char *gBackgroundColor = "backgroundColor"; const char *gBorderWidth = "borderWidth"; - const char *gCornerRadius = "cornerRadius"; + const char *gBorderRadius = "borderRadius"; const char *gTopColor = "topColor"; const char *gRightColor = "rightColor"; @@ -31,31 +33,27 @@ namespace { Frame::Frame() : Widget(), - m_cornerRadius(0.0f), - m_frameColor(1.0f, 1.0f, 1.0f, 0.5f), + m_borderRadius(0.0f), + m_backgroundColor(1.0f, 1.0f, 1.0f, 0.5f), m_topColor(0.8f), m_rightColor(0.8f), m_bottomColor(0.8f), m_leftColor(0.8f), m_mesh(Engine::objectCreate("")), - m_material(nullptr), - m_borderWidth(1.0f) { + m_material(nullptr) { Material *material = Engine::loadResource(".embedded/Frame.shader"); if(material) { m_material = material->createInstance(); - Vector4 normCorners(m_cornerRadius / m_meshSize.y); - m_material->setVector4(gCornerRadius, &normCorners); - - float width = m_borderWidth / m_meshSize.y; - m_material->setFloat(gBorderWidth, &width); + Vector4 normCorners(m_borderRadius / m_meshSize.y); + m_material->setVector4(gBorderRadius, &normCorners); m_material->setVector4(gTopColor, &m_topColor); m_material->setVector4(gRightColor, &m_rightColor); m_material->setVector4(gBottomColor, &m_bottomColor); m_material->setVector4(gLeftColor, &m_leftColor); - m_material->setVector4(gFrameColor, &m_frameColor); + m_material->setVector4(gBackgroundColor, &m_backgroundColor); } } /*! @@ -77,50 +75,85 @@ void Frame::draw(CommandBuffer &buffer) { } } /*! - Returns the corners radiuses of the frame. -*/ -Vector4 Frame::corners() const { - return m_cornerRadius; -} -/*! - Sets the \a corners radiuses of the frame. + \internal + Applies style settings assigned to widget. */ -void Frame::setCorners(Vector4 corners) { - m_cornerRadius = corners; - if(m_material) { - Vector4 normCorners(m_cornerRadius / m_meshSize.y); - m_material->setVector4(gCornerRadius, &normCorners); +void Frame::applyStyle() { + Widget::applyStyle(); + + // Background color + auto it = m_styleRules.find("background-color"); + if(it != m_styleRules.end()) { + setColor(StyleSheet::toColor(it->second.second)); + } + + // Border color + it = m_styleRules.find("border-color"); + if(it != m_styleRules.end()) { + setBorderColor(StyleSheet::toColor(it->second.second)); + } + + it = m_styleRules.find("border-top-color"); + if(it != m_styleRules.end()) { + setTopColor(StyleSheet::toColor(it->second.second)); + } + + it = m_styleRules.find("border-right-color"); + if(it != m_styleRules.end()) { + setRightColor(StyleSheet::toColor(it->second.second)); + } + + it = m_styleRules.find("border-bottom-color"); + if(it != m_styleRules.end()) { + setBottomColor(StyleSheet::toColor(it->second.second)); + } + + it = m_styleRules.find("border-left-color"); + if(it != m_styleRules.end()) { + setLeftColor(StyleSheet::toColor(it->second.second)); } + + // Border radius + bool pixels; + Vector4 radius(corners()); + radius = styleBlockLength("border-radius", radius, pixels); + + radius.x = styleLength("border-top-left-radius", radius.x, pixels); + radius.y = styleLength("border-top-right-radius", radius.y, pixels); + radius.z = styleLength("border-bottom-left-radius", radius.z, pixels); + radius.w = styleLength("border-bottom-right-radius", radius.w, pixels); + + setCorners(radius); } /*! - Returns the border width of the frame. + Returns the corners radiuses of the frame. */ -float Frame::borderWidth() const { - return m_borderWidth; +Vector4 Frame::corners() const { + return m_borderRadius; } /*! - Sets the border \a width of the frame. + Sets the \a corners radiuses of the frame. */ -void Frame::setBorderWidth(float width) { - m_borderWidth = width; +void Frame::setCorners(Vector4 corners) { + m_borderRadius = corners; if(m_material) { - float width = m_borderWidth / m_meshSize.y; - m_material->setFloat(gBorderWidth, &width); + Vector4 normCorners(m_borderRadius / m_meshSize.y); + m_material->setVector4(gBorderRadius, &normCorners); } } /*! Returns the color of the frame to be drawn. */ Vector4 Frame::color() const { - return m_frameColor; + return m_backgroundColor; } /*! Changes the \a color of the frame to be drawn. */ void Frame::setColor(const Vector4 color) { - m_frameColor = color; + m_backgroundColor = color; if(m_material) { - m_material->setVector4(gFrameColor, &m_frameColor); + m_material->setVector4(gBackgroundColor, &m_backgroundColor); } } /*! @@ -201,10 +234,10 @@ void Frame::boundChanged(const Vector2 &bounds) { SpriteRender::composeMesh(nullptr, 0, m_mesh, m_meshSize, SpriteRender::Simple, false, 100.0f); if(m_material) { - Vector4 normCorners(m_cornerRadius / m_meshSize.y); - m_material->setVector4(gCornerRadius, &normCorners); + Vector4 normCorners(m_borderRadius / m_meshSize.y); + m_material->setVector4(gBorderRadius, &normCorners); - float width = m_borderWidth / m_meshSize.y; - m_material->setFloat(gBorderWidth, &width); + Vector4 normBorders(rectTransform()->border() / m_meshSize.y); + m_material->setVector4(gBorderWidth, &normBorders); } } diff --git a/modules/uikit/src/components/label.cpp b/modules/uikit/src/components/label.cpp index a5d9a7a00..481c8a988 100644 --- a/modules/uikit/src/components/label.cpp +++ b/modules/uikit/src/components/label.cpp @@ -2,6 +2,8 @@ #include "components/recttransform.h" +#include "resources/stylesheet.h" + #include #include #include @@ -18,6 +20,7 @@ namespace { const char *gFont = "Font"; const char *gOverride = "texture0"; const char *gClipRect = "clipRect"; + const char *gWeight = "weight"; }; /*! @@ -35,6 +38,7 @@ Label::Label() : m_mesh(Engine::objectCreate()), m_size(16), m_alignment(Left), + m_fontWeight(0.5f), m_kerning(true), m_wrap(false) { @@ -43,6 +47,7 @@ Label::Label() : Material *material = Engine::loadResource(".embedded/DefaultFont.shader"); if(material) { m_material = material->createInstance(); + m_material->setFloat(gWeight, &m_fontWeight); } } @@ -63,6 +68,45 @@ void Label::draw(CommandBuffer &buffer) { buffer.drawMesh(rectTransform()->worldTransform(), m_mesh, 0, CommandBuffer::UI, m_material); } } +/*! + \internal + Applies style settings assigned to widget. +*/ +void Label::applyStyle() { + Widget::applyStyle(); + + auto it = m_styleRules.find("color"); + if(it != m_styleRules.end()) { + setColor(StyleSheet::toColor(it->second.second)); + } + + it = m_styleRules.find("font-size"); + if(it != m_styleRules.end()) { + setFontSize(stoul(it->second.second, nullptr)); + } + + it = m_styleRules.find("font-weight"); + if(it != m_styleRules.end()) { + m_fontWeight = 0.5f; + if(it->second.second == "normal") { + m_fontWeight = 0.5f; + } else if(it->second.second == "bold") { + m_fontWeight = 0.8f; + } else if(it->second.second.back() == '0') { + m_fontWeight = stof(it->second.second) / 100.0f + 0.1f; + } + + if(m_material) { + m_material->setFloat(gWeight, &m_fontWeight); + } + } + + it = m_styleRules.find("white-space"); + if(it != m_styleRules.end()) { + bool wordWrap = it->second.second != "nowrap"; + setWordWrap(wordWrap); + } +} /*! Returns the text which will be drawn. */ diff --git a/modules/uikit/src/components/layout.cpp b/modules/uikit/src/components/layout.cpp index 3f32883df..578fe97eb 100644 --- a/modules/uikit/src/components/layout.cpp +++ b/modules/uikit/src/components/layout.cpp @@ -65,6 +65,7 @@ void Layout::insertTransform(int index, RectTransform *transform) { if(transform) { Layout *layout = new Layout; layout->m_attachedTransform = transform; + layout->m_attachedTransform->setAnchors(Vector2(0.0f, 1.0f), Vector2(0.0f, 1.0f)); transform->m_attachedLayout = this; insertLayout(index, layout); @@ -157,13 +158,6 @@ void Layout::setSpacing(float spacing) { m_spacing = spacing; invalidate(); } -/*! - Sets the \a left, \a top, \a right and \a bottom margins for the layout. -*/ -void Layout::setMargins(float left, float top, float right, float bottom) { - m_margins = Vector4(left, top, right, bottom); - invalidate(); -} /*! Returns the layout direction (Vertical or Horizontal). */ @@ -181,12 +175,19 @@ void Layout::setDirection(int direction) { Returns the size hint for the layout. */ Vector2 Layout::sizeHint() const { - Vector2 result(m_margins.x, m_margins.y); + Vector4 padding = m_rectTransform->padding(); + + Vector2 result(padding.w, padding.x); for(auto it : m_items) { Vector2 size; + if(it->m_attachedTransform) { // Item is widget if(it->m_attachedTransform->actor()->isEnabled()) { - size = it->m_attachedTransform->size(); + Vector4 margin = it->m_attachedTransform->margin(); + + size = Vector2(margin.w, margin.x); + size += it->m_attachedTransform->size(); + size += Vector2(margin.y, margin.z); } } else { // Item is layout size = it->sizeHint(); @@ -200,8 +201,9 @@ Vector2 Layout::sizeHint() const { result.y = MAX(result.y, size.y); } } - result.x += m_margins.z; - result.y += m_margins.w; + + result.x += padding.y; + result.y += padding.z; return result; } @@ -217,30 +219,41 @@ void Layout::invalidate() { */ void Layout::update() { if(m_dirty) { - float pos = ((m_direction == Vertical) ? m_margins.y : m_margins.x); + Vector4 padding = m_rectTransform->padding(); + + // Top and left paddings + float offset = ((m_direction == Vertical) ? padding.x : padding.w); for(auto it : m_items) { if(it->m_attachedTransform) { - if(it->m_attachedTransform->actor()->isEnabled()) { - pos += (it != *m_items.begin()) ? m_spacing : 0.0f; + RectTransform *r = it->m_attachedTransform; + if(r->actor()->isEnabled()) { + offset += (it != *m_items.begin()) ? m_spacing : 0.0f; + + Vector2 size = r->size(); + Vector2 pivot = r->pivot(); if(m_direction == Vertical) { - it->m_attachedTransform->setPosition(Vector3(m_margins.x + m_position.x, m_position.y - pos, 0.0f)); - pos += it->m_attachedTransform->size().y; + offset += size.y * (1.0f - pivot.y); + r->setPosition(Vector3(m_position.x + padding.w + size.x * pivot.x, + m_position.y - offset, 0.0f)); + offset += size.y * pivot.y; } else { - it->m_attachedTransform->setPosition(Vector3(m_position.x + pos, m_position.y - m_margins.y, 0.0f)); - pos += it->m_attachedTransform->size().x; + offset += size.x * pivot.x; + r->setPosition(Vector3(m_position.x + offset, + m_position.y - padding.x + size.y * pivot.y, 0.0f)); + offset += size.x * (1.0f - pivot.x); } } } else { - it->m_position = (m_direction == Vertical) ? Vector2(0.0f, -pos) : Vector2(pos, 0.0f); + it->m_position = (m_direction == Vertical) ? Vector2(0.0f, -offset) : Vector2(offset, 0.0f); it->update(); Vector2 size(it->sizeHint()); if(m_direction == Vertical) { - pos += size.y; + offset += size.y; } else { - pos += size.x; + offset += size.x; } } } diff --git a/modules/uikit/src/components/menu.cpp b/modules/uikit/src/components/menu.cpp index 699d015d3..b35814bf0 100644 --- a/modules/uikit/src/components/menu.cpp +++ b/modules/uikit/src/components/menu.cpp @@ -162,7 +162,6 @@ void Menu::composeComponent() { Layout *layout = new Layout; Vector4 c = corners(); - layout->setMargins(c.x, 0.0f, c.z, c.w); RectTransform *r = rectTransform(); if(r) { r->setLayout(layout); diff --git a/modules/uikit/src/components/recttransform.cpp b/modules/uikit/src/components/recttransform.cpp index a0a4c052a..d6c28eeea 100644 --- a/modules/uikit/src/components/recttransform.cpp +++ b/modules/uikit/src/components/recttransform.cpp @@ -138,6 +138,38 @@ void RectTransform::setMargin(const Vector4 margin) { setDirty(); } } +/*! + Returns the border width of the RectTransform. + The Vector4 contains border widths in top, right, bottom and left order. +*/ +Vector4 RectTransform::border() const { + return m_border; +} +/*! + Sets the top, right, bottom and left \a border width of the RectTransform. +*/ +void RectTransform::setBorder(const Vector4 border) { + if(m_border != border) { + m_border = border; + + notify(); + } +} +/*! + Returns the padding offset of the RectTransform. + The Vector4 contains padding offsets in top, right, bottom and left order. +*/ +Vector4 RectTransform::padding() const { + return m_padding; +} +/*! + Sets the top, right, bottom and left \a padding offsets of the RectTransform. +*/ +void RectTransform::setPadding(const Vector4 padding) { + if(m_padding != padding) { + m_padding = padding; + } +} /*! Returns true if the point with coodinates \a x and \a y is within the bounds, otherwise false. */ @@ -177,17 +209,6 @@ void RectTransform::setLayout(Layout *layout) { m_layout = layout; m_layout->setRectTransform(this); } -/*! - Returns the world transformation matrix of the RectTransform. -*/ -Matrix4 RectTransform::worldTransform() const { - if(m_dirty) { - m_worldTransform = Transform::worldTransform(); - cleanDirty(); - } - - return m_worldTransform; -} /*! \internal Internal method that returns the axis-aligned bounding box (AABBox). @@ -227,8 +248,8 @@ void RectTransform::cleanDirty() const { Vector2 v1 = parentCenter - rectCenter; Vector2 v2 = parentRect->m_size * m_minAnchors - m_bottomLeft; - m_worldTransform[12] += (m_minAnchors.x == m_maxAnchors.x) ? v1.x : v2.x; - m_worldTransform[13] += (m_minAnchors.y == m_maxAnchors.y) ? v1.y : v2.y; + m_worldTransform[12] += (abs(m_minAnchors.x - m_maxAnchors.x) <= EPSILON) ? v1.x : v2.x; + m_worldTransform[13] += (abs(m_minAnchors.y - m_maxAnchors.y) <= EPSILON) ? v1.y : v2.y; } } /*! diff --git a/modules/uikit/src/components/uiloader.cpp b/modules/uikit/src/components/uiloader.cpp new file mode 100644 index 000000000..baec3aa0a --- /dev/null +++ b/modules/uikit/src/components/uiloader.cpp @@ -0,0 +1,133 @@ +#include "components/uiloader.h" + +#include "components/actor.h" +#include "components/widget.h" +#include "components/recttransform.h" + +#include "resources/stylesheet.h" + +#include "utils/stringutil.h" + +#include + +void loadElementHelper(pugi::xml_node &node, Actor *parent) { + string type = node.name(); + + Actor *element = Engine::composeActor(type, node.attribute("name").as_string(), parent); + Widget *widget = dynamic_cast(element->component(type)); + if(widget) { + const MetaObject *meta = widget->metaObject(); + for(auto it : node.attributes()) { + int32_t index = meta->indexOfProperty(it.name()); + if(index > -1) { + MetaProperty property = meta->property(index); + Variant current = widget->property(property.name()); + + switch(current.type()) { + case MetaType::BOOLEAN: widget->setProperty(property.name(), it.as_bool()); break; + case MetaType::INTEGER: widget->setProperty(property.name(), it.as_int()); break; + case MetaType::FLOAT: widget->setProperty(property.name(), it.as_float()); break; + case MetaType::STRING: widget->setProperty(property.name(), it.as_string()); break; + default: break; + } + } + } + + string classes = node.attribute("class").as_string(); + if(!classes.empty()) { + for(auto &it : StringUtil::split(classes, ' ')) { + widget->addClass(it); + } + } + + string style = node.attribute("style").as_string(); + if(!style.empty()) { + widget->setStyle(style); + } + } + + for(pugi::xml_node it : node.children()) { + loadElementHelper(it, element); + } +} + +UiLoader::UiLoader() : + m_document(nullptr), + m_styleSheet(nullptr) { + +} + +void UiLoader::loadFromBuffer(const string &buffer) { + cleanHierarchy(this); + + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_buffer(buffer.c_str(), buffer.size()); + + if(result) { + for(pugi::xml_node node : doc.child("ui").children()) { + string type = node.name(); + if(type == "style") { + m_documentStyle = node.text().as_string(); + + if(m_styleSheet) { + m_styleSheet->addRawData(m_documentStyle); + } + } else { + loadElementHelper(node, actor()); + } + } + + applyStyle(); + } +} + +UiDocument *UiLoader::document() const { + return m_document; +} + +void UiLoader::setUiDocument(UiDocument *document) { + if(m_document != document) { + m_document = document; + + loadFromBuffer(m_document->data()); + } +} +/*! + Returns a style sheet assigned to the hierarhy of widgets. +*/ +StyleSheet *UiLoader::styleSheet() const { + return m_styleSheet; +} +/*! + Sets a \a style sheet to the hierarhy of widgets. +*/ +void UiLoader::setStyleSheet(StyleSheet *style) { + if(m_styleSheet != style) { + m_styleSheet = style; + + if(m_styleSheet) { + m_styleSheet->addRawData(m_documentStyle); + + resolveStyleSheet(this); + } + + applyStyle(); + } +} +/*! + \internal +*/ +void UiLoader::resolveStyleSheet(Widget *widget) { + for(auto it : widget->childWidgets()) { + m_styleSheet->resolve(it); + resolveStyleSheet(widget); + } +} +/*! + \internal +*/ +void UiLoader::cleanHierarchy(Widget *widget) { + for(auto it : widget->childWidgets()) { + delete it->actor(); + } +} diff --git a/modules/uikit/src/components/widget.cpp b/modules/uikit/src/components/widget.cpp index 4f97f42c8..552d2ba0d 100644 --- a/modules/uikit/src/components/widget.cpp +++ b/modules/uikit/src/components/widget.cpp @@ -3,6 +3,9 @@ #include "components/recttransform.h" #include "components/layout.h" +#include "resources/stylesheet.h" +#include "utils/stringutil.h" + #include "uisystem.h" #include @@ -53,6 +56,20 @@ string Widget::style() const { */ void Widget::setStyle(const string style) { m_style = style; + + StyleSheet::resolveInline(this); +} +/*! + Returns a list of stylesheet class names attached to this widget. +*/ +const list &Widget::classes() const { + return m_classes; +} +/*! + Adds a stylesheet class \a name attached to this widget. +*/ +void Widget::addClass(const string &name) { + m_classes.push_back(name); } /*! \internal @@ -73,9 +90,7 @@ void Widget::update() { Internal method called to draw the widget using the provided command buffer. */ void Widget::draw(CommandBuffer &buffer) { - if(m_parent == nullptr && m_transform) { - m_transform->setSize(buffer.viewport()); - } + A_UNUSED(buffer); } /*! Lowers the widget to the bottom of the widget's stack. @@ -107,12 +122,141 @@ void Widget::raise() { void Widget::boundChanged(const Vector2 &size) { A_UNUSED(size); } +/*! + Applies style settings assigned to widget. +*/ +void Widget::applyStyle() { + // Size + bool pixels; + Vector2 size = m_transform->size(); + + size.x = styleLength("width", size.x, pixels); + size.y = styleLength("height", size.y, pixels); + + m_transform->setSize(size); + + // Pivot point + auto it = m_styleRules.find("-uikit-pivot"); + if(it != m_styleRules.end()) { + auto list = StringUtil::split(it->second.second, ' '); + + Vector2 value; + if(list.size() == 1) { + value.x = value.y = stof(list[0]); + } else { + value.x = stof(list[0]); + value.y = stof(list[1]); + } + m_transform->setPivot(value); + } + + // Anchors + it = m_styleRules.find("-uikit-min-anchors"); + if(it != m_styleRules.end()) { + auto list = StringUtil::split(it->second.second, ' '); + + Vector2 value; + if(list.size() == 1) { + value.x = value.y = stof(list[0]); + } else { + value.x = stof(list[0]); + value.y = stof(list[1]); + } + m_transform->setMinAnchors(value); + } + + it = m_styleRules.find("-uikit-max-anchors"); + if(it != m_styleRules.end()) { + auto list = StringUtil::split(it->second.second, ' '); + + Vector2 value; + if(list.size() == 1) { + value.x = value.y = stof(list[0]); + } else { + value.x = stof(list[0]); + value.y = stof(list[1]); + } + m_transform->setMaxAnchors(value); + } + + // Border width + Vector4 border(m_transform->border()); + border = styleBlockLength("border-width", border, pixels); + + border.x = styleLength("border-top-width", border.x, pixels); + border.y = styleLength("border-right-width", border.y, pixels); + border.z = styleLength("border-bottom-width", border.z, pixels); + border.w = styleLength("border-left-width", border.w, pixels); + + m_transform->setBorder(border); + + // Margins + Vector4 margin(m_transform->margin()); + margin = styleBlockLength("margin", margin, pixels); + + margin.x = styleLength("margin-top", margin.x, pixels); + margin.y = styleLength("margin-right", margin.y, pixels); + margin.z = styleLength("margin-bottom", margin.z, pixels); + margin.w = styleLength("margin-left", margin.w, pixels); + + m_transform->setMargin(margin); + + // Padding + Vector4 padding(m_transform->padding()); + padding = styleBlockLength("padding", padding, pixels); + + padding.x = styleLength("padding-top", padding.x, pixels); + padding.y = styleLength("padding-right", padding.y, pixels); + padding.z = styleLength("padding-bottom", padding.z, pixels); + padding.w = styleLength("padding-left", padding.w, pixels); + + m_transform->setPadding(padding); + + // Display + Layout *layout = m_transform->layout(); + + it = m_styleRules.find("display"); + if(it != m_styleRules.end()) { + string layoutMode = it->second.second; + if(layoutMode == "none") { + actor()->setEnabled(false); + } else { + layout = new Layout; + + if(layoutMode == "block") { + layout->setDirection(Layout::Vertical); + } else if(layoutMode == "inline") { + layout->setDirection(Layout::Horizontal); + } + + m_transform->setLayout(layout); + } + } + + // Child widgets + for(auto it : childWidgets()) { + if(layout) { + layout->addTransform(it->rectTransform()); + } + it->applyStyle(); + } +} /*! Returns the parent Widget. */ Widget *Widget::parentWidget() { return m_parent; } +/*! + Returns a list of child widgets; +*/ +list Widget::childWidgets() const { + list result; + for(auto it : actor()->componentsInChild("Widget")) { + result.push_back(static_cast(it)); + } + return result; +} /*! Returns RectTransform component attached to parent Actor. */ @@ -186,6 +330,19 @@ void Widget::setSystem(ObjectSystem *system) { UiSystem *render = static_cast(system); render->addWidget(this); } +/*! + \internal + Applies a new stylesheet \a rules to the widget. + A \a wieght parameter required to select rules between new one and existant. +*/ +void Widget::addStyleRules(const map &rules, uint32_t weight) { + for(auto rule : rules) { + auto it = m_styleRules.find(rule.first); + if(it == m_styleRules.end() || it->second.first <= weight) { + m_styleRules[rule.first] = make_pair(weight, rule.second); + } + } +} /*! \internal Internal method to draw selected gizmos for the widget, such as a wireframe box. @@ -195,6 +352,58 @@ void Widget::drawGizmosSelected() { Gizmos::drawRectangle(box.center, Vector2(box.extent.x * 2.0f, box.extent.y * 2.0f), Vector4(0.5f, 1.0f, 0.5f, 1.0f)); } +/*! + \internal + Returns length for the stylesheet \a property. + Default \a value will be used in case of property will not be found. + Parameter \a pixels contains a definition of unit of measurement. +*/ +float Widget::styleLength(const string &property, float value, bool &pixels) { + auto it = m_styleRules.find(property); + if(it != m_styleRules.end()) { + return StyleSheet::toLength(it->second.second, pixels); + } else { + pixels = true; + } + return value; +} +/*! + \internal + Returns length block for the stylesheet \a property. + Default \a value will be used in case of property will not be found. + Parameter \a pixels contains a definition of unit of measurement. +*/ +Vector4 Widget::styleBlockLength(const string &property, const Vector4 &value, bool &pixels) { + Vector4 result(value); + + auto it = m_styleRules.find(property); + if(it != m_styleRules.end()) { + auto array = StringUtil::split(it->second.second, ' '); + switch(array.size()) { + case 1: { + result = Vector4(StyleSheet::toLength(array[0], pixels)); + } break; + case 2: { + result.x = result.z = StyleSheet::toLength(array[0], pixels); + result.y = result.w = StyleSheet::toLength(array[1], pixels); + } break; + case 3: { + result.y = StyleSheet::toLength(array[0], pixels); + result.z = result.x = StyleSheet::toLength(array[1], pixels); + result.w = StyleSheet::toLength(array[2], pixels); + } break; + case 4: { + result.x = StyleSheet::toLength(array[0], pixels); + result.y = StyleSheet::toLength(array[1], pixels); + result.z = StyleSheet::toLength(array[2], pixels); + result.w = StyleSheet::toLength(array[3], pixels); + } break; + default: break; + } + } + + return result; +} /*! \internal Returns true if this widget was defined as internal for the other complex widgets; otherwise returns false. diff --git a/modules/uikit/src/pipelinetasks/guilayer.cpp b/modules/uikit/src/pipelinetasks/guilayer.cpp index 86eceab6b..7fb099baf 100644 --- a/modules/uikit/src/pipelinetasks/guilayer.cpp +++ b/modules/uikit/src/pipelinetasks/guilayer.cpp @@ -10,6 +10,7 @@ #include "components/actor.h" #include "components/world.h" #include "components/widget.h" +#include "components/recttransform.h" GuiLayer::GuiLayer() : m_uiAsSceneView(false) { @@ -52,6 +53,10 @@ void GuiLayer::exec(PipelineContext &context) { } for(auto it : m_uiComponents) { + if(!m_uiAsSceneView && it->parentWidget() == nullptr && it->rectTransform()) { + it->rectTransform()->setSize(buffer->viewport()); + } + it->draw(*buffer); } diff --git a/modules/uikit/src/resources/stylesheet.cpp b/modules/uikit/src/resources/stylesheet.cpp new file mode 100644 index 000000000..819f71620 --- /dev/null +++ b/modules/uikit/src/resources/stylesheet.cpp @@ -0,0 +1,156 @@ +#include "stylesheet.h" + +#include +#include + +#include +#include + +#include "utils/selector.h" +#include "utils/cssparser.h" +#include "utils/stringutil.h" + +StyleSheet::StyleSheet() : + m_parser(new CSSParser()) { + +} + +bool StyleSheet::addRawData(const string &data) { + return reinterpret_cast(m_parser)->parseByString(data); +} + +void StyleSheet::resolve(Widget *widget) { + CSSParser *parser = reinterpret_cast(m_parser); + + for(auto it : parser->selectors()) { + if(it->isMeet(widget)) { + widget->addStyleRules(it->ruleDataMap(), it->weight()); + } + } +} + +void StyleSheet::resolveInline(Widget *widget) { + string inlineStyle = widget->style(); + if(!inlineStyle.empty()) { + std::map result; + + StringUtil::trim(inlineStyle); + StringUtil::deletechar(inlineStyle, '\n'); + auto keyValues = StringUtil::splitButSkipBrackets(inlineStyle, ';'); + + for(const auto &pair : keyValues) { + auto keyAndValue = StringUtil::splitButSkipBrackets(pair, ':'); + + if(keyAndValue.size() < 2) { + continue; + } + result[StringUtil::trim(keyAndValue[0])] = StringUtil::trim(keyAndValue[1]); + } + + widget->addStyleRules(result, 1000); + } +} + +Vector4 StyleSheet::toColor(const string &value) { + static map colors = { + {"aliceblue", "#fff0f8ff"}, {"antiquewhite", "#fffaebd7"}, {"aqua", "#ff00ffff"}, {"aquamarine", "#ff7fffd4"}, + {"azure", "#fff0ffff"}, {"beige", "#fff5f5dc"}, {"bisque", "#ffffe4c4"}, {"black", "#ff000000"}, + {"blanchedalmond", "#ffffebcd"}, {"blue", "#ff0000ff"}, {"blueviolet", "#ff8a2be2"}, {"brown", "#ffa52a2a"}, + {"burlywood", "#ffdeb887"}, {"cadetblue", "#ff5f9ea0"}, {"chartreuse", "#ff7fff00"}, {"chocolate", "#ffd2691e"}, + {"coral", "#ffff7f50"}, {"cornflowerblue", "#ff6495ed"}, {"cornsilk", "#fffff8dc"}, {"crimson", "#ffdc143c"}, + {"cyan", "#ff00ffff"}, {"darkblue", "#ff00008b"}, {"darkcyan", "#ff008b8b"}, {"darkgoldenrod", "#ffb8860b"}, + {"darkgray", "#ffa9a9a9"}, {"darkgreen", "#ff006400"}, {"darkgrey", "#ffa9a9a9"}, {"darkkhaki", "#ffbdb76b"}, + {"darkmagenta", "#ff8b008b"}, {"darkolivegreen", "#ff556b2f"}, {"darkorange", "#ffff8c00"}, {"darkorchid", "#ff9932cc"}, + {"darkred", "#ff8b0000"}, {"darksalmon", "#ffe9967a"}, {"darkseagreen", "#ff8fbc8f"}, {"darkslateblue", "#ff483d8b"}, + {"darkslategray", "#ff2f4f4f"}, {"darkslategrey", "#ff2f4f4f"}, {"darkturquoise", "#ff00ced1"}, {"darkviolet", "#ff9400d3"}, + {"deeppink", "#ffff1493"}, {"deepskyblue", "#ff00bfff"}, {"dimgray", "#ff696969"}, {"dimgrey", "#ff696969"}, + {"dodgerblue", "#ff1e90ff"}, {"firebrick", "#ffb22222"}, {"floralwhite", "#fffffaf0"}, {"forestgreen", "#ff228b22"}, + {"gainsboro", "#ffdcdcdc"}, {"ghostwhite", "#fff8f8ff"}, {"gold", "#ffffd700"}, {"gray", "#ff808080"}, + {"green", "#ff008000"}, {"goldenrod", "#ffdaa520"}, {"greenyellow", "#ffadff2f"}, {"grey", "#ff808080"}, + {"honeydew", "#fff0fff0"}, {"hotpink", "#ffff69b4"}, {"indianred", "#ffcd5c5c"}, {"indigo", "#ff4b0082"}, + {"ivory", "#fffffff0"}, {"khaki", "#fff0e68c"}, {"lavender", "#ffe6e6fa"}, {"lavenderblush", "#fffff0f5"}, + {"lawngreen", "#ff7cfc00"}, {"lemonchiffon", "#fffffacd"}, {"lightblue", "#ffadd8e6"}, {"lightcoral", "#fff08080"}, + {"lightcyan", "#ffe0ffff"}, {"lightgray", "#ffd3d3d3"}, {"lightgreen", "#ff90ee90"}, {"lightgrey", "#ffd3d3d3"}, + {"lightpink", "#ffffb6c1"}, {"lightsalmon", "#ffffa07a"}, {"lightseagreen", "#ff20b2aa"}, {"lightskyblue", "#ff87cefa"}, + {"lightslategray", "#ff778899"}, {"lightslategrey", "#ff778899"}, {"lightsteelblue", "#ffb0c4de"}, {"lightyellow", "#ffffffe0"}, + {"lime", "#ff00ff00"}, {"limegreen", "#ff32cd32"}, {"linen", "#fffaf0e6"}, {"magenta", "#ffff00ff"}, + {"maroon", "#ff800000"}, {"mediumaquamarine","#ff66cdaa"}, {"mediumblue", "#ff0000cd"}, {"mediumorchid", "#ffba55d3"}, + {"mediumpurple", "#ff9370db"}, {"mediumseagreen", "#ff3cb371"}, {"mediumslateblue", "#ff7b68ee"}, {"mediumturquoise", "#ff48d1cc"}, + {"mediumvioletred", "#ffc71585"}, {"midnightblue", "#ff191970"}, {"mintcream", "#fff5fffa"}, {"mistyrose", "#ffffe4e1"}, + {"moccasin", "#ffffe4b5"}, {"navajowhite", "#ffffdead"}, {"navy", "#ff000080"}, {"oldlace", "#fffdf5e6"}, + {"olive", "#ff808000"}, {"olivedrab", "#ff6b8e23"}, {"orange", "#ffffa500"}, {"orangered", "#ffff4500"}, + {"orchid", "#ffda70d6"}, {"palegoldenrod", "#ffeee8aa"}, {"palegreen", "#ff98fb98"}, {"paleturquoise", "#ffafeeee"}, + {"palevioletred", "#ffdb7093"}, {"papayawhip", "#ffffefd5"}, {"peachpuff", "#ffffdab9"}, {"peru", "#ffcd853f"}, + {"pink", "#ffffc0cb"}, {"plum", "#ffdda0dd"}, {"powderblue", "#ffb0e0e6"}, {"purple", "#ff800080"}, + {"rebeccapurple", "#ff663399"}, {"red", "#ffff0000"}, {"rosybrown", "#ffbc8f8f"}, {"royalblue", "#ff4169e1"}, + {"saddlebrown", "#ff8b4513"}, {"salmon", "#fffa8072"}, {"sandybrown", "#fff4a460"}, {"seagreen", "#ff2e8b57"}, + {"seashell", "#fffff5ee"}, {"sienna", "#ffa0522d"}, {"silver", "#ffc0c0c0"}, {"skyblue", "#ff87ceeb"}, + {"slateblue", "#ff6a5acd"}, {"slategray", "#ff708090"}, {"slategrey", "#ff708090"}, {"snow", "#fffffafa"}, + {"springgreen", "#ff00ff7f"}, {"steelblue", "#ff4682b4"}, {"tan", "#ffd2b48c"}, {"teal", "#ff008080"}, + {"thistle", "#ffd8bfd8"}, {"tomato", "#ffff6347"}, {"transparent", "#00000000"}, {"turquoise", "#ff40e0d0"}, + {"violet", "#ffee82ee"}, {"wheat", "#fff5deb3"}, {"white", "#ffffffff"}, {"whitesmoke", "#fff5f5f5"}, + {"yellow", "#ffffff00"}, {"yellowgreen", "#ff9acd32"}, + }; + + string str = value; + auto it = colors.find(str); + if(it != colors.end()) { + str = it->second; + } + + Vector4 result(0.0f, 0.0f, 0.0f, 1.0f); + + if(str[0] == '#') { + uint32_t rgba = stoul(&str[1], nullptr, 16); + + uint32_t size = str.size(); + switch(size) { + case 4: // #RGB + case 5: { // #ARGB + uint8_t p1 = rgba; + uint8_t p0 = rgba >> 8; + + result.x = float((p0 & 0x0f) | ((p0 & 0x0f) << 4)) / 255.0f; + result.y = float((p1 >> 4) | ((p1 >> 4) << 4)) / 255.0f; + result.z = float((p1 & 0x0f) | ((p1 & 0x0f) << 4)) / 255.0f; + if(size == 5) { + result.w = float((p0 >> 4) | ((p0 >> 4) << 4)) / 255.0f; + } + } break; + case 7: // #RRGGBB + case 9: { // #FFRRGGBB + result.z = float(uint8_t(rgba)) / 255.0f; + result.y = float(uint8_t(rgba >> 8)) / 255.0f; + result.x = float(uint8_t(rgba >> 16)) / 255.0f; + if(size == 9) { + result.w = float(uint8_t(rgba >> 24)) / 255.0f; + } + } break; + default: break; + } + } else if(str[0] == 'r') { + smatch match; + regex_search(value, match, regex("([0-9]*[.]?[0-9]+),*[ ]*([0-9]*[.]?[0-9]+)?,*[ ]*([0-9]*[.]?[0-9]+)?,*[ ]*([0-9]*[.]?[0-9]+)?")); + + for(int i = 1; i < match.size(); i++) { + string sub = match[i]; + if(sub.empty() || i == 4) { + break; + } + result[i-1] = stof(match[i]); + } + + result.x /= 255.0f; + result.y /= 255.0f; + result.z /= 255.0f; + } + + return result; +} + +float StyleSheet::toLength(const string &value, bool &pixels) { + pixels = (value.back() != '%'); + + string sr = value.substr(0, value.size() - (pixels ? 2 : 1)); + return stof(sr); +} diff --git a/modules/uikit/src/resources/uidocument.cpp b/modules/uikit/src/resources/uidocument.cpp new file mode 100644 index 000000000..cce24f7cc --- /dev/null +++ b/modules/uikit/src/resources/uidocument.cpp @@ -0,0 +1,39 @@ +#include "resources/uidocument.h" + +namespace { + const char *gData = "Data"; +} + +UiDocument::UiDocument() { + +} + +/*! + Returns content as a string. +*/ +string UiDocument::data() const { + return m_data; +} +/*! + Sets a new content \a data. +*/ +void UiDocument::setData(const string &data) { + m_data = data; +} +/*! + \internal +*/ +void UiDocument::loadUserData(const VariantMap &data) { + auto it = data.find(gData); + if(it != data.end()) { + m_data = (*it).second.toString(); + } +} +/*! + \internal +*/ +VariantMap UiDocument::saveUserData() const { + VariantMap result; + result[gData] = m_data; + return result; +} diff --git a/modules/uikit/src/uisystem.cpp b/modules/uikit/src/uisystem.cpp index 390ec3384..88e3bad32 100644 --- a/modules/uikit/src/uisystem.cpp +++ b/modules/uikit/src/uisystem.cpp @@ -1,6 +1,7 @@ #include "uisystem.h" #include +#include #include #include "components/recttransform.h" @@ -15,9 +16,13 @@ #include "components/textinput.h" #include "components/floatinput.h" #include "components/toolbutton.h" +#include "components/uiloader.h" #include "pipelinetasks/guilayer.h" +#include "resources/stylesheet.h" +#include "resources/uidocument.h" + list UiSystem::m_uiComponents; UiSystem::UiSystem() : @@ -47,6 +52,11 @@ UiSystem::UiSystem() : GuiLayer::registerClassFactory(this); + UiLoader::registerClassFactory(this); + + StyleSheet::registerClassFactory(Engine::resourceSystem()); + UiDocument::registerClassFactory(Engine::resourceSystem()); + setName("Ui"); } @@ -72,6 +82,11 @@ UiSystem::~UiSystem() { FloatInput::unregisterClassFactory(this); ToolButton::unregisterClassFactory(this); + + UiLoader::unregisterClassFactory(this); + + StyleSheet::unregisterClassFactory(Engine::resourceSystem()); + UiDocument::unregisterClassFactory(Engine::resourceSystem()); } bool UiSystem::init() { diff --git a/modules/uikit/src/utils/attributeselector.cpp b/modules/uikit/src/utils/attributeselector.cpp new file mode 100644 index 000000000..677a0d501 --- /dev/null +++ b/modules/uikit/src/utils/attributeselector.cpp @@ -0,0 +1,69 @@ +#include "utils/attributeselector.h" + +#include "utils/stringutil.h" + +#include "components/widget.h" + +AttributeSelector::AttributeSelector(const std::string &key, const std::string &value, AttributeFilterRule rule) { + m_key = key; + m_value = value; + m_filterRule = rule; + m_selectorType = Selector::AttributeSelector; +} + +bool AttributeSelector::isMeet(Widget *widget) { + std::string key = m_key; + + if(key.empty()) { + return false; + } + + const MetaObject *meta = widget->metaObject(); + + int32_t index = meta->indexOfProperty(key.c_str()); + if(index < 0) { + return false; + } + + std::string propertyValue = meta->property(index).read(widget).toString(); + + bool ret = false; + switch(m_filterRule) { + case AttributeSelector::Equal: return (propertyValue == m_value); + case AttributeSelector::DashMatch: { + if(propertyValue.find("-") == std::string::npos) { + break; + } + auto attrs = StringUtil::split(propertyValue, '-'); + return *attrs.begin() == m_value; + } + case AttributeSelector::Prefix: return (propertyValue.find(m_value, 0) == 0); + case AttributeSelector::Suffix: return (propertyValue.rfind(m_value) + m_value.length() == propertyValue.length()); + case AttributeSelector::Include: { + if(propertyValue.find(" ") == std::string::npos) { + break; + } + + auto attrs = StringUtil::split(propertyValue, ' '); + for(const auto &attr : attrs) { + if(attr == m_value) { + return true; + } + } + break; + } + case AttributeSelector::Substring: return (propertyValue.find(m_value, 0) != std::string::npos); + case AttributeSelector::NoRule: return true; + default: return false; + } + + return ret; +} + +bool AttributeSelector::isBaseSelector() const { + return true; +} + +int AttributeSelector::weight() { + return 10; +} diff --git a/modules/uikit/src/utils/classselector.cpp b/modules/uikit/src/utils/classselector.cpp new file mode 100644 index 000000000..9285a87b6 --- /dev/null +++ b/modules/uikit/src/utils/classselector.cpp @@ -0,0 +1,23 @@ +#include "utils/classselector.h" + +#include "components/widget.h" + +#include + +ClassSelector::ClassSelector(const std::string &cls) { + m_class = cls; + m_selectorType = Selector::ClassSelector; +} + +bool ClassSelector::isMeet(Widget *widget) { + auto classes = widget->classes(); + return std::find(classes.begin(), classes.end(), m_class) != classes.end(); +} + +bool ClassSelector::isBaseSelector() const { + return true; +} + +int ClassSelector::weight() { + return 10; +} diff --git a/modules/uikit/src/utils/combineselector.cpp b/modules/uikit/src/utils/combineselector.cpp new file mode 100644 index 000000000..046edd84c --- /dev/null +++ b/modules/uikit/src/utils/combineselector.cpp @@ -0,0 +1,273 @@ +#include "utils/combineselector.h" + +#include "components/widget.h" + +#include + +CombineSelector::CombineSelector() { + m_selectorType = Selector::CombineSelector; + m_combineType = NoCombine; +} + +CombineSelector::~CombineSelector() { + for(auto it : m_normalSiblingList) { + delete it; + } + + for(auto it : m_instanceSiblingList) { + delete it; + } + + for(auto it : m_normalInhericalList) { + delete it; + } + + for(auto it : m_instanceSiblingList) { + delete it; + } +} + +void CombineSelector::initialNormalSiblingList(Selector *head, Selector *sibling) { + if(m_combineType != NoCombine) { + assert(0); + } + if(!head || !sibling) { + return; + } + + for(auto it : m_normalSiblingList) { + delete it; + } + m_normalSiblingList.clear(); + + m_normalSiblingList.push_back(head); + m_normalSiblingList.push_back(sibling); + m_combineType = NormalSibling; +} + +void CombineSelector::initialInstanceSiblingList(Selector *head, Selector *sibling) { + if(m_combineType != NoCombine) { + assert(0); + } + + if(!head || !sibling) { + return; + } + + for(auto it : m_instanceSiblingList) { + delete it; + } + m_instanceSiblingList.clear(); + + m_instanceSiblingList.push_back(head); + m_instanceSiblingList.push_back(sibling); + m_combineType = InstanceSibling; +} + +void CombineSelector::initialNormalInhericalList(Selector *root, Selector *child) { + if(m_combineType != NoCombine) { + assert(0); + } + + if(!root || !child) { + return; + } + + for(auto it : m_normalInhericalList) { + delete it; + } + m_normalInhericalList.clear(); + + m_normalInhericalList.push_back(root); + m_normalInhericalList.push_back(child); + m_combineType = NormalInherical; +} + +void CombineSelector::initialInstanceInhericalList(Selector *root, Selector *child) { + if (m_combineType != NoCombine) { + assert(0); + } + + if (!root || !child) { + return; + } + + for(auto it : m_instanceInhericalList) { + delete it; + } + m_instanceInhericalList.clear(); + + m_instanceInhericalList.push_back(root); + m_instanceInhericalList.push_back(child); + m_combineType = InstanceInherical; +} + +bool CombineSelector::isMeet(Widget *widget) { + matchingWidgets.clear(); + Selector *before = CombineSelector::before(); + Selector *after = CombineSelector::after(); + + if(!before || !after) { + return false; + } + + Widget *parent = widget->parentWidget(); + + if(!parent || !after->isMeet(widget)) { + return false; + } + + std::vector nodesToCheck; + if(after->type() == Selector::CombineSelector) { + auto cs = static_cast(after); + nodesToCheck.insert(nodesToCheck.end(), cs->matchingWidgets.begin(), cs->matchingWidgets.end()); + } else { + nodesToCheck.push_back(widget); + } + + for(size_t i = 0; i < nodesToCheck.size(); i++) { + widget = nodesToCheck[i]; + parent = widget->parentWidget(); + + if(!parent) { + continue; + } + + switch(m_combineType) { + case CombineSelector::NormalSibling: { // Unsupported + //int idx = widget->GetIndexWithinParent(); + //for(const auto &sibling : parent->childWidgets()) { + // if(sibling->GetIndexWithinParent() >= idx) { + // continue; + // } + // if(doesWidgetSelector(sibling, before)) { + // selector->matchingWidgets.push_back(sibling); + // } + //} + break; + } + case CombineSelector::InstanceSibling: { + Widget *lastWidget = nullptr; + for(const auto &sibling : parent->childWidgets()) { + if(lastWidget && sibling == widget) { + if(before->isMeet(lastWidget)) { + matchingWidgets.push_back(lastWidget); + } + } + lastWidget = sibling; + } + break; + } + case CombineSelector::InstanceInherical: { + if(before->isMeet(parent)) { + matchingWidgets.push_back(parent); + } + break; + } + case CombineSelector::NormalInherical: { + while(parent) { + if(before->isMeet(parent)) { + matchingWidgets.push_back(parent); + break; + } + parent = parent->parentWidget(); + } + break; + } + default: break; + } + } + + return !matchingWidgets.empty(); +} + +bool CombineSelector::isBaseSelector() const { + return false; +} + +int CombineSelector::weight() { + int w = 0; + std::list::iterator one; + std::list::iterator other; + do { + if (m_normalInhericalList.size() == 2) { + one = m_normalInhericalList.begin(); + other = --m_normalInhericalList.end(); + break; + } + if (m_instanceInhericalList.size() == 2) { + one = m_instanceInhericalList.begin(); + other = --m_instanceInhericalList.end(); + break; + } + if (m_normalSiblingList.size() == 2) { + one = m_normalSiblingList.begin(); + other = --m_normalSiblingList.end(); + break; + } + if (m_instanceSiblingList.size() == 2) { + one = m_instanceSiblingList.begin(); + other = --m_instanceSiblingList.end(); + break; + } + } while(0); + + if(!*one || !*other) { + return w; + } + + w += (*one)->weight() + (*other)->weight(); + return w; +} + +Selector* CombineSelector::before() { + std::list::iterator before; + do { + if(m_normalInhericalList.size() == 2) { + before = m_normalInhericalList.begin(); + break; + } + + if(m_instanceInhericalList.size() == 2) { + before = m_instanceInhericalList.begin(); + break; + } + + if(m_normalSiblingList.size() == 2) { + before = m_normalSiblingList.begin(); + break; + } + + if(m_instanceSiblingList.size() == 2) { + before = m_instanceSiblingList.begin(); + break; + } + } while (0); + return *before; +} + +Selector* CombineSelector::after() { + std::list::iterator after; + do { + if(m_normalInhericalList.size() == 2) { + after = --m_normalInhericalList.end(); + break; + } + + if(m_instanceInhericalList.size() == 2) { + after = --m_instanceInhericalList.end(); + break; + } + + if(m_normalSiblingList.size() == 2) { + after = --m_normalSiblingList.end(); + break; + } + + if(m_instanceSiblingList.size() == 2) { + after = --m_instanceSiblingList.end(); + break; + } + } while (0); + return *after; +} diff --git a/modules/uikit/src/utils/csslex.cpp b/modules/uikit/src/utils/csslex.cpp new file mode 100644 index 000000000..1603b8bb6 --- /dev/null +++ b/modules/uikit/src/utils/csslex.cpp @@ -0,0 +1,801 @@ +#include "utils/csslex.h" + +#include + +#define NextChar(buffer) m_forwardPos >= m_bufferSize ? 0 : *(buffer + m_forwardPos++) +#define ErrorInLoop STATUS = LexError;stopLoop = true; +#define WS_CASE ' ': case '\r': case '\n': case '\t': case '\f' +#define NUMBER_CASE '0': case '1': case '2': case '3': case '4':\ +case '5': case '6': case '7': case '8': case '9' + +Lex::Lex() { + m_buffer = 0; + m_firstPos = 0; + m_forwardPos = 0; +} + +Lex::~Lex() { + cleanResource(); +} + +void Lex::setBufferString(const std::string &bufferString) { + if(bufferString.empty()) { + return; + } + + if(m_buffer) { + delete [] m_buffer; + m_buffer = 0; + } + + m_bufferSize = bufferString.size(); + m_buffer = new char[m_bufferSize]; + memcpy(static_cast(m_buffer), bufferString.data(), m_bufferSize); + m_firstPos = 0; + m_forwardPos = 0; +} + +Lex::CSSToken *Lex::textToken(char stringType) { + enum { + _START = 0, + _ESCAPESTART, + _STRING + }; + char status = _START; + CSSToken *token = new CSSToken; + m_tokenCache.insert(token); + m_forwardPos = m_firstPos; + + if(m_forwardPos >= m_bufferSize) { + token->type = END; + return token; + } + char stringBoundary = stringType == 1 ? '"' : '\''; + while(1) { + char c = NextChar(m_buffer); + switch(status) { + case _START: { + if((c != '\r' && c != '\n' && c != '\f' && c != stringBoundary) || (c & 0x80)) { + status = _STRING; + break; + } else if(c == '\\') { + status = _ESCAPESTART; + break; + } + break; + } + case _ESCAPESTART: { + if (isDigitalCharacter(c) || isLetter(c)) { + // [0-9a-z]{1,6} + for(int i = 0; i < 5; i ++) { + c = NextChar(m_buffer); + if (isDigitalCharacter(c) || isLetter(c)) { + continue; + } else { + break; + } + } + } else if (c == '\r' || c == '\n' || c == '\f') { + // \\{nl} + // nl \n|\r\n|\r|\f + } else { + // escape's \\[^\n\r\f0-9a-f] + } + status = _STRING; + break; + } + case _STRING: { + if((c != '\r' && c != '\n' && c != '\f' && c != stringBoundary) || (c & 0x80) || c == '\\') { + --m_forwardPos; + status = _START; + } else { + token->type = STRING; + token->data = createData(m_firstPos, m_forwardPos); + return token; + } + break; + } + default: break; + } + } + + return token; +} + +Lex::CSSToken *Lex::identToken() { + CSSToken *token = new CSSToken; + m_tokenCache.insert(token); + token->type = IDENT; + CSSDFAStatus STATUS = Start; + bool stopLoop = false; + while(1) { + if (m_forwardPos >= m_bufferSize) { + STATUS = end; + break; + } + unsigned char c = NextChar(m_buffer); + switch (STATUS) { + case Start: { + if(c == IDENT_START_SIGN) { + STATUS = iDentStart; + } else if(isLetter(c) || c == UNDER_LINE_SIGN || (c & 0x80)) { + STATUS = NMStart; + } else if(c == BACK_SPLASH) { + STATUS = EscapeStartInNMStart; + } else if(isWs(c)) { + STATUS = Ws; + } else if(c == HASH_SIGN) { + STATUS = HashStart; + } else if(c == KEYWORD_SIGN) { + STATUS = AtKeyWordStart; + } else { + STATUS = LexError; + stopLoop = true; + } + break; + } + case iDentStart: { + if(isLetter(c) || c == UNDER_LINE_SIGN || (c & 0x80)) { + STATUS = NMStart; + } else if(c == BACK_SPLASH) { + STATUS = EscapeStartInNMStart; + } else { + STATUS = LexError; + stopLoop = true; + } + break; + } + case NMStart: { + if(isLetter(c) || isDigitalCharacter(c) || c == UNDER_LINE_SIGN || c == IDENT_START_SIGN || (c &0x80)) { + STATUS = NMChar; + } else if(c == BACK_SPLASH) { + STATUS = EscapeStartInNMChar; + } else { + STATUS = iDent; + stopLoop = true; + } + break; + } + case NMChar: { + if(isLetter(c) || isDigitalCharacter(c) || c == UNDER_LINE_SIGN || c == IDENT_START_SIGN || (c &0x80)) { + STATUS = NMChar; + } else if(c == BACK_SPLASH) { + STATUS = EscapeStartInNMChar; + } else { + STATUS = iDent; + stopLoop = true; + } + break; + } + case EscapeStartInNMStart: { + if(isHexCharacter(c)) { + for(int i = 0; i < 5; i++) { + c = NextChar(m_buffer); + if(c == 0) { + break; + } + if(isHexCharacter(c)) { + continue; + } else { + --m_forwardPos; + break; + } + } + STATUS = NMStart; + } + break; + } + case EscapeStartInNMChar: { + if(isHexCharacter(c)) { + for(int i = 0; i < 5; i++) { + c = NextChar(m_buffer); + if(c == 0) { + break; + } + if(isHexCharacter(c)) { + continue; + } else { + --m_forwardPos; + break; + } + } + STATUS = NMChar; + } + break; + } + default: { + STATUS = LexError; + break; + } + } + if(stopLoop) { + --m_forwardPos; + break; + } + } + token->data = createData(m_firstPos, m_forwardPos); + if(STATUS == iDent) { + token->type = IDENT; + } else if(STATUS == end) { + token->type = END; + } else { + token->type = ERROR; + } + m_firstPos = m_forwardPos; + return token; +} + +Lex::CSSToken *Lex::numberToken() { + enum { + _start, + _numberStart, + _numberFinish, + _numberInDecimal, + _error + }; + std::string s; + char status = _start; + char c = NextChar(m_buffer); + if (c == 0) { + return nullptr; + } + std::string data; + while(1) { + switch(status) { + case _start: { + if(!isDigitalCharacter(c)) { + status = _error; + break; + } else { + status = _numberStart; + data.append(std::string(1, c)); + } + break; + } + case _numberStart: { + if(!isDigitalCharacter(c)) { + if(c == '.') { + status = _numberInDecimal; + } else { + status = _numberFinish; + break; + } + } + data.append(std::string(1, c)); + break; + } + case _numberInDecimal:{ + if(!isDigitalCharacter(c)) { + status = _numberFinish; + } + data.append(std::string(1, c)); + break; + } + default: { + status = _error; + break; + } + } + if(status == _numberFinish || + status == _error) { + --m_forwardPos; + break; + } + if(m_bufferSize <= m_forwardPos + 1) { + //status = _numberFinish; + break; + } + c = NextChar(m_buffer); + } + + if(data.size() == 0) { + return NULL; + } else { + CSSToken* token = new CSSToken; + m_tokenCache.insert(token); + token->type = NUMBER; + token->data = data; + return token; + } +} + +Lex::CSSToken *Lex::token() { + CSSToken *token = new CSSToken; + m_tokenCache.insert(token); + token->type = IDENT; + m_firstPos = m_forwardPos; + bool stopLoop = false; + std::string data; + static CSSDFAStatus STATUS; + if (m_firstPos >= m_bufferSize || !m_buffer) { + token->type = END; + return token; + } + while(1) { + char c = NextChar(m_buffer); + switch (STATUS) { + case Start: { + switch (c) { + case '@': { + CSSToken* identToken = Lex::identToken(); + stopLoop = true; + if(identToken->type == IDENT) { + STATUS = AtKeyWord; + data = identToken->data; + } else { + STATUS = LexError; + } + break; + } + case '#': { + CSSToken *identToken = Lex::identToken(); + stopLoop = true; + if(identToken->type == IDENT) { + STATUS = Hash; + data = identToken->data; + } else { + STATUS = LexError; + } + break; + } + case '~': { + if(NextChar(m_buffer) == EQUAL_SIGN) { + STATUS = include; + } else { + STATUS = tidle; + --m_forwardPos; + } + break; + } + case '|': { + if(NextChar(m_buffer) == EQUAL_SIGN) { + STATUS = dashMatch; + } else { + STATUS = LexError; + } + break; + } + case '^': { + if(NextChar(m_buffer) == EQUAL_SIGN) { + STATUS = prefixMatch; + } else { + STATUS = LexError; + } + break; + } + case '$': { + if(NextChar(m_buffer) == EQUAL_SIGN) { + STATUS = suffixMatch; + } else { + STATUS = LexError; + } + break; + } + case '*': { + if(NextChar(m_buffer) == EQUAL_SIGN) { + STATUS = subStringMatch; + } else { + --m_forwardPos; + STATUS = star; + } + break; + } + case '"': { + STATUS = string1Start; + break; + } + case '\'': { + STATUS = string2Start; + break; + } + case WS_CASE: { + STATUS = Ws; + break; + } + case '.': { + STATUS = dot; + stopLoop = true; + break; + } + case '{': { + STATUS = blockStart; + stopLoop = true; + break; + } + case '}': { + STATUS = blockEnd; + stopLoop = true; + break; + } + case ',': { + STATUS = comma; + stopLoop = true; + break; + } + case '>': { + STATUS = greater; + stopLoop = true; + break; + } + case '+': { + STATUS = plus; + stopLoop = true; + break; + } + case '[': { + STATUS = leftSqureBracket; + break; + } + case ']': { + STATUS = rightSqureBracket; + break; + } + case ':': { + STATUS = colon; + break; + } + case '=': { + STATUS = equal; + break; + } + case '/': { + char cn = NextChar(m_buffer); + if(cn == '*') { + STATUS = annotationStart; + } else { + --m_forwardPos; + goto identLabel; + } + break; + } + case ';' : { + STATUS = semicolon; + break; + } + case NUMBER_CASE: { + --m_forwardPos; + STATUS = numberStart; + break; + } + case ')': { + STATUS = rightBracket; + break; + } + case '-': { + STATUS = minus; + break; + } + + default: { + identLabel: + m_forwardPos = m_firstPos; + CSSToken* idToken = identToken(); + char next = NextChar(m_buffer); + if(next == '(') { + STATUS = function; + data = idToken->data; + } else { + --m_forwardPos; + if(idToken->type == IDENT) { + STATUS = iDent; + data = idToken->data; + } else if(idToken->type == END) { + STATUS = end; + } else { + STATUS = LexError; + } + } + stopLoop = true; + break; + } + } + break; + } + case Ws: { + stopLoop = true; + if (isWs(c)) { + STATUS = Ws; + stopLoop = false; + } else if (c == PLUS_SIGN) { + STATUS = plus; + } else if (c == COMMA_SIGN) { + STATUS = comma; + } else if (c == GREATER_SIGN) { + STATUS = greater; + } else if (c == TIDLE_SIGN) { + STATUS = tidle; + } else if ( c == '\0') { + STATUS = end; + } else { + --m_forwardPos; + STATUS = Ws; + } + break; + } + case include: { + stopLoop = true; + break; + } + case blockStart: { + if (c == BLOCK_END_SIGN) { + STATUS = blockEnd; + } + while (c != BLOCK_END_SIGN) { + if (m_forwardPos >= m_bufferSize) { + STATUS = LexError; + break; + } else { + c = NextChar(m_buffer); + STATUS = blockEnd; + } + } + data = createData(m_firstPos, m_forwardPos); + stopLoop = true; + break; + } + case string1Start: { + CSSToken *stringToken = textToken(1); + if (stringToken->type == STRING) { + STATUS = string1End; + data = stringToken->data; + } else { + STATUS = LexError; + } + break; + } + case string2Start: { + CSSToken *stringToken = textToken(2); + if (stringToken->type == STRING) { + STATUS = string2End; + data = stringToken->data; + } else { + STATUS = LexError; + } + break; + } + case annotationStart: { + char cn = NextChar(m_buffer); + if (cn == 0) {break;} + if (c == '*' && cn == '/') { + STATUS = annotationEnd; + } else { + --m_forwardPos; + continue; + } + break; + } + case numberStart: { + --m_forwardPos; + CSSToken *numberToken = Lex::numberToken(); + if (numberToken) { + data = numberToken->data; + STATUS = num; + } else { + STATUS = LexError; + } + break; + } + + default: { + stopLoop = true; + } + break; + } + bool resetStatus = true; + switch(STATUS) { + case AtKeyWord: { + token->type = ATKEYWORD; + token->data = data; + break; + } + case LexError: { + token->type = ERROR; + stopLoop = true; + break; + } + case Hash: { + token->type = HASH; + token->data = data; + break; + } + case Ws: { + token->type = WS; + break; + } + case include: { + token->type = INCLUDES; + stopLoop = true; + break; + } + case iDent: { + token->type = IDENT; + token->data = data; + break; + } + case dot: { + token->type = DOT; + break; + } + case blockStart: { + token->type = BLOCKSTART; + resetStatus = false; + break; + } + case blockEnd:{ + token->type = BLOCKEND; + token->data = data; + STATUS = Start; + break; + } + case end: { + token->type = END; + resetStatus = true; + break; + } + case comma: { + token->type = COMMA; + break; + } + case plus:{ + token->type = PLUS; + break; + } + case tidle: { + token->type = TIDLE; + stopLoop = true; + break; + } + case greater: { + token->type = GREATER; + stopLoop = true; + break; + } + case star:{ + token->type = STAR; + stopLoop = true; + break; + } + case dashMatch:{ + token->type = DASHMATCH; + stopLoop = true; + break; + } + case prefixMatch:{ + token->type = PREFIXMATCH; + stopLoop = true; + break; + } + case suffixMatch:{ + token->type = SUFFIXMATCH; + stopLoop = true; + break; + } + case includes:{ + token->type = INCLUDES; + stopLoop = true; + break; + } + case subStringMatch:{ + token->type = SUBSTRINGMATCH; + stopLoop = true; + break; + } + case leftSqureBracket: { + token->type = LEFTSQUREBRACKET; + stopLoop = true; + break; + } + case rightSqureBracket: { + token->type = RIGHTSQUREBRACKET; + stopLoop = true; + break; + } + case colon: { + token->type = COLON; + stopLoop = true; + break; + } + case equal: { + token->type = EQUAL; + stopLoop = true; + break; + } + case semicolon: { + token->type =SYNTAXEND; + stopLoop = true; + break; + } + case string1End: case string2End: { + token->type = STRING; + token->data = data; + stopLoop = true; + break; + } + case annotationEnd: { + token->type = ANNOTATION; + stopLoop = true; + break; + } + case function: { + token->type = FUNCTION; + token->data = data; + stopLoop = true; + break; + } + case numberStart: { + stopLoop = false; + break; + } + case num: { + token->data = data; + token->type = NUMBER; + stopLoop = true; + break; + } + case rightBracket: { + token->type = RIGHTBRACKET; + stopLoop = true; + break; + } + case minus: { + token->type = MINUS; + stopLoop = true; + break; + } + default: + break; + } + if(stopLoop) { + if(resetStatus) { + STATUS = Start; + } + break; + } + } + return token; +} + +std::string Lex::createData(size_t start, size_t end) { + if(m_buffer == NULL || start > end) { + return NULL; + } + + size_t size = end - start + 1; + if(m_bufferSize < end) { + return NULL; + } + + char* ptr = new char[size]; + ptr[size -1] = '\0'; + memmove(ptr, m_buffer + start, size - 1); + std::string ret = ptr; + delete [] ptr; + + return ret; +} + +void Lex::cleanResource() { + for(auto it : m_tokenCache) { + delete it; + } + + delete [] m_buffer; + m_buffer = 0; +} + +inline bool Lex::isDigitalCharacter(char c) { + return (c > 0x2F && c < 0x3A); +} + +inline bool Lex::isLetter(char c) { + return (c > 0x60 && c < 0x7B) || (c > 0x40 && c < 0x5B); +} + +inline bool Lex::isHexCharacter(char c) { + return isDigitalCharacter(c) || (c > 0x60 && c < 0x67) || (c > 0x40 && c < 0x47); +} + +inline bool Lex::isWs(char c) { + return (c == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t'); +} + diff --git a/modules/uikit/src/utils/cssparser.cpp b/modules/uikit/src/utils/cssparser.cpp new file mode 100644 index 000000000..52989f0b6 --- /dev/null +++ b/modules/uikit/src/utils/cssparser.cpp @@ -0,0 +1,767 @@ +#include "utils/cssparser.h" +#include "utils/csslex.h" +#include "utils/stringutil.h" + +#include "utils/signselector.h" +#include "utils/selectorgroup.h" +#include "utils/universalselector.h" +#include "utils/classselector.h" +#include "utils/idselector.h" +#include "utils/attributeselector.h" +#include "utils/typeselector.h" +#include "utils/combineselector.h" +#include "utils/selectorsequence.h" + +#include +#include + +CSSParser::ASTNode* TreeTranverseCreateExpressionAction(std::stack< + CSSParser::ASTNode *>* stack); +static void cleanASTTree(CSSParser::ASTNode *); + +#define PopOperand(left,right) ASTNode*left = operandStack.top();\ +operandStack.pop();\ +ASTNode*right = operandStack.top();\ +operandStack.pop() + +#define PopOperator(node) ASTNode* node = operatorStack.top();\ +operatorStack.pop(); + +CSSParser::CSSParser() { + m_lexer = new Lex; +} + +CSSParser::~CSSParser() { + clean(); +} + +template +static void eraseStack(std::stack& stack) { + std::stack st; + stack.swap(st); +} +template +static void cleanStack(std::stack& stack) { + while (!stack.empty()) { + auto topEle = stack.top(); + stack.pop(); + delete topEle; + } +} + +void CSSParser::prepareByString(const std::string &cssString) { + m_hostCssFile.clear(); + m_lexer->setBufferString(cssString); +} + +bool CSSParser::parseByString(const std::string &cssString) { + if (cssString.empty()) { + return false; + } + prepareByString(cssString); + return parse(); +} + +bool CSSParser::parse() { + cleanRes(); + std::stack syntaxStack; + Lex::CSSToken *token = m_lexer->token(); + m_status = START; + bool success = true; + while(token->type != END && token->type != ERROR && success) { + switch(m_status) { + case START: { + if (token->type == WS) { + m_status = START; + } else if (startSelector(token->type)) { + Selector* selector = getSelector(token); + if (!selector) { + return false; + } + syntaxStack.push(selector); + m_status = INSELECTOR; + } else if (token->type == ATKEYWORD) { + m_status = INATKEYWORD; + KeywordItem *keyword = new KeywordItem(token->data); + m_keywords.push_back(keyword); + } + break; + } + case INSELECTOR: { + if(token->type == WS) { + pushSign(syntaxStack, SignSelector::NormalInherit); + } else if (token->type == GREATER) { + pushSign(syntaxStack, SignSelector::Greater); + } else if (token->type == PLUS) { + pushSign(syntaxStack, SignSelector::Plus); + } else if (token->type == TIDLE) { + pushSign(syntaxStack, SignSelector::Tidle); + } else if (token->type == COMMA) { + pushSign(syntaxStack, SignSelector::Comma); + } else if (token->type == BLOCKSTART) { + m_status = STARTBLOCK; + } else { + Selector *s = getSelector(token); + if(s) { + if(!topHaveSign(syntaxStack) && syntaxStack.size()) { + pushSign(syntaxStack, SignSelector::Concat); + } + syntaxStack.push(s); + } else { + return false; + } + } + break; + } + case INATKEYWORD: { + if (token->type == BLOCKSTART) { + Lex::CSSToken *t = m_lexer->token(); + if (t->type == BLOCKEND) { + m_status = START; + KeywordItem *keyword = *--m_keywords.end(); + keyword->setData(t->data); + } + } else if (token->type == WS) { + break; + } else if (token->type == STRING) { + KeywordItem* keyword = *--m_keywords.end(); + keyword->setData(token->data); + Lex::CSSToken* t = m_lexer->token(); + if (t->type != SYNTAXEND) { + success = false; + } else { + m_status = START; + } + break; + } + break; + } + case STARTBLOCK: { + // create selector ATS + if (token->type == BLOCKEND) { + if (topHaveSign(syntaxStack)) { + syntaxStack.pop(); + } + std::list astContainer = createATS(syntaxStack); + eraseStack(syntaxStack); + std::list::iterator it = astContainer.begin(); + std::list::iterator end = astContainer.end(); + bool isGroupSelector = astContainer.size() > 1; + GroupSelector* group = isGroupSelector ? new GroupSelector : 0; + while (it != end) { + std::stack *result = new std::stack< + CSSParser::ASTNode *>; + MLRtranverseAST(*it, TreeTranverseCreateExpressionAction, + result); + ASTNode* node = result->top(); + if (isGroupSelector) { + group->addSelector(node->head); + } else { + node->head->setRuleData(StringUtil::trim(token->data, "{} ")); + node->head->setHostCSSFilePath(m_hostCssFile); + m_selectors.push_back(node->head); + } + delete result; + LRMtranverseAST(*it++, cleanASTTree); + } + if (isGroupSelector) { + m_selectors.push_back(group); + group->setRuleData(StringUtil::trim(token->data, "{} ")); + group->setHostCSSFilePath(m_hostCssFile); + } + m_status = START; + } else { + success = false; + } + + break; + } + default: + break; + } + if (!success) { + break; + } + token = m_lexer->token(); + } + m_lexer->cleanResource(); + return success; +} + +Selector *CSSParser::getSelector(Lex::CSSToken *token) { + Selector *selector = NULL; + switch (token->type) { + case STAR: { + selector = new UniversalSelector(); + break; + } + case DOT: { + Lex::CSSToken *t = m_lexer->token(); + if(t->type == IDENT) { + if(!t->data.empty()) { + ClassSelector* s = new ClassSelector(t->data); + selector = s; + } + } + break; + } + case HASH: { + if(!token->data.empty()) { + std::string& id = token->data; + id = id.substr(1, id.length() - 1); + IdSelector *s = new IdSelector(id); + selector = s; + } + break; + } + case LEFTSQUREBRACKET: { + Lex::CSSToken *t = m_lexer->token(); + enum { + _start = 0, + _key, + _sign, + _value, + _end, + _error + }; + + std::string key; + std::string value; + char _status = _start; + AttributeSelector::AttributeFilterRule rule = AttributeSelector::NoRule; + while(t->type != END || t->type != ERROR) { + switch(_status) { + case _start: { + if(t->type == WS) { + _status = _start; + } else if(t->type == IDENT) { + _status = _key; + if(!t->data.empty()) { + key = t->data; + } + } else { + _status = _error; + } + break; + } + case _key: { + _status = _sign; + if(t->type == WS) { + _status = _key; + } else if(t->type == INCLUDES) { + // ~= + rule = AttributeSelector::Include; + } else if(t->type == DASHMATCH) { + // |= + rule = AttributeSelector::DashMatch; + } else if(t->type == PREFIXMATCH) { + // ^= + rule = AttributeSelector::Prefix; + } else if(t->type == SUFFIXMATCH) { + // $= + rule = AttributeSelector::Suffix; + } else if(t->type == SUBSTRINGMATCH) { + // *= + rule = AttributeSelector::Substring; + } else if(t->type == RIGHTSQUREBRACKET) { + // ] + _status = _end; + } else if(t->type == EQUAL) { + // = + rule = AttributeSelector::Equal; + } else { + _status = _error; + } + break; + } + case _sign: { + if(t->type == WS) { + _status = _sign; + } else if(t->type == IDENT || t->type == STRING) { + _status = _value; + if(t->data.empty()) { + break; + } + value = t->data; + if(t->type == STRING) { + value = value.substr(1, value.length() - 2); + } + } else { + _status = _error; + } + break; + } + case _value: { + if(t->type == WS) { + _status = _value; + } else if (t->type == RIGHTSQUREBRACKET) { + _status = _end; + } else { + _status = _error; + } + break; + } + default: { + _status = _error; + } break; + } + if(_status == _error) { + break; + } else if(_status == _end) { + // generate selector + selector = new AttributeSelector(key, value, rule); + break; + } + t = m_lexer->token(); + } + break; + } + case IDENT: { + selector = new TypeSelector(token->data); + break; + } + case COLON: { + Lex::CSSToken *t = m_lexer->token(); + if(t->type == IDENT) { + selector = new PseudoSelector(t->data); + } else if(t->type == FUNCTION) { + PseudoSelector *s = new PseudoSelector(t->data); + PseudoSelector::Parameter *parameter = getFunctionParamenter(); + if(parameter) { + s->setParameter(parameter); + } + selector = s; + } + break; + } + default: break; + } + + return selector; +} + +PseudoSelector::Parameter* CSSParser::getFunctionParamenter() { +#define CleanRetAndStopLoop delete parameter; parameter = NULL; endLoop = true + enum { + _start, + _inNumber, + _inString, + _inIdent, + _inPolynomialLeft, + _inPolynomialRight, + _number, + _polynomial, + _error + }; + PseudoSelector::Parameter* parameter = new PseudoSelector::Parameter; + char state = _start; + bool endLoop = false; + Lex::CSSToken* token = m_lexer->token(); + while(1) { + switch(state) { + case _start: { + if (token->type == NUMBER) { + parameter->type = PseudoSelector::ParameterType::NUMBER; + parameter->pNumber = StringUtil::str2int(token->data); + state = _inNumber; + } else if(token->type == IDENT) { + if (token->data == "n" || token->data == "N") { + parameter->type = PseudoSelector::ParameterType::POLYNOMIAL; + parameter->polynomial.coefficient = 1; + state = _inPolynomialLeft; + } else { + parameter->type = PseudoSelector::ParameterType::IDENT; + state = _inIdent; + parameter->pString = token->data; + } + } else if(token->type == STRING) { + parameter->type = PseudoSelector::ParameterType::STRING; + state = _inString; + token->data.erase(token->data.begin()); + token->data.erase(token->data.end() - 1); + parameter->pString = token->data; + } else if(token->type == WS) { + state = _start; + break; + } else if(token->type == RIGHTBRACKET) { + CleanRetAndStopLoop + ; + } else if(token->type == PLUS || token->type == MINUS) { + Lex::CSSToken* t = m_lexer->token(); + int sign = token->type == PLUS ? 1 : -1; + if (t->type == NUMBER) { + parameter->type = PseudoSelector::ParameterType::NUMBER; + parameter->pNumber = StringUtil::str2int(t->data) * sign; + state = _inNumber; + } else if (t->type == IDENT && (t->data == "n" || t->data + == "N")) { + parameter->type = PseudoSelector::ParameterType::POLYNOMIAL; + parameter->polynomial.coefficient = sign; + state = _inPolynomialLeft; + } else { + CleanRetAndStopLoop; + state = _error; + } + } else { + CleanRetAndStopLoop; + state = _error; + } + break; + } + case _inString: + case _inIdent: { + if (token->type == WS) { + break; + } else if (token->type == RIGHTBRACKET) { + endLoop = true; + } else { + state = _error; + CleanRetAndStopLoop + ; + } + break; + } + case _inNumber: { + if (token->type == WS) { + state = _number; + } else if (token->type == IDENT) { + state = _inPolynomialLeft; + parameter->type = PseudoSelector::ParameterType::POLYNOMIAL; + parameter->polynomial.coefficient = parameter->pNumber; + parameter->pNumber = 0; + } else if (token->type == RIGHTBRACKET) { + endLoop = true; + state = _number; + } else { + CleanRetAndStopLoop; + state = _error; + } + break; + } + case _inPolynomialLeft: { + if(token->type == WS) { + state = _inPolynomialLeft; + } else if(token->type == PLUS) { + parameter->polynomial.sign = 1; + state = _inPolynomialRight; + } else if(token->type == MINUS) { + parameter->polynomial.sign = -1; + state = _inPolynomialRight; + } else if(token->type == RIGHTBRACKET) { + parameter->polynomial.sign = 1; + parameter->polynomial.constant = 0; + endLoop = true; + } else { + CleanRetAndStopLoop; + state = _error; + } + break; + } + case _inPolynomialRight: { + if(token->type == WS) { + break; + } else if(token->type == NUMBER) { + parameter->polynomial.constant = StringUtil::str2int(token->data); + state = _polynomial; + } else if(token->type == RIGHTBRACKET) { + endLoop = true; + state = _polynomial; + } else { + CleanRetAndStopLoop; + state = _error; + } + break; + } + case _number: + case _polynomial: { + if (token->type == WS) { + break; + } else if (token->type == RIGHTBRACKET) { + endLoop = true; + } else { + CleanRetAndStopLoop; + state = _error; + } + break; + } + case _error: { + return NULL; + } + default: { + CleanRetAndStopLoop; + state = _error; + break; + } + } + if (endLoop) { + break; + } + token = m_lexer->token(); + } + return parameter; +} + +std::list CSSParser::createATS(std::stack &syntax) { + std::stack operatorStack; + std::stack operandStack; + std::list atsCollection; + while (topHaveSign(syntax)) { + syntax.pop(); + } + if (!syntax.size()) { + return atsCollection; + } + while (syntax.size()) { + Selector* s = syntax.top(); + syntax.pop(); + SignSelector* newOperator = dynamic_cast (s); + if (!newOperator) { + pushOperatedElement(operandStack, s); + continue; + } + if(!operatorStack.size()) { + if (newOperator->signType() == SignSelector::Comma) { + atsCollection.push_back(operandStack.top()); + operandStack.pop(); + continue; + } + pushOperatedElement(operatorStack, s); + continue; + } + SignSelector* oldOperator = + dynamic_cast (operatorStack.top()->head); + if(newOperator->signType() == SignSelector::Comma) { + // close current ATS and put it in the list + buildReversePolishNotation(operatorStack, operandStack); + ASTNode* root = operandStack.top(); + operandStack.pop(); + atsCollection.push_back(root); + continue; + } + if(newOperator->signType() == SignSelector::Concat + && oldOperator->signType() != SignSelector::Concat) { + pushOperatedElement(operatorStack, s); + continue; + } + // create ATS + { + buildReversePolishNotation(operatorStack, operandStack); + pushOperatedElement(operatorStack, s); + } + } + if(operandStack.size() && operatorStack.size()) { + buildReversePolishNotation(operatorStack, operandStack); + } + atsCollection.push_back(operandStack.top()); + return atsCollection; +} + +void CSSParser::buildReversePolishNotation( + std::stack &operatorStack, + std::stack &operandStack) { + while (operatorStack.size()) { + PopOperand(leftNode, rightNode); + PopOperator(head); + initialASTNode(head, 0, leftNode, rightNode); + operandStack.push(head); + } +} + +void CSSParser::LMRtranverseAST(CSSParser::ASTNode *root, treeTranverseAction action) { + if(!root) { + return; + } + if(root->left) { + LMRtranverseAST(root->left, action); + } + action(root); + if(root->right) { + LMRtranverseAST(root->right, action); + } +} + +void CSSParser::RMLtranverseAST(CSSParser::ASTNode *root, treeTranverseAction action) { + if (!root) { + return; + } + if (root->right) { + RMLtranverseAST(root->right, action); + } + action(root); + if (root->left) { + RMLtranverseAST(root->left, action); + } +} + +void CSSParser::LRMtranverseAST(CSSParser::ASTNode *root, treeTranverseAction action) { + if(!root) { + return; + } + if(root->left) { + LRMtranverseAST(root->left, action); + } + if(root->right) { + LRMtranverseAST(root->right, action); + } + action(root); +} + +void CSSParser::MLRtranverseAST(ASTNode *root, treeTranverseWithUserDataAction action, void *userData) { + if(!root) { + return; + } + std::stack *oldStack = (std::stack *) userData; + std::stack *newStack = new std::stack; + newStack->push(root); + if(root->left) { + MLRtranverseAST(root->left, action, newStack); + } + if(root->right) { + MLRtranverseAST(root->right, action, newStack); + } + size_t size = newStack->size(); + if(size == 3) { + ASTNode* n = TreeTranverseCreateExpressionAction(newStack); + oldStack->push(n); + } else if(size == 1) { + oldStack->push(newStack->top()); + } + delete newStack; +} + +CSSParser::ASTNode *TreeTranverseCreateExpressionAction(std::stack *stack) { + typedef CSSParser::ASTNode ASTNode; + if(stack->size() != 3) { + return 0; + } + Selector *_right = stack->top()->head; + stack->pop(); + Selector *_left = stack->top()->head; + stack->pop(); + SignSelector *_operator = dynamic_cast (stack->top()->head); + stack->pop(); + + Selector *selector = 0; + + if(_operator) { + switch (_operator->signType()) { + case SignSelector::Plus: { + // + + CombineSelector *s = new CombineSelector; + s->initialInstanceSiblingList(_left, _right); + selector = s; + break; + } + case SignSelector::Tidle: { + // ~ + CombineSelector *s = new CombineSelector; + s->initialNormalSiblingList(_left, _right); + selector = s; + break; + } + case SignSelector::NormalInherit: { + // ' ' + CombineSelector *s = new CombineSelector; + s->initialNormalInhericalList(_left, _right); + selector = s; + break; + } + case SignSelector::Concat: { + SequenceSelector *s = new SequenceSelector; + s->appendSelector(_left); + s->appendSelector(_right); + selector = s; + break; + } + case SignSelector::Greater: { + // > + CombineSelector *s = new CombineSelector; + s->initialInstanceInhericalList(_left, _right); + selector = s; + break; + } + default: + break; + } + } + + ASTNode *node = new ASTNode; + node->head = selector; + return node; +} + +bool CSSParser::startSelector(CSSTokenType type) { + return (type == IDENT || type == DOT || type == HASH || type + == LEFTSQUREBRACKET || type == STAR || type == COLON); +} + +bool CSSParser::tokenHasInfo(CSSTokenType type) { + return (type == IDENT || type == HASH || type == STAR); +} + +bool CSSParser::topHaveSign(std::stack& stack) { + if (stack.empty()) { + return false; + } + Selector* topSelector = stack.top(); + return topSelector->type() == Selector::SignSelector; +} + +std::vector &CSSParser::selectors() { + return m_selectors; +} + +std::vector &CSSParser::keywords() { + return m_keywords; +} + +void CSSParser::pushSign(std::stack &stack, + SignSelector::SignType type) { + if (!stack.size()) { + return; + } + if (!topHaveSign(stack)) { + SignSelector* s = new SignSelector(type); + stack.push(s); + m_signSelecors.push_back(s); + } +} + +void CSSParser::pushOperatedElement(std::stack &stack, Selector *head) { + ASTNode *n = new ASTNode; + initialASTNode(n, head, 0, 0); + stack.push(n); +} + +void CSSParser::initialASTNode(ASTNode *target, Selector *head, ASTNode *left, ASTNode *right) { + if(head) { + target->head = head; + } + if(left) { + target->left = left; + } + if(right) { + target->right = right; + } +} + +void CSSParser::clean() { + cleanRes(); + if (m_lexer) { + delete m_lexer; + m_lexer = NULL; + } +} + +void CSSParser::cleanRes() { + m_selectors.clear(); + m_keywords.clear(); + m_signSelecors.clear(); +} + +static void cleanASTTree(CSSParser::ASTNode *node) { + node->left = NULL; + node->right = NULL; + node->head = NULL; + delete node; +} diff --git a/modules/uikit/src/utils/idselector.cpp b/modules/uikit/src/utils/idselector.cpp new file mode 100644 index 000000000..e4e2e0ab6 --- /dev/null +++ b/modules/uikit/src/utils/idselector.cpp @@ -0,0 +1,20 @@ +#include "utils/idselector.h" + +#include "components/widget.h" + +IdSelector::IdSelector(const std::string& id) { + m_id = id; + m_selectorType = Selector::IDSelector; +} + +bool IdSelector::isMeet(Widget *widget) { + return m_id == widget->name(); +} + +bool IdSelector::isBaseSelector() const { + return true; +} + +int IdSelector::weight() { + return 100; +} diff --git a/modules/uikit/src/utils/keyworditem.cpp b/modules/uikit/src/utils/keyworditem.cpp new file mode 100644 index 000000000..d925b2e95 --- /dev/null +++ b/modules/uikit/src/utils/keyworditem.cpp @@ -0,0 +1,5 @@ +#include "utils/keyworditem.h" + +KeywordItem::KeywordItem(const std::string& name) { + m_name = name; +} diff --git a/modules/uikit/src/utils/pseudoselector.cpp b/modules/uikit/src/utils/pseudoselector.cpp new file mode 100644 index 000000000..1a17d89bf --- /dev/null +++ b/modules/uikit/src/utils/pseudoselector.cpp @@ -0,0 +1,39 @@ +#include "utils/pseudoselector.h" + +PseudoSelector::PseudoSelector(const std::string& data) { + m_selectorType = Selector::PseudoSelector; + m_data = data; + m_parameter = NULL; +} + +PseudoSelector::~PseudoSelector() { + delete m_parameter; + m_parameter = NULL; +} + +bool PseudoSelector::isMeet(Widget *) { + return false; +} + +bool PseudoSelector::isBaseSelector() const { + return true; +} + +int PseudoSelector::weight() { + return 10; +} + +void PseudoSelector::setParameter(PseudoSelector::Parameter *p) { + if(m_parameter == p) { + return; + } + + if(m_parameter) { + delete m_parameter; + } + m_parameter = p; +} + +PseudoSelector::Parameter* PseudoSelector::parameter() { + return m_parameter; +} diff --git a/modules/uikit/src/utils/selector.cpp b/modules/uikit/src/utils/selector.cpp new file mode 100644 index 000000000..967d163bb --- /dev/null +++ b/modules/uikit/src/utils/selector.cpp @@ -0,0 +1,49 @@ +#include "utils/selector.h" + +#include "utils/stringutil.h" + +Selector::Selector() { +} + +Selector::~Selector() { +} + +inline const std::string &Selector::ruleData() const { + return m_ruleData; +} + +void Selector::setRuleData(const std::string &data) { + m_ruleData = data; + m_ruleDataMap.clear(); +} + +std::map &Selector::ruleDataMap() { + if(m_ruleDataMap.empty()) { + StringUtil::trim(m_ruleData); + StringUtil::deletechar(m_ruleData, '\n'); + auto keyValues = StringUtil::splitButSkipBrackets(m_ruleData, ';'); + + for(const auto &pair : keyValues) { + auto keyAndValue = StringUtil::splitButSkipBrackets(pair, ':'); + + if(keyAndValue.size() < 2) { + continue; + } + m_ruleDataMap[StringUtil::trim(keyAndValue[0])] = StringUtil::trim(keyAndValue[1]); + } + } + + return m_ruleDataMap; +} + +Selector::SelectorType Selector::type() { + return m_selectorType; +} + +const std::string &Selector::hostCSSFilePath() const { + return m_hostCSSFilePath; +} + +void Selector::setHostCSSFilePath(const std::string &path) { + m_hostCSSFilePath = path; +} diff --git a/modules/uikit/src/utils/selectorgroup.cpp b/modules/uikit/src/utils/selectorgroup.cpp new file mode 100644 index 000000000..01b29305d --- /dev/null +++ b/modules/uikit/src/utils/selectorgroup.cpp @@ -0,0 +1,45 @@ +#include "utils/selectorgroup.h" + +#include + +GroupSelector::GroupSelector() { + m_selectorType = Selector::SelectorGroup; +} + +GroupSelector::~GroupSelector() { + for(auto it : m_selectors) { + delete it; + } + m_selectors.clear(); +} + +void GroupSelector::addSelector(Selector *s) { + if(!s) { + return; + } + + m_selectors.push_back(s); +} + +bool GroupSelector::isMeet(Widget *widget) { + for(const auto &s : m_selectors) { + if(s->isMeet(widget)) { + targetSelector = (std::find(m_selectors.begin(), m_selectors.end(), s) - m_selectors.begin()); + + return true; + } + } + return false; +} + +bool GroupSelector::isBaseSelector() const { + return false; +} + +int GroupSelector::weight() { + if(m_selectors.size() < targetSelector + 1) { + return 0; + } + + return m_selectors[targetSelector]->weight(); +} diff --git a/modules/uikit/src/utils/selectorsequence.cpp b/modules/uikit/src/utils/selectorsequence.cpp new file mode 100644 index 000000000..6b0dadae4 --- /dev/null +++ b/modules/uikit/src/utils/selectorsequence.cpp @@ -0,0 +1,42 @@ +#include "utils/selectorsequence.h" + +SequenceSelector::SequenceSelector() { + m_selectorType = Selector::SelectorSequence; +} + +SequenceSelector::~SequenceSelector() { + for(auto it : m_selectors) { + delete it; + } +} + +void SequenceSelector::appendSelector(Selector *s) { + if(!s) { + return; + } + m_selectors.push_back(s); +} + +bool SequenceSelector::isMeet(Widget *widget) { + for(const auto &s : m_selectors) { + if(!s->isMeet(widget)) { + return false; + } + } + return true; +} + +bool SequenceSelector::isBaseSelector() const { + return false; +} + +int SequenceSelector::weight() { + auto it = m_selectors.begin(); + + int w = 0; + while(it != m_selectors.end()) { + w += (*it++)->weight(); + } + + return w; +} diff --git a/modules/uikit/src/utils/signselector.cpp b/modules/uikit/src/utils/signselector.cpp new file mode 100644 index 000000000..9248bd259 --- /dev/null +++ b/modules/uikit/src/utils/signselector.cpp @@ -0,0 +1,27 @@ +#include "utils/signselector.h" + +SignSelector::SignSelector(SignType type) { + m_signType = type; + m_selectorType = Selector::SignSelector; +} + +bool SignSelector::operator>(SignSelector *other) { + SignType otherType = other->signType(); + return m_signType == Concat && otherType != Concat; +} + +SignSelector::SignType SignSelector::signType() { + return m_signType; +} + +bool SignSelector::isMeet(Widget *) { + return false; +} + +bool SignSelector::isBaseSelector() const { + return true; +} + +int SignSelector::weight() { + return 0; +} diff --git a/modules/uikit/src/utils/stringutil.cpp b/modules/uikit/src/utils/stringutil.cpp new file mode 100644 index 000000000..5bf9a0f4c --- /dev/null +++ b/modules/uikit/src/utils/stringutil.cpp @@ -0,0 +1,153 @@ +#include "utils/stringutil.h" + +#include +#include +#include +#include + +StringUtil::StringUtil() { + +} + +StringUtil::~StringUtil() { +} + +const char* StringUtil::ws = " \t\n\r\f\v"; + +std::string StringUtil::longlong2str(long long in) { + std::stringstream stream; + stream << in; + return stream.str(); +} + +std::string StringUtil::int2str(int in) { + std::stringstream stream; + stream << in; + return stream.str(); +} + +int StringUtil::str2int(const std::string &in) { + int out = 0; + std::stringstream stream(in); + stream >> out; + return out; +} + +bool StringUtil::startWith(std::string &str, std::string &start) { + return str.compare(0, start.size(), start) == 0; +} + +bool StringUtil::endWith(std::string &str, std::string &end) { + return str.compare(str.size() - end.size(), end.size(), end) == 0; +} + +std::string StringUtil::tolower(const std::string &str) { + std::string ret(str); + std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); + return ret; +} + +std::string StringUtil::toupper(const std::string &str) { + std::string ret(str); + std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper); + return ret; +} + +bool StringUtil::contains(const std::string str1, const std::string str2) { + if (str1.find(str2) != std::string::npos) { + return true; + } + return false; +} + +std::string StringUtil::tostring(long long in) { + std::stringstream ss; + std::string ret; + ss << in; + ss >> ret; + return ret; +} + +void StringUtil::replace(std::string &srcStr, const std::string &findStr, + const std::string &replaceStr) { + std::size_t replaceStrLen = replaceStr.length(); + for (std::size_t pos = 0; pos != std::string::npos; pos += replaceStrLen) { + if ((pos = srcStr.find(findStr, pos)) != std::string::npos) { + srcStr.replace(pos, findStr.length(), replaceStr); + } else { + break; + } + } +} + +// trim from end of string (right) +std::string& StringUtil::rtrim(std::string& s, const char* t) +{ + s.erase(s.find_last_not_of(t) + 1); + return s; +} + +// trim from beginning of string (left) +std::string& StringUtil::ltrim(std::string& s, const char* t) +{ + s.erase(0, s.find_first_not_of(t)); + return s; +} + +// trim from both ends of string (right then left) +std::string& StringUtil::trim(std::string& s, const char* t) { + if (s.empty()) { return s; } + return rtrim(ltrim(s, t), t); +} + +std::vector StringUtil::split(const std::string& s, char seperator) { + std::vectorcontainer; + std::istringstream f(s); + std::istringstream& stream = f; + std::string out; + while(std::getline(stream, out, seperator)) { + container.push_back(out); + } + return container; +} + +std::vector StringUtil::splitButSkipBrackets(const std::string& s, char separator) { + std::vector container; + size_t length = s.length(); + size_t i = 0, start = 0; + bool insideBracket = false; + + for (; i < length; i++) { + if (s[i] == '(' || s[i] == '[' || s[i] == '{') { + insideBracket = true; + } + else if (s[i] == ')' || s[i] == ']' || s[i] == '}') { + insideBracket = false; + } + else if (s[i] == separator && !insideBracket) { + container.push_back(s.substr(start, i - start)); + start = i + 1; + } + } + + container.push_back(s.substr(start)); + return container; +} + +std::string StringUtil::join(std::vector& v, char separator) { + std::string s; + for(unsigned int i = 0; i < v.size(); i++) { + s += v[i]; + if(i >= (v.size() - 1)) { + break; // escaping in the last iteration + } + s += separator; // concatenating string + } + return s; +} + +std::string StringUtil::deletechar(const std::string &source, char target) { + std::string dest = source; + dest.erase(std::remove(dest.begin(), dest.end(), target)); + return dest; +} diff --git a/modules/uikit/src/utils/typeselector.cpp b/modules/uikit/src/utils/typeselector.cpp new file mode 100644 index 000000000..973025cc8 --- /dev/null +++ b/modules/uikit/src/utils/typeselector.cpp @@ -0,0 +1,24 @@ +#include "utils/typeselector.h" + +#include "components/widget.h" + +TypeSelector::TypeSelector(const std::string &typeName) { + m_typeName = typeName; + m_selectorType = Selector::TypeSelector; +} + +std::string TypeSelector::tagName() { + return m_typeName; +} + +bool TypeSelector::isMeet(Widget *widget) { + return widget->typeName() == m_typeName; +} + +bool TypeSelector::isBaseSelector() const { + return true; +} + +int TypeSelector::weight() { + return 1; +} diff --git a/modules/uikit/src/utils/universalselector.cpp b/modules/uikit/src/utils/universalselector.cpp new file mode 100644 index 000000000..0f2491e81 --- /dev/null +++ b/modules/uikit/src/utils/universalselector.cpp @@ -0,0 +1,17 @@ +#include "utils/universalselector.h" + +UniversalSelector::UniversalSelector() { + m_selectorType = Selector::UniversalSelector; +} + +bool UniversalSelector::isMeet(Widget *widget) { + return true; +} + +bool UniversalSelector::isBaseSelector() const { + return true; +} + +int UniversalSelector::weight() { + return 0; +} diff --git a/modules/uikit/uikit.qbs b/modules/uikit/uikit.qbs index 62aeb1f00..45b673c69 100644 --- a/modules/uikit/uikit.qbs +++ b/modules/uikit/uikit.qbs @@ -7,10 +7,13 @@ Project { "src/components/*.cpp", "src/pipelinetasks/*.cpp", "src/resources/*.cpp", + "src/utils/*.cpp", + "../../thirdparty/pugixml/src/*.cpp" ] property stringList incPaths: [ "includes", + "includes/resources", "../../common", "../../engine/includes", "../../engine/includes/resources", @@ -19,9 +22,8 @@ Project { "../../thirdparty/next/inc", "../../thirdparty/next/inc/math", "../../thirdparty/next/inc/core", - "../../thirdparty/openal/include", - "../../thirdparty/libogg/src", - "../../thirdparty/libvorbis/src" + "../../thirdparty/pugixml/src", + "../../thirdparty/cssparser/include", ] DynamicLibrary { @@ -31,16 +33,25 @@ Project { var sources = srcFiles sources.push("src/converters/*.cpp") sources.push("includes/converters/*.h") + sources.push("src/editor/**/*.cpp") + sources.push("src/editor/**/*.h") + sources.push("src/editor/**/*.ui") + sources.push("src/editor/**/*.qrc") return sources } Depends { name: "cpp" } Depends { name: "bundle" } Depends { name: "next-editor" } Depends { name: "engine-editor" } - Depends { name: "Qt"; submodules: ["core", "gui"]; } + Depends { name: "Qt"; submodules: ["core", "gui", "widgets"]; } bundle.isBundle: false - cpp.defines: ["SHARED_DEFINE", "ENGINE_LIBRARY"] + cpp.defines: { + var result = uikit.defines + result.push("SHARED_DEFINE") + result.push("UIKIT_LIBRARY") + return result + } cpp.includePaths: uikit.incPaths cpp.cxxLanguageVersion: uikit.languageVersion cpp.cxxStandardLibrary: uikit.standardLibrary @@ -98,10 +109,6 @@ Project { cpp.defines: ["THUNDER_MOBILE"] } - Properties { - condition: qbs.targetOS.contains("windows") - } - Properties { condition: qbs.targetOS.contains("android") Android.ndk.appStl: uikit.ANDROID_STL diff --git a/worldeditor/bin/engine/materials/DefaultFont.shader b/worldeditor/bin/engine/materials/DefaultFont.shader index 6370e6849..49a051b29 100644 --- a/worldeditor/bin/engine/materials/DefaultFont.shader +++ b/worldeditor/bin/engine/materials/DefaultFont.shader @@ -1,6 +1,7 @@ + @@ -13,6 +14,8 @@ layout(binding = UNIFORM) uniform Uniforms { vec4 clipRect; + + float weight; } uni; layout(location = 0) in vec3 vertex; @@ -37,6 +40,8 @@ void main(void) { layout(binding = UNIFORM) uniform Uniforms { vec4 clipRect; + + float weight; } uni; layout(binding = UNIFORM + 1) uniform sampler2D texture0; @@ -46,20 +51,12 @@ layout(location = 1) in vec4 _color; layout(location = 0) out vec4 color; void main() { - float thickness = 0.5; - float softness = 0.02; - - float oulineThickness = 0.6; - float oulineSoftness = 0.02; - - float a = texture(texture0, _uvMask.xy).x; - float outline = smoothstep(oulineThickness - oulineSoftness, oulineThickness + oulineSoftness, a); - a = smoothstep(1.0 - thickness - softness, 1.0 - thickness + softness, a); + float softness = 0.02f; - vec2 m = clamp(uni.clipRect.zw - uni.clipRect.xy - abs(_uvMask.zw), 0.0, 1.0); - //a *= 1.0 - step(m.x * m.y, 0.0); + float sdf = texture(texture0, _uvMask.xy).x; + float mask = smoothstep(1.0f - uni.weight - softness, 1.0f - uni.weight + softness, sdf); - color = vec4(l.color.xyz, a); + color = vec4(l.color.xyz, mask); } ]]> diff --git a/worldeditor/bin/engine/materials/Frame.shader b/worldeditor/bin/engine/materials/Frame.shader index facd5f623..455604462 100644 --- a/worldeditor/bin/engine/materials/Frame.shader +++ b/worldeditor/bin/engine/materials/Frame.shader @@ -1,12 +1,13 @@ - - + + + - + 0.5; bool rightHalf = _uv0.x > 0.5; - float cornerRad; float ratio = dFdy(_uv0.y) / dFdx(_uv0.x); vec2 uvSDF = _uv0; uvSDF.x *= ratio; + float borderRad; + float borderWidth = uni.borderWidth.x; if(rightHalf) { uvSDF.x = ratio - uvSDF.x; } + if(upperHalf) { uvSDF.y = 1.0 - uvSDF.y; - cornerRad = rightHalf ? uni.cornerRadius.z : uni.cornerRadius.w; + borderRad = rightHalf ? uni.borderRadius.y : uni.borderRadius.x; } else { - cornerRad = rightHalf ? uni.cornerRadius.y : uni.cornerRadius.x; + borderRad = rightHalf ? uni.borderRadius.z : uni.borderRadius.w; } - vec2 radSDF = uvSDF - cornerRad; + vec2 radSDF = uvSDF - borderRad; float innerSDF = max(0.0, min(radSDF.x, radSDF.y)); float outerSDF = -length(min(radSDF, 0.0)); - float sdf = innerSDF + outerSDF + cornerRad; + float sdf = innerSDF + outerSDF + borderRad; const float softness = 0.0; const float margin = 0.0; float surface = smoothstep(margin - softness, margin + softness, sdf); // Border - float width = (uni.borderWidth > 0.0) ? max(fwidth(_uv0.y), uni.borderWidth) : 0.0; + float width = (borderWidth > 0.0) ? max(fwidth(_uv0.y), borderWidth) : 0.0; float border = surface - smoothstep(margin, margin, sdf - width); float brd0 = step(1.0, ratio * _uv0.x + _uv0.y); @@ -78,7 +78,7 @@ void main(void) { vec4 borderColor = mix(mix(mix(uni.bottomColor, uni.leftColor, left), uni.rightColor, right), uni.topColor, top); - rgb = mix(vec4(uni.frameColor.xyz, uni.frameColor.w * surface), borderColor, border); + rgb = mix(vec4(uni.backgroundColor.xyz, uni.backgroundColor.w * surface), borderColor, border); } ]]>