├── src ├── examples │ ├── assets │ │ ├── .gitignore │ │ ├── add.svg │ │ ├── test.md │ │ ├── check_box_blank.svg │ │ ├── delete.svg │ │ ├── check_box.svg │ │ └── include.h │ ├── basic │ │ └── main.cpp │ ├── crud │ │ └── main.cpp │ ├── counter │ │ ├── main.cpp │ │ └── Counter.h │ ├── text │ │ ├── main.cpp │ │ └── TextDemo.h │ ├── button │ │ ├── main.cpp │ │ └── ButtonDemo.h │ ├── flex │ │ ├── main.cpp │ │ └── FlexDemo.h │ ├── tempconv │ │ ├── main.cpp │ │ └── TempConv.h │ ├── elementor.h │ ├── todo │ │ └── main.cpp │ └── main.cpp ├── library │ ├── platforms │ │ └── gl │ │ │ ├── elementor.h │ │ │ ├── include.h │ │ │ ├── GLClipboard.h │ │ │ ├── GLDisplay.h │ │ │ ├── utility.h │ │ │ ├── GLPerfomance.h │ │ │ ├── GLPlatformContext.h │ │ │ ├── GLFontManager.h │ │ │ ├── GLCursor.h │ │ │ ├── GLEventLoop.h │ │ │ ├── GLPlatform.h │ │ │ ├── utility.cpp │ │ │ └── GLPlatform.cpp │ ├── include.h │ ├── WithChild.h │ ├── elements │ │ ├── Flexible.cpp │ │ ├── include.h │ │ ├── Background.cpp │ │ ├── FitCover.cpp │ │ ├── Stack.cpp │ │ ├── Image.cpp │ │ ├── Focusable.cpp │ │ ├── Ratio.cpp │ │ ├── Hoverable.cpp │ │ ├── Height.cpp │ │ ├── FitContain.cpp │ │ ├── Width.cpp │ │ ├── Ratio.h │ │ ├── FitContain.h │ │ ├── Rounded.cpp │ │ ├── Stack.h │ │ ├── FitCover.h │ │ ├── Align.cpp │ │ ├── Width.h │ │ ├── Flexible.h │ │ ├── Height.h │ │ ├── Row.h │ │ ├── Column.h │ │ ├── Clickable.cpp │ │ ├── Column.cpp │ │ ├── Row.cpp │ │ ├── SVG.cpp │ │ ├── Padding.cpp │ │ ├── SVG.h │ │ ├── ParagraphPlaceholder.cpp │ │ ├── Hoverable.h │ │ ├── Background.h │ │ ├── Clickable.h │ │ ├── Image.h │ │ ├── Rounded.h │ │ ├── Padding.h │ │ ├── Focusable.h │ │ ├── Draggable.cpp │ │ ├── ParagraphPlaceholder.h │ │ ├── Border.cpp │ │ ├── Scrollable.cpp │ │ ├── Flex.h │ │ ├── Draggable.h │ │ ├── Border.h │ │ ├── Paragraph.h │ │ └── Scrollable.h │ ├── debug.h │ ├── utility.h │ ├── WithChildren.h │ ├── ApplicationHoverState.h │ ├── Component.h │ ├── ApplicationFocusState.h │ ├── Element.h │ ├── ApplicationHoverState.cpp │ ├── Render.cpp │ ├── utility.cpp │ ├── ApplicationContext.h │ ├── Render.h │ └── ApplicationTree.h └── components │ ├── elementor.h │ ├── include.h │ ├── utility.h │ ├── utility.cpp │ ├── FPSLabel.h │ ├── Slot.h │ ├── Outline.cpp │ ├── Outline.h │ ├── Eventable.h │ ├── TextButton.h │ ├── Cursorable.h │ ├── Scrollbar.h │ ├── IconButton.h │ ├── ClickableOutside.h │ └── Button.h ├── tests ├── .gitignore ├── screenshots │ ├── basic.png │ ├── crud.png │ ├── flex.png │ ├── text.png │ ├── todo.png │ ├── button.png │ ├── counter.png │ └── tempconv.png ├── compare_screenshots.py └── make_screenshots.cpp ├── .gitignore ├── clear.py ├── test.py ├── LICENSE ├── environment.yml ├── xmake.lua ├── third_party └── skia-build.lua └── README.md /src/examples/assets/.gitignore: -------------------------------------------------------------------------------- 1 | todo.md -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | screenshots_diff/ 2 | screenshots_new/ 3 | __pycache__/ 4 | -------------------------------------------------------------------------------- /tests/screenshots/basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/basic.png -------------------------------------------------------------------------------- /tests/screenshots/crud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/crud.png -------------------------------------------------------------------------------- /tests/screenshots/flex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/flex.png -------------------------------------------------------------------------------- /tests/screenshots/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/text.png -------------------------------------------------------------------------------- /tests/screenshots/todo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/todo.png -------------------------------------------------------------------------------- /tests/screenshots/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/button.png -------------------------------------------------------------------------------- /tests/screenshots/counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/counter.png -------------------------------------------------------------------------------- /tests/screenshots/tempconv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noartem/elementor/HEAD/tests/screenshots/tempconv.png -------------------------------------------------------------------------------- /src/examples/assets/add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/examples/assets/test.md: -------------------------------------------------------------------------------- 1 | - [x] Забрать посылку на почте 2 | - [ ] Сходить подстричься 3 | - [x] Найти тот самый фильм, про который мне говорили 4 | - [x] Надо что-нибудь приготовить... 5 | - [x] daadda 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xmake cache 2 | .xmake/ 3 | build/ 4 | 5 | # MacOS Cache 6 | .DS_Store 7 | 8 | Makefile 9 | src/examples/assets/todo.md 10 | 11 | CMakeLists.txt 12 | cmake-build-debug/ 13 | .vscode/ 14 | .idea/ -------------------------------------------------------------------------------- /clear.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | shutil.rmtree("build") 5 | shutil.rmtree(".xmake") 6 | shutil.rmtree("tests/screenshots_diff") 7 | shutil.rmtree("tests/screenshots_new") 8 | os.remove("CMakeLists.txt") 9 | -------------------------------------------------------------------------------- /src/library/platforms/gl/elementor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_ELEMENTOR_H 6 | #define ELEMENTOR_ELEMENTOR_H 7 | 8 | #include "../../include.h" 9 | 10 | #endif //ELEMENTOR_ELEMENTOR_H 11 | -------------------------------------------------------------------------------- /src/examples/assets/check_box_blank.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/examples/assets/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/examples/assets/check_box.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/elementor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 04.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_ELEMENTOR_H 6 | #define ELEMENTOR_COMPONENTS_ELEMENTOR_H 7 | 8 | #include "../library/include.h" 9 | #include "../library/elements/include.h" 10 | 11 | using namespace elementor; 12 | using namespace elementor::elements; 13 | 14 | #endif //ELEMENTOR_COMPONENTS_ELEMENTOR_H 15 | -------------------------------------------------------------------------------- /src/library/platforms/gl/include.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_INCLUDE_H 6 | #define ELEMENTOR_GL_INCLUDE_H 7 | 8 | #include "GLClipboard.h" 9 | #include "GLCursor.h" 10 | #include "GLFontManager.h" 11 | #include "GLDisplay.h" 12 | #include "GLPerfomance.h" 13 | #include "GLPlatform.h" 14 | #include "GLWindow.h" 15 | #include "utility.h" 16 | 17 | #endif //ELEMENTOR_GL_INCLUDE_H 18 | -------------------------------------------------------------------------------- /src/examples/basic/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #include "./Basic.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: Basic"); 12 | window->setSize({ 420, 320 }); 13 | window->setMinSize({ 240, 180 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | Basic::New(window) 18 | ); 19 | 20 | platform->run(); 21 | } -------------------------------------------------------------------------------- /src/examples/crud/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #include "./Crud.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: Counter"); 12 | window->setSize({ 512, 256 }); 13 | window->setMinSize({ 512, 256 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | Crud::New(window) 18 | ); 19 | 20 | platform->run(); 21 | } -------------------------------------------------------------------------------- /src/examples/counter/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #include "./Counter.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: Counter"); 12 | window->setSize({ 224, 64 }); 13 | window->setMinSize({ 160, 64 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | Counter::New(window) 18 | ); 19 | 20 | platform->run(); 21 | } -------------------------------------------------------------------------------- /src/examples/text/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 08.05.2024. 3 | // 4 | 5 | #include "./TextDemo.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: Text"); 12 | window->setSize({ 420, 320 }); 13 | window->setMinSize({ 240, 180 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | TextDemo::New(window) 18 | ); 19 | 20 | platform->run(); 21 | } -------------------------------------------------------------------------------- /src/examples/button/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 08.05.2024. 3 | // 4 | 5 | #include "./ButtonDemo.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: Button"); 12 | window->setSize({ 420, 320 }); 13 | window->setMinSize({ 240, 180 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | ButtonDemo::New(window) 18 | ); 19 | 20 | platform->run(); 21 | } -------------------------------------------------------------------------------- /src/examples/flex/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 08.06.2024. 3 | // 4 | 5 | #include "./FlexDemo.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: Flexbox Layout"); 12 | window->setSize({ 420, 320 }); 13 | window->setMinSize({ 420, 320 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | FlexDemo::New(window) 18 | ); 19 | 20 | platform->run(); 21 | } -------------------------------------------------------------------------------- /src/examples/tempconv/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #include "./TempConv.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: Temperature Converter"); 12 | window->setSize({ 384, 64 }); 13 | window->setMinSize({ 384, 64 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | TempConv::New(window) 18 | ); 19 | 20 | platform->run(); 21 | } -------------------------------------------------------------------------------- /src/components/include.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 04.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_INCLUDE_H 6 | #define ELEMENTOR_COMPONENTS_INCLUDE_H 7 | 8 | #include "Button.h" 9 | #include "ClickableOutside.h" 10 | #include "Cursorable.h" 11 | #include "Eventable.h" 12 | #include "FPSLabel.h" 13 | #include "IconButton.h" 14 | #include "Outline.h" 15 | #include "Scrollbar.h" 16 | #include "Slot.h" 17 | #include "TextButton.h" 18 | #include "TextInput.h" 19 | 20 | #endif //ELEMENTOR_COMPONENTS_INCLUDE_H 21 | -------------------------------------------------------------------------------- /src/examples/elementor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.02.2024. 3 | // 4 | 5 | #ifndef EXAMPLES_ELEMENTOR_H 6 | #define EXAMPLES_ELEMENTOR_H 7 | 8 | #include "../library/include.h" 9 | #include "../library/platforms/gl/include.h" 10 | #include "../library/elements/include.h" 11 | #include "../components/include.h" 12 | 13 | using namespace elementor; 14 | using namespace elementor::platforms::gl; 15 | using namespace elementor::elements; 16 | using namespace elementor::components; 17 | 18 | #endif //EXAMPLES_ELEMENTOR_H 19 | -------------------------------------------------------------------------------- /src/components/utility.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 04.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_UTILITY_H 6 | #define ELEMENTOR_COMPONENTS_UTILITY_H 7 | 8 | #include 9 | #include 10 | 11 | namespace elementor::components { 12 | void openURL(std::string url); 13 | 14 | tm now_tm(); 15 | 16 | std::string leftPadAndFit(const std::string& value, unsigned int size, const char paddingChar); 17 | 18 | int daysInMonth(int month, int year); 19 | } 20 | 21 | #endif //ELEMENTOR_COMPONENTS_UTILITY_H 22 | -------------------------------------------------------------------------------- /src/examples/assets/include.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by admin on 17.02.2024. 3 | // 4 | 5 | #ifndef ASSETS_INCLUDE_H 6 | #define ASSETS_INCLUDE_H 7 | 8 | #include 9 | #include 10 | 11 | std::string asset(const std::string_view& name) { 12 | return std::filesystem::current_path() 13 | .append("..") 14 | .append("..") 15 | .append("..") 16 | .append("..") 17 | .append("src") 18 | .append("examples") 19 | .append("assets") 20 | .append(name) 21 | .string(); 22 | } 23 | 24 | #endif //ASSETS_INCLUDE_H 25 | -------------------------------------------------------------------------------- /src/examples/todo/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.02.2024. 3 | // 4 | 5 | #include "./Todo.h" 6 | 7 | int main() { 8 | auto platform = std::make_shared(); 9 | 10 | auto window = std::make_shared(platform); 11 | window->setTitle("Elementor: TODO"); 12 | window->setSize({ 320, 420 }); 13 | window->setMinSize({ 240, 320 }); 14 | platform->addWindow(window); 15 | 16 | window->setRoot( 17 | Todo::New(window, { 18 | .filename = asset("todo.md"), 19 | }) 20 | ); 21 | 22 | platform->run(); 23 | } -------------------------------------------------------------------------------- /src/library/include.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 29.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_INCLUDE_H 6 | #define ELEMENTOR_INCLUDE_H 7 | 8 | #include "ApplicationTree.h" 9 | #include "ApplicationContext.h" 10 | #include "Component.h" 11 | #include "Element.h" 12 | #include "Event.h" 13 | #include "ApplicationFocusState.h" 14 | #include "ApplicationHoverState.h" 15 | #include "Render.h" 16 | #include "utility.h" 17 | #include "WithChild.h" 18 | #include "WithChildren.h" 19 | #include "debug.h" 20 | 21 | #endif //ELEMENTOR_INCLUDE_H 22 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import os 2 | from os import path 3 | import subprocess 4 | 5 | ctx = os.getcwd() 6 | os.chdir(path.dirname(__file__)) 7 | 8 | 9 | def sh(args): 10 | if isinstance(args, str): 11 | args = args.split(" ") 12 | 13 | process = subprocess.Popen(args) 14 | process.wait() 15 | 16 | 17 | if not path.isdir("./tests/screenshots_new"): 18 | os.mkdir("./tests/screenshots_new") 19 | 20 | sh("xmake") 21 | sh("xmake run make-screenshots") 22 | 23 | os.chdir("tests") 24 | sh("python compare_screenshots.py") 25 | os.chdir("..") 26 | 27 | os.chdir(ctx) 28 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLClipboard.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_CLIPBOARD_H 6 | #define ELEMENTOR_GL_CLIPBOARD_H 7 | 8 | #include "elementor.h" 9 | 10 | #include "GLFW/glfw3.h" 11 | 12 | namespace elementor::platforms::gl { 13 | class GLClipboard : public Clipboard { 14 | public: 15 | void set(const std::string_view& value) override { 16 | glfwSetClipboardString(nullptr, value.data()); 17 | } 18 | 19 | const std::string_view& get() override { 20 | return glfwGetClipboardString(nullptr); 21 | } 22 | }; 23 | }; 24 | 25 | 26 | #endif //ELEMENTOR_GL_CLIPBOARD_H 27 | -------------------------------------------------------------------------------- /src/library/WithChild.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_WITH_CHILD_H 6 | #define ELEMENTOR_WITH_CHILD_H 7 | 8 | #include 9 | 10 | #include "Element.h" 11 | 12 | namespace elementor { 13 | class WithChild { 14 | public: 15 | [[nodiscard]] auto getChild() const { 16 | return child; 17 | } 18 | 19 | [[nodiscard]] bool hasChild() const { 20 | return child != nullptr; 21 | } 22 | 23 | [[nodiscard]] bool doesNotHaveChild() const { 24 | return child == nullptr; 25 | } 26 | 27 | protected: 28 | std::shared_ptr child; 29 | }; 30 | } 31 | 32 | #endif //ELEMENTOR_WITH_CHILD_H 33 | -------------------------------------------------------------------------------- /src/library/elements/Flexible.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Flexible.h" 6 | 7 | namespace elementor::elements { 8 | Size Flexible::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | return child->getSize(boundaries); 14 | } 15 | 16 | std::vector Flexible::getChildren(const ElementRect& rect) { 17 | if (doesNotHaveChild()) { 18 | return {}; 19 | } 20 | 21 | Rect childRect = { 22 | .size = rect.size, 23 | .position = { 0, 0 } 24 | }; 25 | ElementWithRect childElement(child, childRect); 26 | return { childElement }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/library/debug.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.01.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_DEBUG_H 6 | #define ELEMENTOR_DEBUG_H 7 | 8 | #include 9 | 10 | #ifndef LOG 11 | #define LOG(x) std::cout << __TIME__ << " | " << __FILE__ << ":" << __LINE__ << " | " << x << std::endl 12 | #endif 13 | 14 | #ifndef PRINT 15 | #define PRINT(x) LOG(#x"=" << x) 16 | #endif 17 | 18 | #ifdef DEBUG 19 | #define D(x) x 20 | #else 21 | #define D(x) 22 | #endif 23 | 24 | #ifndef D_PRINT 25 | #define D_PRINT(x) D(PRINT(x)) 26 | #endif 27 | 28 | #ifndef D_LOG 29 | #define D_LOG(x) D(LOG(x)) 30 | #endif 31 | 32 | #ifndef D_IF_LOG 33 | #define D_IF_LOG(c, x) if (c) D(LOG(x)) 34 | #endif 35 | 36 | #endif //ELEMENTOR_DEBUG_H 37 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLDisplay.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_DISPLAY_H 6 | #define ELEMENTOR_GL_DISPLAY_H 7 | 8 | #include "elementor.h" 9 | 10 | #include "GLFW/glfw3.h" 11 | 12 | #include "utility.h" 13 | 14 | namespace elementor::platforms::gl { 15 | class GLDisplay : public Display { 16 | public: 17 | GLDisplay(GLFWmonitor* glMonitor) { 18 | size = getMonitorSize(glMonitor); 19 | physicalSize = getMonitorPhysicalSize(glMonitor); 20 | } 21 | 22 | Size getSize() override { 23 | return size; 24 | } 25 | 26 | Size getPhysicalSize() override { 27 | return physicalSize; 28 | } 29 | 30 | private: 31 | Size size; 32 | Size physicalSize; 33 | }; 34 | }; 35 | 36 | #endif //ELEMENTOR_GL_DISPLAY_H 37 | -------------------------------------------------------------------------------- /src/library/platforms/gl/utility.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_UTILITY_H 6 | #define ELEMENTOR_GL_UTILITY_H 7 | 8 | #include "../../Render.h" 9 | 10 | #include "GLFW/glfw3.h" 11 | 12 | namespace elementor::platforms::gl { 13 | Size getWindowSize(GLFWwindow* window); 14 | 15 | Position getWindowPosition(GLFWwindow* window); 16 | 17 | Rect getWindowRect(GLFWwindow* window); 18 | 19 | Size getMonitorSize(GLFWmonitor* monitor); 20 | 21 | Size getMonitorPhysicalSize(GLFWmonitor* monitor); 22 | 23 | Position getMonitorPosition(GLFWmonitor* monitor); 24 | 25 | Rect getMonitorRect(GLFWmonitor* monitor); 26 | 27 | GLFWmonitor* getWindowMonitor(GLFWwindow* window); 28 | }; 29 | 30 | 31 | #endif //ELEMENTOR_GL_UTILITY_H 32 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLPerfomance.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_PERFOMANCE_H 6 | #define ELEMENTOR_GL_PERFOMANCE_H 7 | 8 | #include "elementor.h" 9 | 10 | #include "GLFW/glfw3.h" 11 | 12 | namespace elementor::platforms::gl { 13 | class GLPerfomance : public Perfomance { 14 | public: 15 | double getFPS() override { 16 | return lastFPS; 17 | } 18 | 19 | void incrementFramesCount() { 20 | framesCount++; 21 | 22 | double now = glfwGetTime(); 23 | if ((now - framesLastTime) >= 1) { 24 | lastFPS = framesCount; 25 | framesLastTime = now; 26 | framesCount = 0; 27 | } 28 | } 29 | 30 | private: 31 | double framesLastTime; 32 | double framesCount; 33 | double lastFPS; 34 | }; 35 | }; 36 | 37 | #endif //ELEMENTOR_GL_PERFOMANCE_H 38 | -------------------------------------------------------------------------------- /src/library/elements/include.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 02.06.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_ELEMENTS_INCLUDE_H 6 | #define ELEMENTOR_ELEMENTS_INCLUDE_H 7 | 8 | #include "Align.h" 9 | #include "Background.h" 10 | #include "Border.h" 11 | #include "Clickable.h" 12 | #include "Column.h" 13 | #include "Draggable.h" 14 | #include "FitContain.h" 15 | #include "FitCover.h" 16 | #include "Flex.h" 17 | #include "Flexible.h" 18 | #include "Focusable.h" 19 | #include "Height.h" 20 | #include "Hoverable.h" 21 | #include "Image.h" 22 | #include "Padding.h" 23 | #include "Paragraph.h" 24 | #include "ParagraphPlaceholder.h" 25 | #include "Ratio.h" 26 | #include "Rounded.h" 27 | #include "Row.h" 28 | #include "Scrollable.h" 29 | #include "Stack.h" 30 | #include "SVG.h" 31 | #include "Text.h" 32 | #include "Width.h" 33 | 34 | #endif //ELEMENTOR_ELEMENTS_INCLUDE_H 35 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLPlatformContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 05.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_PLATFORM_CONTEXT_H 6 | #define ELEMENTOR_GL_PLATFORM_CONTEXT_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "elementor.h" 15 | 16 | namespace elementor { 17 | class GLPlatformContext { 18 | public: 19 | [[nodiscard]] virtual std::string getLocale() const = 0; 20 | 21 | virtual void setLocale(std::string locale) = 0; 22 | 23 | [[nodiscard]] virtual std::shared_ptr getClipboard() const = 0; 24 | 25 | [[nodiscard]] virtual std::shared_ptr getPerfomance() const = 0; 26 | 27 | [[nodiscard]] virtual sk_sp getSkFontManager() const = 0; 28 | 29 | virtual void requestNextFrame(const std::function& callback) = 0; 30 | 31 | virtual void requestNextFrame() = 0; 32 | }; 33 | } 34 | 35 | #endif //ELEMENTOR_GL_PLATFORM_CONTEXT_H 36 | -------------------------------------------------------------------------------- /src/library/utility.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 07.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_UTILITY_H 6 | #define ELEMENTOR_UTILITY_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace elementor { 16 | inline std::string toUTF8(const std::u32string_view& source) { 17 | std::wstring_convert, char32_t> convertor; 18 | return convertor.to_bytes(source.data()); 19 | } 20 | 21 | inline std::u32string fromUTF8(const std::string_view& source) { 22 | std::wstring_convert, char32_t> convertor; 23 | return convertor.from_bytes(source.data()); 24 | } 25 | 26 | SkColor makeSkColorFromRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a); 27 | 28 | SkColor makeSkColorFromRGB(uint8_t r, uint8_t g, uint8_t b); 29 | 30 | SkColor makeSkColorFromHex(std::string hex); 31 | }; 32 | 33 | 34 | #endif //ELEMENTOR_UTILITY_H 35 | -------------------------------------------------------------------------------- /src/library/elements/Background.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Background.h" 6 | 7 | namespace elementor::elements { 8 | Size Background::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | return getChild()->getSize(boundaries); 14 | } 15 | 16 | void Background::paintBackground(SkCanvas* canvas, const ElementRect& rect) { 17 | SkPaint paint; 18 | paint.setColor(color); 19 | 20 | SkRect skRect = SkRect::MakeXYWH(0, 0, rect.size.width, rect.size.height); 21 | canvas->drawRect(skRect, paint); 22 | } 23 | 24 | std::vector Background::getChildren(const ElementRect& rect) { 25 | if (doesNotHaveChild()) { 26 | return {}; 27 | } 28 | 29 | Rect childRect = { 30 | .size = rect.size, 31 | .position = { .x = 0.0f, .y = 0.0f }, 32 | }; 33 | 34 | ElementWithRect childElementWithRect(child, childRect); 35 | return { childElementWithRect }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/library/WithChildren.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_WITH_CHILDREN_H 6 | #define ELEMENTOR_WITH_CHILDREN_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Element.h" 12 | 13 | namespace elementor { 14 | class WithChildren { 15 | public: 16 | [[nodiscard]] std::vector> getChildrenList() const { 17 | return children; 18 | } 19 | 20 | [[nodiscard]] size_t getChildrenSize() const { 21 | return children.size(); 22 | } 23 | 24 | [[nodiscard]] std::shared_ptr getChild(int i) const { 25 | return children[i]; 26 | } 27 | 28 | [[nodiscard]] int childIndex(const std::shared_ptr& child) const { 29 | for (int i = 0; i < children.size(); i++) { 30 | if (children[i] == child) { 31 | return i; 32 | } 33 | } 34 | return -1; 35 | } 36 | 37 | protected: 38 | std::vector> children; 39 | }; 40 | 41 | } 42 | 43 | #endif //ELEMENTOR_WITH_CHILDREN_H 44 | -------------------------------------------------------------------------------- /src/library/elements/FitCover.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 27.07.2022. 3 | // 4 | 5 | #include "FitCover.h" 6 | 7 | #include 8 | 9 | namespace elementor::elements { 10 | std::vector FitCover::getChildren(const ElementRect& rect) { 11 | if (doesNotHaveChild()) { 12 | return {}; 13 | } 14 | 15 | Size realChildSize = child->getSize(InfiniteBoundaries); 16 | 17 | float ratio = realChildSize.width / realChildSize.height; 18 | Size childSize{ 19 | .width = rect.size.width > rect.size.height ? rect.size.width : rect.size.height / ratio, 20 | .height = rect.size.width > rect.size.height ? rect.size.width / ratio : rect.size.height, 21 | }; 22 | 23 | Position childPosition = { 24 | .x = (rect.size.width - childSize.width) / 2, 25 | .y = (rect.size.height - childSize.height) / 2 26 | }; 27 | 28 | Rect childRect = { .size = childSize, .position = childPosition }; 29 | 30 | ElementWithRect childElement(child, childRect); 31 | return { childElement }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLFontManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_FONT_MANAGER_H 6 | #define ELEMENTOR_GL_FONT_MANAGER_H 7 | 8 | #include "elementor.h" 9 | 10 | #include 11 | 12 | namespace elementor::platforms::gl { 13 | class GLFontManager { 14 | public: 15 | GLFontManager() { 16 | skFontManager = sk_make_sp(); 17 | } 18 | 19 | [[nodiscard]] sk_sp getSkFontManager() const { 20 | return skFontManager; 21 | } 22 | 23 | void registerFontFromSkData(sk_sp data) { 24 | skFontManager->registerTypeface(SkTypeface::MakeFromData(std::move(data))); 25 | } 26 | 27 | void registerFontFromPath(const std::string& path) { 28 | registerFontFromSkData(SkData::MakeFromFileName(path.c_str())); 29 | } 30 | 31 | private: 32 | sk_sp skFontManager; 33 | }; 34 | }; 35 | 36 | 37 | #endif //ELEMENTOR_GL_FONT_MANAGER_H 38 | -------------------------------------------------------------------------------- /src/library/ApplicationHoverState.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 06.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_APPLICATION_HOVER_STATE_H 6 | #define ELEMENTOR_APPLICATION_HOVER_STATE_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Event.h" 12 | #include "ApplicationContext.h" 13 | #include "ApplicationTree.h" 14 | 15 | namespace elementor { 16 | class ApplicationHoverState { 17 | public: 18 | ApplicationHoverState( 19 | const std::shared_ptr& applicationTree, 20 | const std::shared_ptr& cursor 21 | ) 22 | : applicationTree(applicationTree), cursor(cursor) { 23 | } 24 | 25 | void tick(); 26 | 27 | EventCallbackResponse onEvent(const std::shared_ptr& event); 28 | 29 | private: 30 | std::shared_ptr applicationTree; 31 | std::shared_ptr cursor; 32 | 33 | std::shared_ptr hoveredNode = nullptr; 34 | 35 | void setHoveredNode(const std::shared_ptr& newHoveredNode); 36 | }; 37 | } 38 | 39 | #endif //ELEMENTOR_APPLICATION_HOVER_STATE_H 40 | -------------------------------------------------------------------------------- /src/library/elements/Stack.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 03.02.2022. 3 | // 4 | 5 | #include "Stack.h" 6 | 7 | namespace elementor::elements { 8 | Size Stack::getSize(const Boundaries& boundaries) { 9 | Size size{ 0, 0 }; 10 | for (const auto& child: children) { 11 | Size childSize = child->getSize(boundaries); 12 | size.width = std::max(size.height, childSize.width); 13 | size.height = std::max(size.height, childSize.height); 14 | } 15 | return fitSizeInBoundaries(size, boundaries); 16 | } 17 | 18 | std::vector Stack::getChildren(const ElementRect& rect) { 19 | std::vector childrenElements; 20 | 21 | Boundaries childBoundaries = { 22 | .min = ZeroSize, 23 | .max = rect.size 24 | }; 25 | 26 | for (const auto& child: children) { 27 | Size childSize = child->getSize(childBoundaries); 28 | 29 | Rect childRect = { 30 | .size = childSize, 31 | .position = { .x = 0, .y = 0 } 32 | }; 33 | 34 | ElementWithRect childElement(child, childRect); 35 | childrenElements.push_back(childElement); 36 | } 37 | 38 | return childrenElements; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/library/elements/Image.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 26.07.2022. 3 | // 4 | 5 | #include "Image.h" 6 | 7 | #include 8 | 9 | namespace elementor::elements { 10 | sk_sp Image::makeImageFromFile(const std::string& path) { 11 | sk_sp encodedData = SkData::MakeFromFileName(path.c_str()); 12 | return SkImage::MakeFromEncoded(encodedData); 13 | } 14 | 15 | void Image::paintBackground(SkCanvas* canvas, const ElementRect& rect) { 16 | if (skImage == nullptr) { 17 | return; 18 | } 19 | 20 | SkRect skRect = SkRect::MakeXYWH(0, 0, rect.size.width, rect.size.height); 21 | canvas->drawImageRect(skImage, skRect, samplingOptions); 22 | } 23 | 24 | Size Image::getSize(const Boundaries& boundaries) { 25 | if (skImage == nullptr) { 26 | return boundaries.min; 27 | } 28 | 29 | float pixelScale = ctx->getPixelScale(); 30 | float imageWidthScaled = skImage->width() * pixelScale; 31 | float imageHeightScaled = skImage->height() * pixelScale; 32 | 33 | Size size = { imageWidthScaled, imageHeightScaled }; 34 | size = fitSizeInBoundaries(size, boundaries); 35 | 36 | return size; 37 | } 38 | } -------------------------------------------------------------------------------- /src/library/Component.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 02.01.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENT_H 6 | #define ELEMENTOR_COMPONENT_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Render.h" 12 | #include "Element.h" 13 | 14 | namespace elementor::elements { 15 | class Component : public Element { 16 | public: 17 | explicit Component(const std::shared_ptr& ctx) 18 | : Element(ctx) { 19 | } 20 | 21 | Size getSize(const Boundaries& boundaries) override { 22 | if (element == nullptr) { 23 | return boundaries.max; 24 | } 25 | 26 | return element->getSize(boundaries); 27 | } 28 | 29 | std::vector getChildren(const ElementRect& rect) override { 30 | if (element == nullptr) { 31 | return {}; 32 | } 33 | 34 | Rect elementRect = { 35 | .size = rect.size, 36 | .position = { .x = 0, .y = 0 }, 37 | }; 38 | ElementWithRect elementWithRect(this->element, elementRect); 39 | return { elementWithRect }; 40 | } 41 | 42 | protected: 43 | std::shared_ptr element = nullptr; 44 | }; 45 | } 46 | 47 | #endif //ELEMENTOR_COMPONENT_H 48 | -------------------------------------------------------------------------------- /src/library/elements/Focusable.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.05.2022. 3 | // 4 | 5 | #include "Focusable.h" 6 | 7 | namespace elementor::elements { 8 | Size Focusable::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | return child->getSize(boundaries); 14 | } 15 | 16 | std::vector Focusable::getChildren(const ElementRect& rect) { 17 | if (doesNotHaveChild()) { 18 | return {}; 19 | } 20 | 21 | Rect childRect = { 22 | .size = rect.size, 23 | .position = { .x = 0, .y = 0 }, 24 | }; 25 | 26 | ElementWithRect childElement(child, childRect); 27 | return { childElement }; 28 | } 29 | 30 | std::vector > Focusable::getEventsHandlers() { 31 | return { 32 | FocusEvent::Handle([this](const auto& event) { 33 | focused = event->focused; 34 | 35 | pendingBlur = false; 36 | pendingFocus = false; 37 | 38 | if (callbackFocusChange.has_value()) { 39 | callbackFocusChange.value()(event->focused); 40 | } 41 | 42 | return EventCallbackResponse::None; 43 | }) 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/library/elements/Ratio.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Ratio.h" 6 | 7 | namespace elementor::elements { 8 | Size fitRatioWithin(float ratio, const Size& size) { 9 | if (ratio >= 1) { 10 | return { 11 | .width = size.width, 12 | .height = size.width / ratio 13 | }; 14 | } else { 15 | return { 16 | .width = size.height * ratio, 17 | .height = size.height 18 | }; 19 | } 20 | } 21 | 22 | Size Ratio::getSize(const Boundaries& boundaries) { 23 | return fitSizeInBoundaries(fitRatioWithin(ratio, boundaries.max), boundaries); 24 | } 25 | 26 | std::vector Ratio::getChildren(const ElementRect& rect) { 27 | if (doesNotHaveChild()) { 28 | return {}; 29 | } 30 | 31 | Size childSize = fitRatioWithin(ratio, rect.size); 32 | 33 | Position childPosition = { 34 | .x = (rect.size.width - childSize.width) / 2, 35 | .y = (rect.size.height - childSize.height) / 2 36 | }; 37 | 38 | Rect childRect = { .size = childSize, .position = childPosition }; 39 | 40 | ElementWithRect childElement(child, childRect); 41 | return { childElement }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/library/elements/Hoverable.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.05.2022. 3 | // 4 | 5 | #include "Hoverable.h" 6 | 7 | namespace elementor::elements { 8 | Size Hoverable::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | return child->getSize(boundaries); 14 | } 15 | 16 | std::vector Hoverable::getChildren(const ElementRect& rect) { 17 | if (doesNotHaveChild()) { 18 | return {}; 19 | } 20 | 21 | Rect childRect = { 22 | .size = rect.size, 23 | .position = { .x = 0, .y = 0 }, 24 | }; 25 | 26 | ElementWithRect childElement(child, childRect); 27 | return { childElement }; 28 | } 29 | 30 | std::vector > Hoverable::getEventsHandlers() { 31 | return { 32 | HoverEvent::Handle([this](const auto& event) { 33 | if (callbackLeave && hovered && !event->hovered) { 34 | hovered = false; 35 | return callbackLeave(); 36 | } 37 | 38 | if (callbackEnter && !hovered && event->hovered) { 39 | hovered = true; 40 | return callbackEnter(); 41 | } 42 | 43 | hovered = event->hovered; 44 | return EventCallbackResponse::None; 45 | }) 46 | }; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/library/elements/Height.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Height.h" 6 | 7 | namespace elementor::elements { 8 | Size Height::getSize(const Boundaries& boundaries) { 9 | float pixelScale = ctx->getPixelScale(); 10 | float heightScaled = height * pixelScale; 11 | 12 | if (doesNotHaveChild()) { 13 | return fitSizeInBoundaries({ 14 | .width = boundaries.max.width, 15 | .height = heightScaled 16 | }, boundaries); 17 | } 18 | 19 | Boundaries childBoundaries = { 20 | .min = { 21 | .width = boundaries.min.width, 22 | .height = std::max(heightScaled, boundaries.min.height) 23 | }, 24 | .max = { 25 | .width = boundaries.max.width, 26 | .height = std::max(std::min(heightScaled, boundaries.max.height), boundaries.min.height) 27 | } 28 | }; 29 | 30 | return child->getSize(childBoundaries); 31 | } 32 | 33 | std::vector Height::getChildren(const ElementRect& rect) { 34 | if (doesNotHaveChild()) { 35 | return {}; 36 | } 37 | 38 | Rect childRect = { 39 | .size = rect.size, 40 | .position = { .x = 0, .y = 0 }, 41 | }; 42 | 43 | ElementWithRect childElement(child, childRect); 44 | return { childElement }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/library/elements/FitContain.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 27.07.2022. 3 | // 4 | 5 | #include "FitContain.h" 6 | 7 | #include 8 | 9 | namespace elementor::elements { 10 | Size fitSizeWithin(const Size& inner, const Size& outer) { 11 | float innerRatio = inner.width / inner.height; 12 | float outerRatio = outer.width / outer.height; 13 | float resizeFactor = (innerRatio >= outerRatio) 14 | ? (outer.width / inner.width) 15 | : (outer.height / inner.height); 16 | 17 | return { 18 | .width = inner.width * resizeFactor, 19 | .height = inner.height * resizeFactor 20 | }; 21 | } 22 | 23 | std::vector FitContain::getChildren(const ElementRect& rect) { 24 | if (doesNotHaveChild()) { 25 | return {}; 26 | } 27 | 28 | Size realChildSize = child->getSize(InfiniteBoundaries); 29 | Size childSize = fitSizeWithin(realChildSize, rect.size); 30 | 31 | Position childPosition = { 32 | .x = (rect.size.width - childSize.width) / 2, 33 | .y = (rect.size.height - childSize.height) / 2 34 | }; 35 | 36 | Rect childRect = { .size = childSize, .position = childPosition }; 37 | 38 | ElementWithRect childElement(child, childRect); 39 | return { childElement }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/library/elements/Width.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Width.h" 6 | 7 | namespace elementor::elements { 8 | Size Width::getSize(const Boundaries& boundaries) { 9 | float pixelScale = ctx->getPixelScale(); 10 | float widthScaled = width * pixelScale; 11 | 12 | if (doesNotHaveChild()) { 13 | return fitSizeInBoundaries({ 14 | .width = std::max(widthScaled, boundaries.min.width), 15 | .height = boundaries.max.height 16 | }, boundaries); 17 | } 18 | 19 | Boundaries childBoundaries = { 20 | .min = { 21 | .width = std::max(widthScaled, boundaries.min.width), 22 | .height = boundaries.min.height, 23 | }, 24 | .max = { 25 | .width = std::max(std::min(widthScaled, boundaries.max.width), boundaries.min.width), 26 | .height = boundaries.max.height, 27 | } 28 | }; 29 | 30 | return child->getSize(childBoundaries); 31 | } 32 | 33 | std::vector Width::getChildren(const ElementRect& rect) { 34 | if (doesNotHaveChild()) { 35 | return {}; 36 | } 37 | 38 | Rect childRect = { 39 | .size = rect.size, 40 | .position = { .x = 0, .y = 0 }, 41 | }; 42 | 43 | ElementWithRect childElement(child, childRect); 44 | return { childElement }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/utility.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 04.02.2023. 3 | // 4 | 5 | #include "utility.h" 6 | 7 | namespace elementor::components { 8 | void openURL(std::string url) { 9 | #if defined(OS_HOST_WINDOWS) 10 | std::string command = "start " + url; 11 | #elif defined(OS_HOST_LINUX) 12 | std::string command = "xdg-open " + url; 13 | #elif defined(OS_HOST_MACOS) 14 | std::string command = "open " + url; 15 | #endif 16 | 17 | system(command.c_str()); 18 | } 19 | 20 | tm now_tm() { 21 | time_t now = time(nullptr); 22 | return *localtime(&now); 23 | } 24 | 25 | std::string leftPadAndFit(const std::string& value, unsigned int size, const char paddingChar) { 26 | if (value.length() == size) { 27 | return value; 28 | } else if (value.length() > size) { 29 | return value.substr(value.length() - size); 30 | } else { 31 | return std::string(size - value.length(), paddingChar) + value; 32 | } 33 | } 34 | 35 | int daysInMonth(int month, int year) { 36 | if (month == 4 || month == 6 || month == 9 || month == 11) { 37 | return 30; 38 | } else if (month == 2) { 39 | if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { 40 | return 29; 41 | } else { 42 | return 28; 43 | } 44 | } else { 45 | return 31; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/library/ApplicationFocusState.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 06.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_APPLICATION_FOCUS_STATE_H 6 | #define ELEMENTOR_APPLICATION_FOCUS_STATE_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Event.h" 13 | #include "ApplicationContext.h" 14 | #include "ApplicationTree.h" 15 | 16 | #include "elements/Focusable.h" 17 | 18 | namespace elementor { 19 | class ApplicationFocusState { 20 | public: 21 | explicit ApplicationFocusState(const std::shared_ptr& applicationTree) 22 | : applicationTree(applicationTree) { 23 | } 24 | 25 | void tick(); 26 | 27 | EventCallbackResponse onEvent(const std::shared_ptr& event); 28 | 29 | private: 30 | std::shared_ptr applicationTree; 31 | 32 | std::vector> focusableNodes = {}; 33 | std::shared_ptr focusedNode = nullptr; 34 | 35 | void setFocusedNode(const std::shared_ptr& newFocusedNode); 36 | 37 | int getFocusedNodeIndex(); 38 | 39 | void focusIthNode(int i); 40 | 41 | void focusNextNode(); 42 | 43 | void focusPreviousNode(); 44 | 45 | void clearFocusedNodeIfRemoved(); 46 | 47 | void clearFocusedNodeIfPendingBlur(); 48 | }; 49 | } 50 | 51 | #endif //ELEMENTOR_APPLICATION_FOCUS_STATE_H 52 | -------------------------------------------------------------------------------- /src/library/elements/Ratio.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 29.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_RATIO_H 6 | #define ELEMENTOR_RATIO_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct RatioProps { 12 | float ratio = 0; 13 | const std::shared_ptr & child = nullptr; 14 | }; 15 | 16 | class Ratio : public Element, public WithChild { 17 | public: 18 | Ratio(const std::shared_ptr & ctx, const RatioProps& props) 19 | : Element(ctx) { 20 | setRatio(props.ratio); 21 | setChild(props.child); 22 | } 23 | 24 | static std::shared_ptr New( 25 | const std::shared_ptr & ctx, 26 | const RatioProps& props 27 | ) { 28 | return std::make_shared(ctx, props); 29 | } 30 | 31 | static std::shared_ptr New(const std::shared_ptr & ctx) { 32 | return New(ctx, {}); 33 | } 34 | 35 | void setRatio(float newValue) { 36 | markChanged(); 37 | ratio = newValue; 38 | } 39 | 40 | [[nodiscard]] float getRatio() const { 41 | return ratio; 42 | } 43 | 44 | void setChild(const std::shared_ptr & newChild) { 45 | markChanged(); 46 | child = newChild; 47 | } 48 | 49 | Size getSize(const Boundaries& boundaries) override; 50 | 51 | std::vector getChildren(const ElementRect& rect) override; 52 | 53 | private: 54 | float ratio = 0; 55 | }; 56 | } 57 | 58 | 59 | #endif //ELEMENTOR_RATIO_H 60 | -------------------------------------------------------------------------------- /src/library/elements/FitContain.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 27.07.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_FIT_CONTAIN_H 6 | #define ELEMENTOR_FIT_CONTAIN_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct FitContainProps { 12 | const std::shared_ptr & child = nullptr; 13 | }; 14 | 15 | class FitContain : public Element, public WithChild { 16 | public: 17 | FitContain(const std::shared_ptr & ctx, const FitContainProps& props) 18 | : Element(ctx) { 19 | setChild(props.child); 20 | } 21 | 22 | static std::shared_ptr New( 23 | const std::shared_ptr & ctx, 24 | const FitContainProps& props 25 | ) { 26 | return std::make_shared(ctx, props); 27 | } 28 | 29 | static std::shared_ptr New( 30 | const std::shared_ptr & ctx, 31 | std::shared_ptr & elementRef, 32 | const FitContainProps& props 33 | ) { 34 | auto element = New(ctx, props); 35 | elementRef = element; 36 | return element; 37 | } 38 | 39 | static std::shared_ptr New(const std::shared_ptr & ctx) { 40 | return New(ctx, {}); 41 | } 42 | 43 | void setChild(const std::shared_ptr & newChild) { 44 | markChanged(); 45 | child = newChild; 46 | } 47 | 48 | std::vector getChildren(const ElementRect& rect) override; 49 | }; 50 | } 51 | 52 | #endif //ELEMENTOR_FIT_CONTAIN_H 53 | -------------------------------------------------------------------------------- /src/library/elements/Rounded.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 21.04.2022. 3 | // 4 | 5 | #include "Rounded.h" 6 | 7 | #include 8 | 9 | namespace elementor::elements { 10 | void Rounded::paintBackground(SkCanvas* canvas, const ElementRect& rect) { 11 | auto pixelScale = ctx->getPixelScale(); 12 | 13 | float topLeft = rectRadius.topLeft * pixelScale; 14 | float topRight = rectRadius.topRight * pixelScale; 15 | float bottomRight = rectRadius.bottomRight * pixelScale; 16 | float bottomLeft = rectRadius.bottomLeft * pixelScale; 17 | 18 | SkVector corners[] = {{ topLeft, topLeft }, 19 | { topRight, topRight }, 20 | { bottomRight, bottomRight }, 21 | { bottomLeft, bottomLeft }}; 22 | 23 | SkRRect skRRect; 24 | SkRect skRect = SkRect::MakeXYWH(0, 0, rect.size.width, rect.size.height); 25 | skRRect.setRectRadii(skRect, corners); 26 | 27 | canvas->clipRRect(skRRect, SkClipOp::kIntersect, true); 28 | } 29 | 30 | Size Rounded::getSize(const Boundaries& boundaries) { 31 | if (doesNotHaveChild()) { 32 | return boundaries.max; 33 | } 34 | 35 | return child->getSize(boundaries); 36 | } 37 | 38 | std::vector Rounded::getChildren(const ElementRect& rect) { 39 | if (doesNotHaveChild()) { 40 | return {}; 41 | } 42 | 43 | Rect childRect = { 44 | .size = rect.size, 45 | .position = { .x=0, .y=0 } 46 | }; 47 | ElementWithRect childElement(child, childRect); 48 | return { childElement }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/library/elements/Stack.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 03.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_STACK_H 6 | #define ELEMENTOR_STACK_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct StackProps { 12 | const std::vector >& children = {}; 13 | }; 14 | 15 | class Stack : public Element, public WithChildren { 16 | public: 17 | Stack(const std::shared_ptr & ctx, const StackProps& props) 18 | : Element(ctx) { 19 | setChildren(props.children); 20 | } 21 | 22 | static std::shared_ptr New( 23 | const std::shared_ptr & ctx, 24 | const StackProps& props 25 | ) { 26 | return std::make_shared(ctx, props); 27 | } 28 | 29 | static std::shared_ptr New( 30 | const std::shared_ptr & ctx, 31 | std::shared_ptr & elementRef, 32 | const StackProps& props 33 | ) { 34 | auto element = New(ctx, props); 35 | elementRef = element; 36 | return element; 37 | } 38 | 39 | static std::shared_ptr New(const std::shared_ptr & ctx) { 40 | return New(ctx, {}); 41 | } 42 | 43 | void setChildren(const std::vector >& newChildren) { 44 | markChanged(); 45 | children = newChildren; 46 | } 47 | 48 | Size getSize(const Boundaries& boundaries) override; 49 | 50 | std::vector getChildren(const ElementRect& rect) override; 51 | }; 52 | } 53 | 54 | 55 | #endif //ELEMENTOR_STACK_H 56 | -------------------------------------------------------------------------------- /src/library/elements/FitCover.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 27.07.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_FIT_COVER_H 6 | #define ELEMENTOR_FIT_COVER_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct FitCoverProps { 12 | const std::shared_ptr& child = nullptr; 13 | }; 14 | 15 | class FitCover : public Element, public WithChild { 16 | public: 17 | FitCover(const std::shared_ptr& ctx, const FitCoverProps& props) 18 | : Element(ctx) { 19 | setChild(props.child); 20 | } 21 | 22 | static std::shared_ptr New( 23 | const std::shared_ptr& ctx, 24 | const FitCoverProps& props 25 | ) { 26 | return std::make_shared(ctx, props); 27 | } 28 | 29 | static std::shared_ptr New( 30 | const std::shared_ptr & ctx, 31 | std::shared_ptr & elementRef, 32 | const FitCoverProps& props 33 | ) { 34 | auto element = New(ctx, props); 35 | elementRef = element; 36 | return element; 37 | } 38 | 39 | static std::shared_ptr New(const std::shared_ptr& ctx) { 40 | return New(ctx, {}); 41 | } 42 | 43 | void setChild(const std::shared_ptr & newChild) { 44 | markChanged(); 45 | child = newChild; 46 | } 47 | 48 | std::vector getChildren(const ElementRect& rect) override; 49 | 50 | ClipBehavior getClipBehaviour() override { 51 | return ClipBehavior::AntiAlias; 52 | } 53 | }; 54 | } 55 | 56 | #endif //ELEMENTOR_FIT_COVER_H 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Artem Noskov 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the 13 | distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/library/Element.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_ELEMENT_H 6 | #define ELEMENTOR_ELEMENT_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "Render.h" 17 | #include "ApplicationContext.h" 18 | 19 | namespace elementor { 20 | enum class ClipBehavior { 21 | None, 22 | Hard, 23 | AntiAlias, 24 | }; 25 | 26 | class Element { 27 | public: 28 | explicit Element(const std::shared_ptr& ctx) 29 | : ctx(ctx) { 30 | } 31 | 32 | virtual ~Element() = default; 33 | 34 | virtual Size getSize(const Boundaries& boundaries) { 35 | return boundaries.max; 36 | } 37 | 38 | virtual void paintBackground(SkCanvas* canvas, const ElementRect& rect) { 39 | } 40 | 41 | virtual std::vector&, Rect>> getChildren( 42 | const ElementRect& rect 43 | ) { 44 | return {}; 45 | } 46 | 47 | virtual ClipBehavior getClipBehaviour() { 48 | return ClipBehavior::None; 49 | } 50 | 51 | virtual bool popChanged() { 52 | if (changed) { 53 | changed = false; 54 | return true; 55 | } else { 56 | return false; 57 | } 58 | } 59 | 60 | protected: 61 | std::shared_ptr ctx; 62 | bool changed = true; 63 | 64 | virtual void markChanged() { 65 | changed = true; 66 | } 67 | }; 68 | 69 | using ElementWithRect = std::tuple&, Rect>; 70 | } 71 | 72 | #endif //ELEMENTOR_ELEMENT_H 73 | -------------------------------------------------------------------------------- /src/library/elements/Align.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 29.05.2022. 3 | // 4 | 5 | #include "Align.h" 6 | 7 | namespace elementor::elements { 8 | Size Align::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | Boundaries childBoundaries = { 14 | .min = { 15 | widthCoefficient.has_value() ? 0 : boundaries.min.width, 16 | heightCoefficient.has_value() ? 0 : boundaries.min.height, 17 | }, 18 | .max = boundaries.max 19 | }; 20 | 21 | Size childSize = child->getSize(childBoundaries); 22 | Size elementSize = fitSizeInBoundaries(childSize, boundaries); 23 | 24 | return elementSize; 25 | } 26 | 27 | std::vector Align::getChildren(const ElementRect& rect) { 28 | if (doesNotHaveChild()) { 29 | return {}; 30 | } 31 | 32 | Boundaries childBoundaries = { 33 | .min = { 34 | .width = widthCoefficient.has_value() ? 0 : rect.size.width, 35 | .height = heightCoefficient.has_value() ? 0 : rect.size.height, 36 | }, 37 | .max = rect.size, 38 | }; 39 | Size childSize = child->getSize(childBoundaries); 40 | 41 | Position childPosition = { 42 | .x = rect.size.width * widthCoefficient.value_or(0) - 43 | childSize.width * widthChildCoefficient.value_or(widthCoefficient.value_or(0)), 44 | .y = rect.size.height * heightCoefficient.value_or(0) - 45 | childSize.height * heightChildCoefficient.value_or(heightCoefficient.value_or(0)) 46 | }; 47 | 48 | Rect childRect = { .size = childSize, .position = childPosition }; 49 | 50 | ElementWithRect childElement(child, childRect); 51 | return { childElement }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/library/ApplicationHoverState.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 06.02.2024. 3 | // 4 | 5 | #include "ApplicationHoverState.h" 6 | 7 | namespace elementor { 8 | void ApplicationHoverState::tick() { 9 | auto cursorPosition = cursor->getPosition(); 10 | if (cursorPosition == InvalidPosition) { 11 | setHoveredNode(nullptr); 12 | return; 13 | } 14 | 15 | setHoveredNode( 16 | applicationTree->findLastNode( 17 | [=](const std::shared_ptr& node) { 18 | return node->getRect().contains(cursorPosition); 19 | } 20 | ) 21 | ); 22 | } 23 | 24 | bool isCursorCausedEvent(const std::shared_ptr& event) { 25 | return event->getName() == "mouse-button" 26 | || event->getName() == "mouse-move" 27 | || event->getName() == "scroll"; 28 | } 29 | 30 | EventCallbackResponse ApplicationHoverState::onEvent(const std::shared_ptr& event) { 31 | if (hoveredNode && isCursorCausedEvent(event)) { 32 | return hoveredNode->bubbleEvent(event); 33 | } 34 | 35 | return EventCallbackResponse::None; 36 | } 37 | 38 | void ApplicationHoverState::setHoveredNode(const std::shared_ptr& newHoveredNode) { 39 | if (hoveredNode && newHoveredNode && hoveredNode->getElement() == newHoveredNode->getElement() 40 | || hoveredNode == nullptr && newHoveredNode == nullptr) { 41 | return; 42 | } 43 | 44 | if (hoveredNode) { 45 | auto hoverLeaveEvent = std::make_shared(false); 46 | hoveredNode->bubbleEvent(hoverLeaveEvent); 47 | hoveredNode = nullptr; 48 | } 49 | 50 | if (newHoveredNode) { 51 | auto hoverEnterEvent = std::make_shared(true); 52 | newHoveredNode->bubbleEvent(hoverEnterEvent); 53 | hoveredNode = newHoveredNode; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/library/elements/Width.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 29.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_WIDTH_H 6 | #define ELEMENTOR_WIDTH_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct WidthProps { 12 | float width = 0; 13 | const std::shared_ptr & child = nullptr; 14 | }; 15 | 16 | class Width : public Element, public WithChild { 17 | public: 18 | Width(const std::shared_ptr & ctx, const WidthProps& props) 19 | : Element(ctx) { 20 | setWidth(props.width); 21 | setChild(props.child); 22 | } 23 | 24 | static std::shared_ptr New( 25 | const std::shared_ptr & ctx, 26 | const WidthProps& props 27 | ) { 28 | return std::make_shared(ctx, props); 29 | } 30 | 31 | static std::shared_ptr New( 32 | const std::shared_ptr & ctx, 33 | std::shared_ptr & elementRef, 34 | const WidthProps& props 35 | ) { 36 | auto element = New(ctx, props); 37 | elementRef = element; 38 | return element; 39 | } 40 | 41 | static std::shared_ptr New(const std::shared_ptr & ctx) { 42 | return New(ctx, {}); 43 | } 44 | 45 | void setWidth(float newValue) { 46 | markChanged(); 47 | width = newValue; 48 | } 49 | 50 | [[nodiscard]] float getWidth() const { 51 | return width; 52 | } 53 | 54 | void setChild(const std::shared_ptr & newChild) { 55 | markChanged(); 56 | child = newChild; 57 | } 58 | 59 | Size getSize(const Boundaries& boundaries) override; 60 | 61 | std::vector getChildren(const ElementRect& rect) override; 62 | 63 | private: 64 | float width = 0; 65 | }; 66 | } 67 | 68 | 69 | #endif //ELEMENTOR_WIDTH_H 70 | -------------------------------------------------------------------------------- /src/library/elements/Flexible.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_FLEXIBLE_H 6 | #define ELEMENTOR_FLEXIBLE_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct FlexibleProps { 12 | float grow = 1.0; 13 | const std::shared_ptr & child = nullptr; 14 | }; 15 | 16 | class Flexible : public Element, public WithChild { 17 | public: 18 | Flexible(const std::shared_ptr & ctx, const FlexibleProps& props) 19 | : Element(ctx) { 20 | setGrow(props.grow); 21 | setChild(props.child); 22 | } 23 | 24 | static std::shared_ptr New( 25 | const std::shared_ptr & ctx, 26 | const FlexibleProps& props 27 | ) { 28 | return std::make_shared(ctx, props); 29 | } 30 | 31 | static std::shared_ptr New( 32 | const std::shared_ptr & ctx, 33 | std::shared_ptr & elementRef, 34 | const FlexibleProps& props 35 | ) { 36 | auto element = New(ctx, props); 37 | elementRef = element; 38 | return element; 39 | } 40 | 41 | static std::shared_ptr New(const std::shared_ptr & ctx) { 42 | return New(ctx, {}); 43 | } 44 | 45 | void setGrow(float newGrow) { 46 | markChanged(); 47 | grow = newGrow; 48 | } 49 | 50 | float getGrow() const { 51 | return grow; 52 | } 53 | 54 | void setChild(const std::shared_ptr & newChild) { 55 | markChanged(); 56 | child = newChild; 57 | } 58 | 59 | Size getSize(const Boundaries& boundaries) override; 60 | 61 | std::vector getChildren(const ElementRect& rect) override; 62 | 63 | private: 64 | float grow = 1; 65 | }; 66 | } 67 | 68 | 69 | #endif //ELEMENTOR_FLEXIBLE_H 70 | -------------------------------------------------------------------------------- /src/library/elements/Height.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 29.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_HEIGHT_H 6 | #define ELEMENTOR_HEIGHT_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct HeightProps { 12 | float height = 0; 13 | const std::shared_ptr & child = nullptr; 14 | }; 15 | 16 | class Height : public Element, public WithChild { 17 | public: 18 | Height(const std::shared_ptr & ctx, const HeightProps& props) 19 | : Element(ctx) { 20 | setHeight(props.height); 21 | setChild(props.child); 22 | } 23 | 24 | static std::shared_ptr New( 25 | const std::shared_ptr & ctx, 26 | const HeightProps& props 27 | ) { 28 | return std::make_shared(ctx, props); 29 | } 30 | 31 | static std::shared_ptr New( 32 | const std::shared_ptr & ctx, 33 | std::shared_ptr & elementRef, 34 | const HeightProps& props 35 | ) { 36 | auto element = New(ctx, props); 37 | elementRef = element; 38 | return element; 39 | } 40 | 41 | static std::shared_ptr New(const std::shared_ptr & ctx) { 42 | return New(ctx, {}); 43 | } 44 | 45 | void setHeight(float newValue) { 46 | markChanged(); 47 | height = newValue; 48 | } 49 | 50 | [[nodiscard]] float getHeight() const { 51 | return height; 52 | } 53 | 54 | void setChild(const std::shared_ptr & newChild) { 55 | markChanged(); 56 | child = newChild; 57 | } 58 | 59 | Size getSize(const Boundaries& boundaries) override; 60 | 61 | std::vector getChildren(const ElementRect& rect) override; 62 | 63 | private: 64 | float height = 0; 65 | }; 66 | } 67 | 68 | 69 | #endif //ELEMENTOR_HEIGHT_H 70 | -------------------------------------------------------------------------------- /src/library/elements/Row.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_ROW_H 6 | #define ELEMENTOR_ROW_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | 12 | struct RowProps { 13 | float spacing = 0; 14 | const std::vector >& children = {}; 15 | }; 16 | 17 | class Row : public Element, public WithChildren { 18 | public: 19 | Row(const std::shared_ptr & ctx, const RowProps& props) 20 | : Element(ctx) { 21 | setSpacing(props.spacing); 22 | setChildren(props.children); 23 | } 24 | 25 | static std::shared_ptr New( 26 | const std::shared_ptr & ctx, 27 | const RowProps& props 28 | ) { 29 | return std::make_shared(ctx, props); 30 | } 31 | 32 | static std::shared_ptr New( 33 | const std::shared_ptr & ctx, 34 | std::shared_ptr & elementRef, 35 | const RowProps& props 36 | ) { 37 | auto element = New(ctx, props); 38 | elementRef = element; 39 | return element; 40 | } 41 | 42 | static std::shared_ptr New(const std::shared_ptr & ctx) { 43 | return New(ctx, {}); 44 | } 45 | 46 | float getSpacing() const { 47 | return spacing; 48 | } 49 | 50 | void setSpacing(float newSpacing) { 51 | markChanged(); 52 | spacing = newSpacing; 53 | } 54 | 55 | void setChildren(const std::vector >& newChildren) { 56 | markChanged(); 57 | children = newChildren; 58 | } 59 | 60 | Size getSize(const Boundaries& boundaries) override; 61 | 62 | std::vector getChildren(const ElementRect& rect) override; 63 | 64 | private: 65 | float spacing = 0; 66 | }; 67 | } 68 | 69 | 70 | #endif //ELEMENTOR_ROW_H 71 | -------------------------------------------------------------------------------- /src/library/elements/Column.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_COLUMN_H 6 | #define ELEMENTOR_COLUMN_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | class Column : public Element, public WithChildren { 12 | public: 13 | struct Props { 14 | float spacing = 0; 15 | const std::vector >& children = {}; 16 | }; 17 | 18 | Column(const std::shared_ptr & ctx, const Props& props) 19 | : Element(ctx) { 20 | setSpacing(props.spacing); 21 | setChildren(props.children); 22 | } 23 | 24 | static std::shared_ptr New( 25 | const std::shared_ptr & ctx, 26 | const Props& props 27 | ) { 28 | return std::make_shared(ctx, props); 29 | } 30 | 31 | static std::shared_ptr New( 32 | const std::shared_ptr & ctx, 33 | std::shared_ptr & elementRef, 34 | const Props& props 35 | ) { 36 | auto element = New(ctx, props); 37 | elementRef = element; 38 | return element; 39 | } 40 | 41 | static std::shared_ptr New(const std::shared_ptr & ctx) { 42 | return New(ctx, {}); 43 | } 44 | 45 | [[nodiscard]] float getSpacing() const { 46 | return spacing; 47 | } 48 | 49 | void setSpacing(float newSpacing) { 50 | markChanged(); 51 | spacing = newSpacing; 52 | } 53 | 54 | void setChildren(const std::vector >& newChildren) { 55 | markChanged(); 56 | children = newChildren; 57 | } 58 | 59 | Size getSize(const Boundaries& boundaries) override; 60 | 61 | std::vector getChildren(const ElementRect& rect) override; 62 | 63 | private: 64 | float spacing = 0; 65 | }; 66 | } 67 | 68 | 69 | #endif //ELEMENTOR_COLUMN_H 70 | -------------------------------------------------------------------------------- /src/library/elements/Clickable.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 21.05.2022. 3 | // 4 | 5 | #include "Clickable.h" 6 | 7 | namespace elementor::elements { 8 | Size Clickable::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | return child->getSize(boundaries); 14 | } 15 | 16 | std::vector Clickable::getChildren(const ElementRect& rect) { 17 | if (doesNotHaveChild()) { 18 | return {}; 19 | } 20 | 21 | Rect childRect = { 22 | .size = rect.size, 23 | .position = { .x = 0, .y = 0 } 24 | }; 25 | 26 | ElementWithRect childElement(child, childRect); 27 | return { childElement }; 28 | } 29 | 30 | std::vector > Clickable::getEventsHandlers() { 31 | return { 32 | HoverEvent::Handle([this](const auto& event) { 33 | hovered = event->hovered; 34 | return EventCallbackResponse::None; 35 | }), 36 | MouseButtonEvent::Handle([this](const auto& event) { 37 | if (!hovered || event->action != KeyAction::Release) { 38 | return EventCallbackResponse::None; 39 | } 40 | 41 | if (callbackButton.has_value()) { 42 | auto callback = callbackButton.value(); 43 | EventCallbackResponse callbackResponse = callback(event->button, event->mods); 44 | if (callbackResponse != EventCallbackResponse::None) { 45 | return callbackResponse; 46 | } 47 | } 48 | 49 | if (event->button == MouseButton::Left && callbackClick.has_value()) { 50 | auto callback = callbackClick.value(); 51 | EventCallbackResponse callbackResponse = callback(event->mods); 52 | if (callbackResponse != EventCallbackResponse::None) { 53 | return callbackResponse; 54 | } 55 | } 56 | 57 | return EventCallbackResponse::None; 58 | }) 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/FPSLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 03.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_FPS_LABEL_H 6 | #define ELEMENTOR_COMPONENTS_FPS_LABEL_H 7 | 8 | #include "utility.h" 9 | #include "elementor.h" 10 | 11 | namespace elementor::components { 12 | class FPSLabel : public Component { 13 | public: 14 | explicit FPSLabel(const std::shared_ptr& ctx) 15 | : Component(ctx) { 16 | element = Rounded::New(ctx, { 17 | .all = 3, 18 | .child = Background::New(ctx, { 19 | .color = "#e8e8e8cc", 20 | .child = Padding::New(ctx, { 21 | .all = 8, 22 | .child = Row::New(ctx, { 23 | .spacing = 4, 24 | .children = { 25 | Text::New(ctx, { 26 | .text = "FPS: ", 27 | .fontColor = "#000", 28 | .fontSize = 16.0f, 29 | .fontFamily = "Arial", 30 | }), 31 | Height::New(ctx, { 32 | .height = 14, 33 | .child = Width::New(ctx, { 34 | .width = 30, 35 | .child = Text::New(ctx, fpsText, { 36 | .text = "0", 37 | .fontColor = "#000", 38 | .fontSize = 16, 39 | .fontWeight = 500, 40 | .fontFamily = "Fira Code", 41 | }), 42 | }) 43 | }) 44 | } 45 | }) 46 | }) 47 | }) 48 | }); 49 | 50 | updateFPS(); 51 | } 52 | 53 | static std::shared_ptr New(const std::shared_ptr& ctx) { 54 | return std::make_shared(ctx); 55 | } 56 | 57 | private: 58 | std::shared_ptr fpsText; 59 | 60 | void updateFPS() { 61 | auto fps = ctx->getPerfomance()->getFPS(); 62 | fpsText->setText(std::to_string((int)fps)); 63 | 64 | ctx->requestNextFrame([this]() { 65 | updateFPS(); 66 | }); 67 | } 68 | }; 69 | } 70 | 71 | #endif //ELEMENTOR_COMPONENTS_FPS_LABEL_H 72 | -------------------------------------------------------------------------------- /src/components/Slot.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 01.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_SLOT_H 6 | #define ELEMENTOR_COMPONENTS_SLOT_H 7 | 8 | #include "elementor.h" 9 | 10 | namespace elementor::elements { 11 | struct SlotProps { 12 | const std::shared_ptr& child = nullptr; 13 | }; 14 | 15 | class Slot : public Element, public WithChild { 16 | public: 17 | Slot(const std::shared_ptr& ctx, const SlotProps& props) 18 | : Element(ctx) { 19 | } 20 | 21 | static std::shared_ptr New( 22 | const std::shared_ptr& ctx, 23 | const SlotProps& props 24 | ) { 25 | return std::make_shared(ctx, props); 26 | } 27 | 28 | static std::shared_ptr New( 29 | const std::shared_ptr& ctx, 30 | std::shared_ptr& elementRef, 31 | const SlotProps& props 32 | ) { 33 | auto element = New(ctx, props); 34 | elementRef = element; 35 | return element; 36 | } 37 | 38 | static std::shared_ptr New(const std::shared_ptr& ctx) { 39 | return New(ctx, {}); 40 | } 41 | 42 | void setChild(const std::shared_ptr & newChild) { 43 | markChanged(); 44 | child = newChild; 45 | } 46 | 47 | Size getSize(const Boundaries& boundaries) override { 48 | if (doesNotHaveChild()) { 49 | return boundaries.max; 50 | } 51 | 52 | return child->getSize(boundaries); 53 | } 54 | 55 | std::vector getChildren(const ElementRect& rect) override { 56 | if (doesNotHaveChild()) { 57 | return {}; 58 | } 59 | 60 | Rect childRect = { 61 | .size = rect.size, 62 | .position = { .x = 0.0f, .y = 0.0f }, 63 | }; 64 | 65 | ElementWithRect childElementWithRect(child, childRect); 66 | return { childElementWithRect }; 67 | } 68 | }; 69 | } 70 | 71 | 72 | #endif //ELEMENTOR_COMPONENTS_SLOT_H 73 | -------------------------------------------------------------------------------- /src/library/elements/Column.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Column.h" 6 | 7 | namespace elementor::elements { 8 | Size Column::getSize(const Boundaries& boundaries) { 9 | float maxWidth = 0; 10 | float totalHeight = 0; 11 | for (const auto& childElement: children) { 12 | Boundaries childBoundaries = { 13 | .min = { .width = boundaries.min.width, .height = 0 }, 14 | .max = boundaries.max 15 | }; 16 | Size childSize = childElement->getSize(childBoundaries); 17 | 18 | maxWidth = std::max(childSize.width, maxWidth); 19 | totalHeight += childSize.height; 20 | } 21 | 22 | auto pixelScale = ctx->getPixelScale(); 23 | auto spacingScaled = spacing * pixelScale; 24 | 25 | totalHeight += ((float)children.size() - 1) * spacingScaled; 26 | 27 | Size elementSize = { 28 | .width = maxWidth, 29 | .height = totalHeight, 30 | }; 31 | elementSize = fitSizeInBoundaries(elementSize, boundaries); 32 | 33 | return elementSize; 34 | } 35 | 36 | std::vector Column::getChildren(const ElementRect& rect) { 37 | std::vector childrenElements; 38 | 39 | auto pixelScale = ctx->getPixelScale(); 40 | auto spacingScaled = spacing * pixelScale; 41 | 42 | float yPosition = 0; 43 | for (const auto& child: children) { 44 | Boundaries childBoundaries = { 45 | .min = { .width = rect.size.width, .height = 0 }, 46 | .max = rect.size 47 | }; 48 | Size childSize = child->getSize(childBoundaries); 49 | 50 | Rect childRect = { 51 | .size = childSize, 52 | .position = { 53 | .x = 0, 54 | .y = yPosition, 55 | } 56 | }; 57 | 58 | ElementWithRect childElement(child, childRect); 59 | childrenElements.push_back(childElement); 60 | 61 | yPosition += childSize.height; 62 | yPosition += spacingScaled; 63 | } 64 | 65 | return childrenElements; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/library/elements/Row.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Row.h" 6 | 7 | namespace elementor::elements { 8 | Size Row::getSize(const Boundaries& boundaries) { 9 | float maxHeight = 0; 10 | float totalWidth = 0; 11 | for (const auto& childElement: children) { 12 | Boundaries childBoundaries = { 13 | .min = { .width = 0, .height = boundaries.min.height }, 14 | .max = boundaries.max 15 | }; 16 | Size childSize = childElement->getSize(childBoundaries); 17 | 18 | maxHeight = std::max(childSize.height, maxHeight); 19 | totalWidth += childSize.width; 20 | } 21 | 22 | auto pixelScale = ctx->getPixelScale(); 23 | auto spacingScaled = spacing * pixelScale; 24 | 25 | totalWidth += ((float)children.size() - 1) * spacingScaled; 26 | 27 | Size elementSize = { 28 | .width = totalWidth, 29 | .height = maxHeight, 30 | }; 31 | elementSize = fitSizeInBoundaries(elementSize, boundaries); 32 | 33 | return elementSize; 34 | } 35 | 36 | std::vector Row::getChildren(const ElementRect& rect) { 37 | std::vector childrenElements; 38 | 39 | auto pixelScale = ctx->getPixelScale(); 40 | auto spacingScaled = spacing * pixelScale; 41 | 42 | float xPosition = 0; 43 | for (const auto& child: children) { 44 | Boundaries childBoundaries = { 45 | .min = { .width = 0, .height = rect.size.height }, 46 | .max = rect.size 47 | }; 48 | Size childSize = child->getSize(childBoundaries); 49 | 50 | Rect childRect = { 51 | .size = child->getSize(childBoundaries), 52 | .position = { 53 | .x = xPosition, 54 | .y = 0, 55 | } 56 | }; 57 | 58 | ElementWithRect childElement(child, childRect); 59 | childrenElements.push_back(childElement); 60 | 61 | xPosition += childSize.width; 62 | xPosition += spacingScaled; 63 | } 64 | 65 | return childrenElements; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLCursor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_CURSOR_H 6 | #define ELEMENTOR_GL_CURSOR_H 7 | 8 | #include "elementor.h" 9 | 10 | #include "GLFW/glfw3.h" 11 | 12 | #include "GLPlatformContext.h" 13 | 14 | namespace elementor::platforms::gl { 15 | class GLCursor : public Cursor { 16 | public: 17 | GLCursor(GLFWwindow* window) 18 | : window(window) { 19 | } 20 | 21 | void set(CursorShape shape) override { 22 | currentShape = shape; 23 | } 24 | 25 | CursorShape get() override { 26 | return currentShape; 27 | } 28 | 29 | Position getPosition() override { 30 | return position; 31 | } 32 | 33 | void setPosition(Position newValue) { 34 | position = newValue; 35 | } 36 | 37 | void updateCursor() { 38 | if (appliedShape != currentShape) { 39 | auto cursor = glfwCreateStandardCursor(mapCursorShapeToInt(currentShape)); 40 | glfwSetCursor(window, cursor); 41 | appliedShape = currentShape; 42 | } 43 | } 44 | 45 | private: 46 | GLFWwindow* window; 47 | 48 | CursorShape currentShape = CursorShape::Default; 49 | CursorShape appliedShape = CursorShape::Default; 50 | 51 | Position position = { .x = -1, .y = -1 }; 52 | 53 | static int mapCursorShapeToInt(CursorShape shape) { 54 | switch (shape) { 55 | case CursorShape::Default: 56 | case CursorShape::Arrow: 57 | return GLFW_ARROW_CURSOR; 58 | case CursorShape::IBeam: 59 | return GLFW_IBEAM_CURSOR; 60 | case CursorShape::Crosshair: 61 | return GLFW_CROSSHAIR_CURSOR; 62 | case CursorShape::Hand: 63 | return GLFW_HAND_CURSOR; 64 | case CursorShape::HorizontalResize: 65 | return GLFW_HRESIZE_CURSOR; 66 | case CursorShape::VerticalResize: 67 | return GLFW_VRESIZE_CURSOR; 68 | default: 69 | return GLFW_ARROW_CURSOR; 70 | } 71 | } 72 | }; 73 | }; 74 | 75 | #endif //ELEMENTOR_GL_CURSOR_H 76 | -------------------------------------------------------------------------------- /src/components/Outline.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 01.02.2024. 3 | // 4 | 5 | #include "Outline.h" 6 | 7 | namespace elementor::components { 8 | Size Outline::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | return getChild()->getSize(boundaries); 14 | } 15 | 16 | std::vector Outline::getChildren(const ElementRect& rect) { 17 | if (doesNotHaveChild()) { 18 | return {}; 19 | } 20 | 21 | std::vector children; 22 | 23 | if (focused) { 24 | auto pixelScale = ctx->getPixelScale(); 25 | auto offsetScaled = offset * pixelScale; 26 | 27 | Rect borderRect = { 28 | .size = { 29 | .width = rect.size.width + offsetScaled * 2, 30 | .height = rect.size.height + offsetScaled * 2, 31 | }, 32 | .position = { 33 | .x = -1 * offsetScaled, 34 | .y = -1 * offsetScaled 35 | }, 36 | }; 37 | auto borderAsElement = std::dynamic_pointer_cast(border); 38 | ElementWithRect borderElementWithRect(borderAsElement, borderRect); 39 | children.push_back(borderElementWithRect); 40 | } 41 | 42 | Rect childRect = { 43 | .size = rect.size, 44 | .position = { .x = 0.0f, .y = 0.0f }, 45 | }; 46 | ElementWithRect childElementWithRect(child, childRect); 47 | children.push_back(childElementWithRect); 48 | 49 | return children; 50 | } 51 | 52 | void Outline::setFocused(bool newFocused) { 53 | markChanged(); 54 | focused = newFocused; 55 | } 56 | 57 | std::vector> Outline::getEventsHandlers() { 58 | return { 59 | FocusInEvent::Handle([this](const auto& event) { 60 | setFocused(true); 61 | return EventCallbackResponse::None; 62 | }), 63 | FocusOutEvent::Handle([this](const auto& event) { 64 | setFocused(false); 65 | return EventCallbackResponse::None; 66 | }) 67 | }; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/library/Render.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem 21 18.05.2022. 3 | // 4 | 5 | #include "Render.h" 6 | 7 | #include 8 | 9 | namespace elementor { 10 | bool Rect::contains(float x, float y) const { 11 | return ( 12 | position.x < x && 13 | position.x + size.width > x && 14 | position.y < y && 15 | position.y + size.height > y 16 | ); 17 | } 18 | 19 | bool Rect::contains(Position point) const { 20 | return contains(point.x, point.y); 21 | } 22 | 23 | Rect unionOfRects(const Rect& a, const Rect& b) { 24 | Rect result{}; 25 | result.position.x = std::max(a.position.x, b.position.x); 26 | result.position.y = std::max(a.position.y, b.position.y); 27 | result.size.width = std::max( 28 | std::min(a.position.x + a.size.width, b.position.x + b.size.width) - result.position.x, 0.0f 29 | ); 30 | result.size.height = std::max( 31 | std::min(a.position.y + a.size.height, b.position.y + b.size.height) - result.position.y, 0.0f 32 | ); 33 | return result; 34 | } 35 | 36 | bool ElementRect::visibleContains(float x, float y) const { 37 | return ( 38 | visiblePosition.x < x && 39 | visiblePosition.x + visibleSize.width > x && 40 | visiblePosition.y < y && 41 | visiblePosition.y + visibleSize.height > y 42 | ); 43 | } 44 | 45 | bool ElementRect::visibleContains(Position point) const { 46 | return visibleContains(point.x, point.y); 47 | } 48 | 49 | Position ElementRect::absolutePositionToContained(Position absolutePosition) const { 50 | return { 51 | .x = absolutePosition.x - position.x, 52 | .y = absolutePosition.y - position.y, 53 | }; 54 | } 55 | 56 | Size fitSizeInBoundaries(Size size, Boundaries boundaries) { 57 | return { 58 | .width = std::min(std::max(size.width, boundaries.min.width), boundaries.max.width), 59 | .height = std::min(std::max(size.height, boundaries.min.height), boundaries.max.height) 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLEventLoop.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.01.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_EVENT_LOOP_H 6 | #define ELEMENTOR_GL_EVENT_LOOP_H 7 | 8 | #include 9 | 10 | #include "GLFW/glfw3.h" 11 | 12 | #include "elementor.h" 13 | 14 | namespace elementor::platforms::gl { 15 | class GLEventLoop { 16 | public: 17 | explicit GLEventLoop(const std::function& callback) 18 | : callback(callback) { 19 | } 20 | 21 | void run() { 22 | running = true; 23 | 24 | for (;;) { 25 | D( 26 | const char* glfwErrorDescription; 27 | int glfwErrorCode = glfwGetError(&glfwErrorDescription); 28 | if (glfwErrorCode != 0 || glfwErrorDescription) { 29 | D_LOG("GLFW error: " << glfwErrorDescription << " (" << glfwErrorCode << ")" << std::endl); 30 | } 31 | ); 32 | 33 | glfwWaitEvents(); 34 | 35 | D( 36 | glfwErrorCode = glfwGetError(&glfwErrorDescription); 37 | if (glfwErrorCode != 0 || glfwErrorDescription) { 38 | D_LOG("GLFW error: " << glfwErrorDescription << " (" << glfwErrorCode << ")" << std::endl); 39 | } 40 | ); 41 | 42 | tryCallCallback(); 43 | if (shouldBreak) { 44 | break; 45 | } 46 | } 47 | 48 | running = false; 49 | } 50 | 51 | void pend() { 52 | glfwPostEmptyEvent(); 53 | tryCallCallback(); 54 | } 55 | 56 | private: 57 | bool running = false; 58 | 59 | std::function callback; 60 | 61 | std::chrono::steady_clock::time_point lastCallbackCall = std::chrono::steady_clock::now(); 62 | bool shouldBreak = false; 63 | 64 | void tryCallCallback() { 65 | if (!running) { 66 | return; 67 | } 68 | 69 | auto now = std::chrono::steady_clock::now(); 70 | 71 | if (now - lastCallbackCall > std::chrono::milliseconds(1)) { 72 | lastCallbackCall = now; 73 | shouldBreak |= callback(); 74 | } 75 | } 76 | }; 77 | } 78 | 79 | #endif //ELEMENTOR_GL_EVENT_LOOP_H 80 | -------------------------------------------------------------------------------- /src/examples/counter/Counter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by admin on 17.02.2024. 3 | // 4 | 5 | #ifndef EXAMPLES_COUNTER_H 6 | #define EXAMPLES_COUNTER_H 7 | 8 | #include "../elementor.h" 9 | 10 | class Counter : public Component { 11 | public: 12 | explicit Counter(const std::shared_ptr& ctx) 13 | : Component(ctx) { 14 | element = Background::New(ctx, { 15 | .color = "#fff", 16 | .child = Flex::New(ctx, { 17 | .spacing = 4, 18 | .alignment = FlexAlignment::Center, 19 | .crossAlignment = FlexCrossAlignment::Center, 20 | .children = { 21 | Rounded::New(ctx, { 22 | .all = 6, 23 | .child = Background::New(ctx, { 24 | .color = "#ebf0f0", 25 | .child = Padding::New(ctx, { 26 | .all = 8, 27 | .child = Width::New(ctx, { 28 | .width = 50, 29 | .child = Align::New(ctx, { 30 | .width = { 1, 1 }, 31 | .child = Text::New(ctx, countText, { 32 | .text = std::to_string(count), 33 | .fontColor = "#000", 34 | .fontSize = 14, 35 | .fontFamily = "Fira Code", 36 | }) 37 | }) 38 | }) 39 | }) 40 | }) 41 | }), 42 | TextButton::New(ctx, { 43 | .text = "Count", 44 | .fontColor = "#fff", 45 | .backgroundColor = "#ff5020", 46 | .onClick = [this] { 47 | incCount(); 48 | return EventCallbackResponse::None; 49 | } 50 | }) 51 | } 52 | }) 53 | }); 54 | } 55 | 56 | static std::shared_ptr New(const std::shared_ptr& ctx) { 57 | return std::make_shared(ctx); 58 | } 59 | 60 | private: 61 | int count = 0; 62 | 63 | std::shared_ptr countText; 64 | 65 | void setCount(int newValue) { 66 | count = newValue; 67 | countText->setText(std::to_string(count)); 68 | } 69 | 70 | void incCount() { 71 | setCount(count + 1); 72 | } 73 | }; 74 | 75 | #endif //EXAMPLES_COUNTER_H 76 | -------------------------------------------------------------------------------- /src/components/Outline.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 02.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_OUTLINE_H 6 | #define ELEMENTOR_COMPONENTS_OUTLINE_H 7 | 8 | #include "utility.h" 9 | #include "elementor.h" 10 | 11 | namespace elementor::components { 12 | struct OutlineProps { 13 | BorderProps border; 14 | float offset = 0.0f; 15 | const std::shared_ptr & child = nullptr; 16 | }; 17 | 18 | class Outline : public Element, public WithChild, public WithEventsHandlers { 19 | public: 20 | explicit Outline(const std::shared_ptr & ctx, const OutlineProps& props) 21 | : Element(ctx) { 22 | offset = props.offset; 23 | border = Border::New(ctx, props.border); 24 | setChild(props.child); 25 | } 26 | 27 | static std::shared_ptr New( 28 | const std::shared_ptr & ctx, 29 | std::shared_ptr & elementRef, 30 | const OutlineProps& props 31 | ) { 32 | auto element = New(ctx, props); 33 | elementRef = element; 34 | return element; 35 | } 36 | 37 | static std::shared_ptr New( 38 | const std::shared_ptr & ctx, 39 | const OutlineProps& props 40 | ) { 41 | return std::make_shared(ctx, props); 42 | } 43 | 44 | static std::shared_ptr New(const std::shared_ptr & ctx) { 45 | return New(ctx, {}); 46 | } 47 | 48 | void setChild(const std::shared_ptr & newChild) { 49 | markChanged(); 50 | child = newChild; 51 | } 52 | 53 | Size getSize(const Boundaries& boundaries) override; 54 | 55 | std::vector getChildren(const ElementRect& rect) override; 56 | 57 | std::vector > getEventsHandlers() override; 58 | 59 | private: 60 | std::shared_ptr border = nullptr; 61 | 62 | float offset = 0.0; 63 | bool focused = false; 64 | 65 | void setFocused(bool newFocused); 66 | }; 67 | } 68 | 69 | #endif //ELEMENTOR_COMPONENTS_OUTLINE_H 70 | -------------------------------------------------------------------------------- /src/library/utility.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 07.02.2024. 3 | // 4 | 5 | #include "utility.h" 6 | 7 | namespace elementor { 8 | SkColor makeSkColorFromRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 9 | return SkColorSetARGB(a, r, g, b); 10 | } 11 | 12 | SkColor makeSkColorFromRGB(uint8_t r, uint8_t g, uint8_t b) { 13 | return SkColorSetARGB(255, r, g, b); 14 | } 15 | 16 | SkColor makeSkColorFromHex(std::string hex) { 17 | if (hex.empty()) { 18 | return SK_ColorTRANSPARENT; 19 | } 20 | 21 | if (hex[0] == '#') { 22 | hex = hex.substr(1); 23 | } 24 | 25 | if (hex.size() == 8) { 26 | uint8_t r = std::stoul(hex.substr(0, 2), nullptr, 16); 27 | uint8_t g = std::stoul(hex.substr(2, 2), nullptr, 16); 28 | uint8_t b = std::stoul(hex.substr(4, 2), nullptr, 16); 29 | uint8_t a = std::stoul(hex.substr(6, 2), nullptr, 16); 30 | return makeSkColorFromRGBA(r, g, b, a); 31 | } 32 | 33 | if (hex.size() == 6) { 34 | uint8_t r = std::stoul(hex.substr(0, 2), nullptr, 16); 35 | uint8_t g = std::stoul(hex.substr(2, 2), nullptr, 16); 36 | uint8_t b = std::stoul(hex.substr(4, 2), nullptr, 16); 37 | return makeSkColorFromRGB(r, g, b); 38 | } 39 | 40 | if (hex.size() == 4) { 41 | uint8_t r = std::stoul(hex.substr(0, 1), nullptr, 16); 42 | r = r * 16 + r; 43 | uint8_t g = std::stoul(hex.substr(1, 1), nullptr, 16); 44 | g = g * 16 + g; 45 | uint8_t b = std::stoul(hex.substr(2, 1), nullptr, 16); 46 | b = b * 16 + b; 47 | uint8_t a = std::stoul(hex.substr(3, 1), nullptr, 16); 48 | a = a * 16 + a; 49 | return makeSkColorFromRGBA(r, g, b, a); 50 | } 51 | 52 | if (hex.size() == 3) { 53 | uint8_t r = std::stoul(hex.substr(0, 1), nullptr, 16); 54 | r = r * 16 + r; 55 | uint8_t g = std::stoul(hex.substr(1, 1), nullptr, 16); 56 | g = g * 16 + g; 57 | uint8_t b = std::stoul(hex.substr(2, 1), nullptr, 16); 58 | b = b * 16 + b; 59 | return makeSkColorFromRGB(r, g, b); 60 | } 61 | 62 | return SK_ColorTRANSPARENT; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLPlatform.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 14.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_GL_PLATFORM_H 6 | #define ELEMENTOR_GL_PLATFORM_H 7 | 8 | #include "elementor.h" 9 | 10 | #include "GLPlatformContext.h" 11 | 12 | #include "GLEventLoop.h" 13 | #include "GLClipboard.h" 14 | #include "GLFontManager.h" 15 | #include "GLPerfomance.h" 16 | #include "GLWindow.h" 17 | 18 | namespace elementor::platforms::gl { 19 | class GLPlatform : public GLPlatformContext { 20 | public: 21 | GLPlatform(); 22 | 23 | void addWindow(const std::shared_ptr& window); 24 | 25 | void removeWindow(const std::shared_ptr& window); 26 | 27 | void removeWindow(unsigned int index); 28 | 29 | void run(); 30 | 31 | void requestNextFrame(const std::function& callback) override; 32 | 33 | void requestNextFrame() override; 34 | 35 | [[nodiscard]] std::shared_ptr getClipboard() const override { 36 | return clipboard; 37 | } 38 | 39 | [[nodiscard]] std::shared_ptr getPerfomance() const override { 40 | return perfomance; 41 | } 42 | 43 | [[nodiscard]] sk_sp getSkFontManager() const override { 44 | return fontManager->getSkFontManager(); 45 | } 46 | 47 | [[nodiscard]] std::string getLocale() const { 48 | return locale; 49 | } 50 | 51 | void setLocale(std::string newLocale) { 52 | locale = newLocale; 53 | } 54 | 55 | #ifdef TEST 56 | void __T_tick(int N) { 57 | for (auto i = 0; i < N; i++) { 58 | tick(); 59 | } 60 | } 61 | #endif 62 | 63 | private: 64 | std::shared_ptr eventLoop; 65 | 66 | std::shared_ptr clipboard; 67 | std::shared_ptr perfomance; 68 | std::shared_ptr fontManager; 69 | 70 | std::vector> windows; 71 | 72 | std::vector> rnfQueue; 73 | 74 | std::string locale = "en"; 75 | 76 | void tick(); 77 | 78 | void applyRnfQueue(); 79 | }; 80 | }; 81 | 82 | #endif //ELEMENTOR_GL_PLATFORM_H 83 | -------------------------------------------------------------------------------- /src/library/elements/SVG.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 30.07.2022. 3 | // 4 | 5 | #include "SVG.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace elementor::elements { 16 | void SVG::fromSkStream(SkStream& stream) { 17 | markChanged(); 18 | fromSkSVGDOM(SkSVGDOM::MakeFromStream(stream)); 19 | } 20 | 21 | void SVG::fromSkData(sk_sp data) { 22 | SkMemoryStream dataStream(std::move(data)); 23 | return fromSkStream(dataStream); 24 | } 25 | 26 | void SVG::fromPath(const std::string& path) { 27 | return fromSkData(SkData::MakeFromFileName(path.c_str())); 28 | } 29 | 30 | void SVG::fromString(const char value[]) { 31 | return fromSkData(SkData::MakeWithCString(value)); 32 | } 33 | 34 | void SVG::paintBackground(SkCanvas* canvas, const ElementRect& rect) { 35 | if (skSVGDOM == nullptr) { 36 | return; 37 | } 38 | 39 | if (skImage == nullptr 40 | || (abs(skImage->width() - rect.size.width) > 5) 41 | || (abs(skImage->height() - rect.size.height) > 5)) { 42 | SkSVGSVG* root = skSVGDOM->getRoot(); 43 | root->setWidth(SkSVGLength(rect.size.width, SkSVGLength::Unit::kNumber)); 44 | root->setHeight(SkSVGLength(rect.size.height, SkSVGLength::Unit::kNumber)); 45 | 46 | // TODO: Cache can be based on something like this 47 | sk_sp svgSurface = SkSurface::MakeRasterN32Premul(rect.size.width, rect.size.height); 48 | if (svgSurface == nullptr) { 49 | skSVGDOM->render(canvas); 50 | return; 51 | } 52 | 53 | SkCanvas* svgCanvas = svgSurface->getCanvas(); 54 | skSVGDOM->render(svgCanvas); 55 | skImage = svgSurface->makeImageSnapshot(); 56 | } 57 | 58 | SkRect skRect = SkRect::MakeXYWH(0, 0, rect.size.width, rect.size.height); 59 | canvas->drawImageRect(skImage, skRect, SkSamplingOptions(SkCubicResampler::Mitchell())); 60 | } 61 | } -------------------------------------------------------------------------------- /src/library/ApplicationContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 12.01.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_APPLICATION_CONTEXT_H 6 | #define ELEMENTOR_APPLICATION_CONTEXT_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "Render.h" 17 | #include "Event.h" 18 | 19 | namespace elementor { 20 | constexpr float DefaultPixelScale = 3.8; // 38 logical pixels per centimeter 21 | 22 | class Clipboard { 23 | public: 24 | virtual ~Clipboard() = default; 25 | 26 | virtual void set(const std::string_view& text) = 0; 27 | 28 | virtual const std::string_view& get() = 0; 29 | }; 30 | 31 | class Perfomance { 32 | public: 33 | virtual double getFPS() = 0; 34 | }; 35 | 36 | enum class CursorShape { 37 | Default, 38 | Arrow, 39 | IBeam, 40 | Crosshair, 41 | Hand, 42 | HorizontalResize, 43 | VerticalResize 44 | }; 45 | 46 | class Cursor { 47 | public: 48 | virtual void set(CursorShape shape) = 0; 49 | 50 | virtual CursorShape get() = 0; 51 | 52 | virtual Position getPosition() = 0; 53 | }; 54 | 55 | class Display { 56 | public: 57 | virtual Size getSize() = 0; 58 | 59 | virtual Size getPhysicalSize() = 0; 60 | }; 61 | 62 | class ApplicationContext { 63 | public: 64 | virtual std::string getLocale() = 0; 65 | 66 | virtual void setLocale(std::string locale) = 0; 67 | 68 | virtual std::shared_ptr getClipboard() = 0; 69 | 70 | virtual std::shared_ptr getPerfomance() = 0; 71 | 72 | virtual sk_sp getSkFontManager() = 0; 73 | 74 | virtual void requestNextFrame(const std::function& callback) = 0; 75 | 76 | virtual void requestNextFrame() = 0; 77 | 78 | virtual std::shared_ptr getCursor() = 0; 79 | 80 | virtual std::shared_ptr getDisplay() = 0; 81 | 82 | virtual float getPixelScale() = 0; 83 | 84 | virtual void setPixelScale(float pixelScale) = 0; 85 | }; 86 | } 87 | 88 | #endif //ELEMENTOR_APPLICATION_CONTEXT_H 89 | -------------------------------------------------------------------------------- /src/components/Eventable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 02.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_EVENTABLE_H 6 | #define ELEMENTOR_COMPONENTS_EVENTABLE_H 7 | 8 | #include "utility.h" 9 | #include "elementor.h" 10 | 11 | namespace elementor::components { 12 | struct EventableProps { 13 | std::vector > events; 14 | std::vector > globalEvents; 15 | const std::shared_ptr & child = nullptr; 16 | }; 17 | 18 | class Eventable : public Component, public WithEventsHandlers, public WithGlobalEventsHandlers { 19 | public: 20 | explicit Eventable(const std::shared_ptr & ctx, const EventableProps& props) 21 | : Component(ctx) { 22 | events = props.events; 23 | globalEvents = props.globalEvents; 24 | 25 | setChild(props.child); 26 | } 27 | 28 | static std::shared_ptr New( 29 | const std::shared_ptr & ctx, 30 | const EventableProps& props 31 | ) { 32 | return std::make_shared(ctx, props); 33 | } 34 | 35 | static std::shared_ptr New( 36 | const std::shared_ptr & ctx, 37 | std::shared_ptr & elementRef, 38 | const EventableProps& props 39 | ) { 40 | auto element = New(ctx, props); 41 | elementRef = element; 42 | return element; 43 | } 44 | 45 | static std::shared_ptr New(const std::shared_ptr & ctx) { 46 | return New(ctx, {}); 47 | } 48 | 49 | void setChild(const std::shared_ptr & child) { 50 | markChanged(); 51 | element = child; 52 | } 53 | 54 | std::vector > getEventsHandlers() override { 55 | return events; 56 | } 57 | 58 | std::vector > getGlobalEventsHandlers() override { 59 | return globalEvents; 60 | } 61 | 62 | private: 63 | std::vector > events; 64 | std::vector > globalEvents; 65 | }; 66 | } 67 | 68 | #endif //ELEMENTOR_COMPONENTS_EVENTABLE_H 69 | -------------------------------------------------------------------------------- /src/library/elements/Padding.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Padding.h" 6 | 7 | namespace elementor::elements { 8 | 9 | Size Padding::getSize(const Boundaries& boundaries) { 10 | if (doesNotHaveChild()) { 11 | return boundaries.max; 12 | } 13 | 14 | auto pixelScale = ctx->getPixelScale(); 15 | float paddingTop = paddings.top * pixelScale; 16 | float paddingBottom = paddings.bottom * pixelScale; 17 | float paddingY = paddingTop + paddingBottom; 18 | float paddingRight = paddings.right * pixelScale; 19 | float paddingLeft = paddings.left * pixelScale; 20 | float paddingX = paddingLeft + paddingRight; 21 | 22 | Boundaries childBoundaries = { 23 | .min = { 24 | .width = std::max(boundaries.min.width - paddingX, 0.0f), 25 | .height = std::max(boundaries.min.height - paddingY, 0.0f) 26 | }, 27 | .max = { 28 | .width = std::max(boundaries.max.width - paddingX, 0.0f), 29 | .height = std::max(boundaries.max.height - paddingY, 0.0f) 30 | } 31 | }; 32 | Size childSize = child->getSize(childBoundaries); 33 | 34 | Size elementSize = { 35 | .width = childSize.width + paddingX, 36 | .height = childSize.height + paddingY 37 | }; 38 | elementSize = fitSizeInBoundaries(elementSize, boundaries); 39 | 40 | return elementSize; 41 | } 42 | 43 | std::vector Padding::getChildren(const ElementRect& rect) { 44 | if (doesNotHaveChild()) { 45 | return {}; 46 | } 47 | 48 | auto pixelScale = ctx->getPixelScale(); 49 | float paddingTop = paddings.top * pixelScale; 50 | float paddingBottom = paddings.bottom * pixelScale; 51 | float paddingY = paddingTop + paddingBottom; 52 | float paddingRight = paddings.right * pixelScale; 53 | float paddingLeft = paddings.left * pixelScale; 54 | float paddingX = paddingLeft + paddingRight; 55 | 56 | Rect childRect = { 57 | .size = { 58 | .width = rect.size.width - paddingX, 59 | .height = rect.size.height - paddingY, 60 | }, 61 | .position = { 62 | .x = paddingLeft, 63 | .y = paddingTop 64 | }, 65 | }; 66 | 67 | ElementWithRect childElementWithRect(child, childRect); 68 | return { childElementWithRect }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/components/TextButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 16.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_TEXT_BUTTON_H 6 | #define ELEMENTOR_COMPONENTS_TEXT_BUTTON_H 7 | 8 | #include "elementor.h" 9 | 10 | #include "./Button.h" 11 | 12 | namespace elementor::components { 13 | class TextButton : public Component { 14 | public: 15 | struct Props { 16 | std::optional text; 17 | std::optional fontColor; 18 | std::optional backgroundColor; 19 | std::optional> onClick; 20 | std::optional> onMiddleClick; 21 | }; 22 | 23 | explicit TextButton(const std::shared_ptr& ctx, const Props& props) 24 | : Component(ctx) { 25 | element = Button::New(ctx, { 26 | .onClick = props.onClick, 27 | .onMiddleClick = props.onMiddleClick, 28 | .child = Rounded::New(ctx, { 29 | .all = 4, 30 | .child = Background::New(ctx, { 31 | .color = props.backgroundColor, 32 | .child = Padding::New(ctx, { 33 | .all = 8, 34 | .top = 6, 35 | .child = Text::New(ctx, text, { 36 | .text = props.text, 37 | .fontColor = props.fontColor, 38 | .fontSize = 16, 39 | .fontFamily = "Arial", 40 | }), 41 | }) 42 | }) 43 | }) 44 | }); 45 | } 46 | 47 | static std::shared_ptr New( 48 | const std::shared_ptr& ctx, 49 | const Props& props 50 | ) { 51 | return std::make_shared(ctx, props); 52 | } 53 | 54 | static std::shared_ptr New( 55 | const std::shared_ptr& ctx, 56 | std::shared_ptr& elementRef, 57 | const Props& props 58 | ) { 59 | auto element = New(ctx, props); 60 | elementRef = element; 61 | return element; 62 | } 63 | 64 | static std::shared_ptr New(const std::shared_ptr& ctx) { 65 | return New(ctx, {}); 66 | } 67 | 68 | void setText(const std::string& newText) { 69 | text->setText(newText); 70 | } 71 | 72 | private: 73 | std::shared_ptr text = nullptr; 74 | }; 75 | } 76 | 77 | #endif //ELEMENTOR_COMPONENTS_TEXT_BUTTON_H 78 | -------------------------------------------------------------------------------- /src/library/Render.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 21.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_RENDER_H 6 | #define ELEMENTOR_RENDER_H 7 | 8 | #include 9 | 10 | namespace elementor { 11 | 12 | struct Position { 13 | float x; 14 | float y; 15 | 16 | bool operator==(const Position& other) const { 17 | return x == other.x && y == other.y; 18 | } 19 | }; 20 | 21 | constexpr Position InvalidPosition = { -1, -1 }; 22 | 23 | struct Size { 24 | float width; 25 | float height; 26 | 27 | bool operator==(const Size& other) const { 28 | return width == other.width && height == other.height; 29 | } 30 | 31 | Size operator*(float multiplier) const { 32 | return { 33 | .width = width * multiplier, 34 | .height = height * multiplier 35 | }; 36 | } 37 | 38 | Size operator/(float divider) const { 39 | return { 40 | .width = width / divider, 41 | .height = height / divider 42 | }; 43 | } 44 | }; 45 | 46 | struct Boundaries { 47 | Size min; 48 | Size max; 49 | }; 50 | 51 | struct Rect { 52 | Size size; 53 | Position position; 54 | 55 | [[nodiscard]] bool contains(float x, float y) const; 56 | 57 | [[nodiscard]] bool contains(Position point) const; 58 | 59 | bool operator==(const Rect& other) const { 60 | return size == other.size && position == other.position; 61 | } 62 | }; 63 | 64 | Rect unionOfRects(const Rect& a, const Rect& b); 65 | 66 | struct ElementRect : Rect { 67 | Size visibleSize; 68 | Position visiblePosition; 69 | Position inParentPosition; 70 | 71 | [[nodiscard]] bool visibleContains(float x, float y) const; 72 | 73 | [[nodiscard]] bool visibleContains(Position point) const; 74 | 75 | [[nodiscard]] Position absolutePositionToContained(Position absolutePosition) const; 76 | }; 77 | 78 | Size fitSizeInBoundaries(Size size, Boundaries boundaries); 79 | 80 | constexpr float Infinity = std::numeric_limits::infinity(); 81 | 82 | constexpr Position ZeroPosition = { 0, 0 }; 83 | 84 | constexpr Size ZeroSize = { 0, 0 }; 85 | 86 | constexpr Size InfiniteSize = { Infinity, Infinity }; 87 | 88 | constexpr Boundaries InfiniteBoundaries = { 89 | .min = ZeroSize, 90 | .max = InfiniteSize 91 | }; 92 | } 93 | 94 | #endif //ELEMENTOR_RENDER_H -------------------------------------------------------------------------------- /src/library/elements/SVG.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 30.07.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_SVG_H 6 | #define ELEMENTOR_SVG_H 7 | 8 | #include "../include.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace elementor::elements { 16 | struct SVGProps { 17 | std::optional> fromDOM = std::nullopt; 18 | std::optional> fromData; 19 | std::optional src; 20 | }; 21 | 22 | class SVG : public Element { 23 | public: 24 | explicit SVG(const std::shared_ptr& ctx) 25 | : Element(ctx) { 26 | } 27 | 28 | SVG(const std::shared_ptr& ctx, const SVGProps& props) 29 | : Element(ctx) { 30 | if (props.fromDOM.has_value()) fromSkSVGDOM(props.fromDOM.value()); 31 | if (props.fromData.has_value()) fromSkData(props.fromData.value()); 32 | if (props.src.has_value()) fromPath(props.src.value()); 33 | } 34 | 35 | static std::shared_ptr New( 36 | const std::shared_ptr& ctx, 37 | const SVGProps& props 38 | ) { 39 | return std::make_shared(ctx, props); 40 | } 41 | 42 | static std::shared_ptr New( 43 | const std::shared_ptr & ctx, 44 | std::shared_ptr & elementRef, 45 | const SVGProps& props 46 | ) { 47 | auto element = New(ctx, props); 48 | elementRef = element; 49 | return element; 50 | } 51 | 52 | static std::shared_ptr New(const std::shared_ptr& ctx) { 53 | return New(ctx, {}); 54 | } 55 | 56 | sk_sp getSkSVGDOM() const { 57 | return skSVGDOM; 58 | } 59 | 60 | void fromSkSVGDOM(const sk_sp& newSkSVGDOM) { 61 | skSVGDOM = newSkSVGDOM; 62 | } 63 | 64 | void fromSkStream(SkStream& stream); 65 | 66 | void fromSkData(sk_sp data); 67 | 68 | void fromPath(const std::string& path); 69 | 70 | void fromString(const char value[]); 71 | 72 | void paintBackground(SkCanvas* canvas, const ElementRect& rect) override; 73 | 74 | private: 75 | sk_sp skSVGDOM; 76 | sk_sp skImage; 77 | }; 78 | } 79 | 80 | 81 | #endif //ELEMENTOR_SVG_H 82 | -------------------------------------------------------------------------------- /src/components/Cursorable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 02.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_CURSORABLE_H 6 | #define ELEMENTOR_COMPONENTS_CURSORABLE_H 7 | 8 | #include "utility.h" 9 | #include "elementor.h" 10 | 11 | namespace elementor::components { 12 | struct CursorableProps { 13 | std::optional cursorShape; 14 | const std::shared_ptr & child = nullptr; 15 | }; 16 | 17 | class Cursorable : public Component { 18 | public: 19 | explicit Cursorable(const std::shared_ptr & ctx, const CursorableProps& props) 20 | : Component(ctx) { 21 | element = Hoverable::New(ctx, hoverable, { 22 | .onEnter = [this]() { 23 | this->ctx->getCursor()->set(cursorShape); 24 | return EventCallbackResponse::None; 25 | }, 26 | .onLeave = [this]() { 27 | this->ctx->getCursor()->set(CursorShape::Default); 28 | return EventCallbackResponse::None; 29 | }, 30 | }); 31 | 32 | if (props.cursorShape.has_value()) setCursorShape(props.cursorShape.value()); 33 | setChild(props.child); 34 | } 35 | 36 | static std::shared_ptr New( 37 | const std::shared_ptr & ctx, 38 | const CursorableProps& props 39 | ) { 40 | return std::make_shared(ctx, props); 41 | } 42 | 43 | static std::shared_ptr New( 44 | const std::shared_ptr & ctx, 45 | std::shared_ptr & elementRef, 46 | const CursorableProps& props 47 | ) { 48 | auto element = New(ctx, props); 49 | elementRef = element; 50 | return element; 51 | } 52 | 53 | static std::shared_ptr New(const std::shared_ptr & ctx) { 54 | return New(ctx, {}); 55 | } 56 | 57 | void setCursorShape(CursorShape newCursorShape) { 58 | cursorShape = newCursorShape; 59 | } 60 | 61 | [[nodiscard]] CursorShape getCursorShape() const { 62 | return cursorShape; 63 | } 64 | 65 | void setChild(const std::shared_ptr & child) { 66 | markChanged(); 67 | hoverable->setChild(child); 68 | } 69 | 70 | private: 71 | CursorShape cursorShape = CursorShape::Default; 72 | 73 | std::shared_ptr hoverable; 74 | }; 75 | } 76 | 77 | #endif //ELEMENTOR_COMPONENTS_CURSORABLE_H 78 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: base 2 | channels: 3 | - defaults 4 | dependencies: 5 | - blas=1.0=mkl 6 | - brotlipy=0.7.0=py39h2bbff1b_1003 7 | - ca-certificates=2023.08.22=haa95532_0 8 | - certifi=2023.7.22=py39haa95532_0 9 | - cffi=1.14.6=py39h2bbff1b_0 10 | - chardet=4.0.0=py39haa95532_1003 11 | - click=8.0.4=py39haa95532_0 12 | - colorama=0.4.6=py39haa95532_0 13 | - conda=4.10.3=py39haa95532_0 14 | - conda-package-handling=1.7.3=py39h8cc25b3_1 15 | - console_shortcut=0.1.1=4 16 | - cryptography=3.4.7=py39h71e12ea_0 17 | - freetype=2.12.1=ha860e81_0 18 | - giflib=5.2.1=h8cc25b3_3 19 | - idna=2.10=pyhd3eb1b0_0 20 | - intel-openmp=2023.1.0=h59b6b97_46319 21 | - joblib=1.2.0=py39haa95532_0 22 | - jpeg=9e=h2bbff1b_1 23 | - lerc=3.0=hd77b12b_0 24 | - libdeflate=1.17=h2bbff1b_1 25 | - libpng=1.6.39=h8cc25b3_0 26 | - libtiff=4.5.1=hd77b12b_0 27 | - libwebp=1.3.2=hbc33d0d_0 28 | - libwebp-base=1.3.2=h2bbff1b_0 29 | - lz4-c=1.9.4=h2bbff1b_0 30 | - menuinst=1.4.16=py39h2bbff1b_0 31 | - mkl=2023.1.0=h6b88ed4_46357 32 | - mkl-service=2.4.0=py39h2bbff1b_1 33 | - mkl_fft=1.3.8=py39h2bbff1b_0 34 | - mkl_random=1.2.4=py39h59b6b97_0 35 | - nltk=3.8.1=py39haa95532_0 36 | - numpy=1.26.0=py39h055cbcc_0 37 | - numpy-base=1.26.0=py39h65a83cf_0 38 | - openssl=1.1.1w=h2bbff1b_0 39 | - pillow=9.4.0=py39hd77b12b_1 40 | - pip=21.1.3=py39haa95532_0 41 | - powershell_shortcut=0.0.1=3 42 | - pycosat=0.6.3=py39h2bbff1b_0 43 | - pycparser=2.20=py_2 44 | - pyopenssl=20.0.1=pyhd3eb1b0_1 45 | - pysocks=1.7.1=py39haa95532_0 46 | - python=3.9.5=h6244533_3 47 | - pywin32=228=py39he774522_0 48 | - regex=2023.10.3=py39h2bbff1b_0 49 | - requests=2.25.1=pyhd3eb1b0_0 50 | - ruamel_yaml=0.15.100=py39h2bbff1b_0 51 | - setuptools=52.0.0=py39haa95532_0 52 | - six=1.16.0=pyhd3eb1b0_0 53 | - sqlite=3.36.0=h2bbff1b_0 54 | - tbb=2021.8.0=h59b6b97_0 55 | - tk=8.6.12=h2bbff1b_0 56 | - tqdm=4.61.2=pyhd3eb1b0_1 57 | - tzdata=2021a=h52ac0ba_0 58 | - urllib3=1.26.6=pyhd3eb1b0_1 59 | - vc=14.2=h21ff451_1 60 | - vs2015_runtime=14.27.29016=h5e58377_2 61 | - wheel=0.36.2=pyhd3eb1b0_0 62 | - win_inet_pton=1.1.0=py39haa95532_0 63 | - wincertstore=0.2=py39h2bbff1b_0 64 | - xz=5.4.2=h8cc25b3_0 65 | - yaml=0.2.5=he774522_0 66 | - zlib=1.2.13=h8cc25b3_0 67 | - zstd=1.5.5=hd43e919_0 68 | -------------------------------------------------------------------------------- /src/library/elements/ParagraphPlaceholder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 05.08.2022. 3 | // 4 | 5 | #include "ParagraphPlaceholder.h" 6 | 7 | namespace elementor::elements { 8 | 9 | skia::textlayout::PlaceholderAlignment ParagraphPlaceholder::getSkPlaceholderAlignment() const { 10 | switch (this->alignment) { 11 | case ParagraphPlaceholderAlignment::Baseline: 12 | return skia::textlayout::PlaceholderAlignment::kBaseline; 13 | case ParagraphPlaceholderAlignment::AboveBaseline: 14 | return skia::textlayout::PlaceholderAlignment::kAboveBaseline; 15 | case ParagraphPlaceholderAlignment::BelowBaseline: 16 | return skia::textlayout::PlaceholderAlignment::kBelowBaseline; 17 | case ParagraphPlaceholderAlignment::Top: 18 | return skia::textlayout::PlaceholderAlignment::kTop; 19 | case ParagraphPlaceholderAlignment::Bottom: 20 | return skia::textlayout::PlaceholderAlignment::kBottom; 21 | case ParagraphPlaceholderAlignment::Middle: 22 | return skia::textlayout::PlaceholderAlignment::kMiddle; 23 | default: 24 | return skia::textlayout::PlaceholderAlignment::kBaseline; 25 | } 26 | } 27 | 28 | skia::textlayout::TextBaseline ParagraphPlaceholder::getSkBaseline() const { 29 | switch (this->baseline) { 30 | case TextBaseline::Alphabetic: 31 | return skia::textlayout::TextBaseline::kAlphabetic; 32 | case TextBaseline::Ideographic: 33 | return skia::textlayout::TextBaseline::kIdeographic; 34 | default: 35 | return skia::textlayout::TextBaseline::kAlphabetic; 36 | } 37 | } 38 | 39 | skia::textlayout::PlaceholderStyle ParagraphPlaceholder::getSkPlaceholderStyle(const Size& size) const { 40 | return { 41 | size.width, 42 | size.height, 43 | getSkPlaceholderAlignment(), 44 | getSkBaseline(), 45 | offset 46 | }; 47 | } 48 | 49 | Size ParagraphPlaceholder::getSize(const Boundaries& boundaries) { 50 | if (doesNotHaveChild()) { 51 | return boundaries.max; 52 | } 53 | 54 | return child->getSize(boundaries); 55 | } 56 | 57 | std::vector ParagraphPlaceholder::getChildren(const ElementRect& rect) { 58 | if (doesNotHaveChild()) { 59 | return {}; 60 | } 61 | 62 | Rect childRect = { 63 | .size = rect.size, 64 | .position = { .x = 0, .y= 0 }, 65 | }; 66 | ElementWithRect childElement(child, childRect); 67 | return { childElement }; 68 | } 69 | } -------------------------------------------------------------------------------- /src/library/elements/Hoverable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_HOVERABLE_H 6 | #define ELEMENTOR_HOVERABLE_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct HoverableProps { 12 | std::optional> onEnter; 13 | std::optional> onLeave; 14 | const std::shared_ptr& child = nullptr; 15 | }; 16 | 17 | class Hoverable : public Element, public WithEventsHandlers, public WithChild { 18 | public: 19 | Hoverable(const std::shared_ptr& ctx, const HoverableProps& props) 20 | : Element(ctx) { 21 | if (props.onEnter.has_value()) onEnter(props.onEnter.value()); 22 | if (props.onLeave.has_value()) onLeave(props.onLeave.value()); 23 | setChild(props.child); 24 | } 25 | 26 | static std::shared_ptr New( 27 | const std::shared_ptr& ctx, 28 | const HoverableProps& props 29 | ) { 30 | return std::make_shared(ctx, props); 31 | } 32 | 33 | static std::shared_ptr New( 34 | const std::shared_ptr& ctx, 35 | std::shared_ptr& elementRef, 36 | const HoverableProps& props 37 | ) { 38 | auto element = New(ctx, props); 39 | elementRef = element; 40 | return element; 41 | } 42 | 43 | static std::shared_ptr New(const std::shared_ptr& ctx) { 44 | return New(ctx, {}); 45 | } 46 | 47 | void onEnter(const std::function& newCallbackEnter) { 48 | callbackEnter = newCallbackEnter; 49 | } 50 | 51 | void onLeave(const std::function& newCallbackLeave) { 52 | callbackLeave = newCallbackLeave; 53 | } 54 | 55 | void setChild(const std::shared_ptr & newChild) { 56 | markChanged(); 57 | child = newChild; 58 | } 59 | 60 | Size getSize(const Boundaries& boundaries) override; 61 | 62 | std::vector getChildren(const ElementRect& rect) override; 63 | 64 | std::vector> getEventsHandlers() override; 65 | 66 | private: 67 | bool hovered; 68 | 69 | std::function callbackEnter; 70 | std::function callbackLeave; 71 | }; 72 | } 73 | 74 | 75 | #endif //ELEMENTOR_HOVERABLE_H 76 | -------------------------------------------------------------------------------- /src/library/elements/Background.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_BACKGROUND_H 6 | #define ELEMENTOR_BACKGROUND_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct BackgroundProps { 12 | std::optional color; 13 | const std::shared_ptr & child = nullptr; 14 | }; 15 | 16 | class Background : public Element, public WithChild { 17 | public: 18 | Background(const std::shared_ptr & ctx, const BackgroundProps& props) 19 | : Element(ctx) { 20 | if (props.color.has_value()) setColor(props.color.value()); 21 | setChild(props.child); 22 | } 23 | 24 | static std::shared_ptr New( 25 | const std::shared_ptr & ctx, 26 | const BackgroundProps& props 27 | ) { 28 | return std::make_shared(ctx, props); 29 | } 30 | 31 | static std::shared_ptr New( 32 | const std::shared_ptr & ctx, 33 | std::shared_ptr & elementRef, 34 | const BackgroundProps& props 35 | ) { 36 | auto element = New(ctx, props); 37 | elementRef = element; 38 | return element; 39 | } 40 | 41 | static std::shared_ptr New(const std::shared_ptr & ctx) { 42 | return New(ctx, {}); 43 | } 44 | 45 | [[nodiscard]] SkColor getColor() const { 46 | return color; 47 | } 48 | 49 | void setColor(SkColor skColor) { 50 | markChanged(); 51 | color = skColor; 52 | } 53 | 54 | void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 55 | setColor(makeSkColorFromRGBA(r, g, b, a)); 56 | } 57 | 58 | void setColor(uint8_t r, uint8_t g, uint8_t b) { 59 | setColor(makeSkColorFromRGB(r, g, b)); 60 | } 61 | 62 | void setColor(const std::string& hex) { 63 | setColor(makeSkColorFromHex(hex)); 64 | } 65 | 66 | void setChild(const std::shared_ptr& newChild) { 67 | markChanged(); 68 | child = newChild; 69 | } 70 | 71 | void paintBackground(SkCanvas* canvas, const ElementRect& rect) override; 72 | 73 | Size getSize(const Boundaries& boundaries) override; 74 | 75 | std::vector getChildren(const ElementRect& rect) override; 76 | 77 | ClipBehavior getClipBehaviour() override { 78 | return ClipBehavior::Hard; 79 | } 80 | 81 | private: 82 | SkColor color = SK_ColorTRANSPARENT; 83 | }; 84 | } 85 | 86 | 87 | #endif //ELEMENTOR_BACKGROUND_H 88 | -------------------------------------------------------------------------------- /src/components/Scrollbar.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 08.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_SCROLLBAR_H 6 | #define ELEMENTOR_SCROLLBAR_H 7 | 8 | #include "utility.h" 9 | #include "elementor.h" 10 | 11 | namespace elementor::elements { 12 | struct ScrollbarProps { 13 | float thickness = 0.0f; 14 | float spacing = 0.0f; 15 | const std::shared_ptr& thumb = nullptr; 16 | const std::shared_ptr& child = nullptr; 17 | }; 18 | 19 | class Scrollbar : public Element, public WithEventsHandlers { 20 | public: 21 | Scrollbar(const std::shared_ptr& ctx, const ScrollbarProps& props) 22 | : Element(ctx) { 23 | thickness = props.thickness; 24 | spacing = props.spacing; 25 | setThumb(props.thumb); 26 | child = props.child; 27 | } 28 | 29 | static std::shared_ptr New( 30 | const std::shared_ptr& ctx, 31 | const ScrollbarProps& props 32 | ) { 33 | return std::make_shared(ctx, props); 34 | } 35 | 36 | static std::shared_ptr New( 37 | const std::shared_ptr& ctx, 38 | std::shared_ptr& elementRef, 39 | const ScrollbarProps& props 40 | ) { 41 | auto element = New(ctx, props); 42 | elementRef = element; 43 | return element; 44 | } 45 | 46 | static std::shared_ptr New(const std::shared_ptr& ctx) { 47 | return New(ctx, {}); 48 | } 49 | 50 | [[nodiscard]] float getThickness() const { 51 | return thickness; 52 | } 53 | 54 | void setThickness(float newThickness) { 55 | markChanged(); 56 | thickness = newThickness; 57 | } 58 | 59 | [[nodiscard]] float getSpacing() const { 60 | return spacing; 61 | } 62 | 63 | void setThumb(const std::shared_ptr& newThumb); 64 | 65 | void setChild(const std::shared_ptr& newChild) { 66 | markChanged(); 67 | child = newChild; 68 | } 69 | 70 | Size getSize(const Boundaries& boundaries) override; 71 | 72 | std::vector getChildren(const ElementRect& rect) override; 73 | 74 | std::vector> getEventsHandlers() override; 75 | 76 | private: 77 | float thickness; 78 | float spacing; 79 | std::shared_ptr thumbX = nullptr; 80 | std::shared_ptr thumbY = nullptr; 81 | std::shared_ptr child = nullptr; 82 | ElementRect lastRect; 83 | 84 | bool hovered; 85 | }; 86 | } 87 | 88 | #endif //ELEMENTOR_SCROLLBAR_H 89 | -------------------------------------------------------------------------------- /src/library/elements/Clickable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_CLICKABLE_H 6 | #define ELEMENTOR_CLICKABLE_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | class Clickable : public Element, public WithEventsHandlers, public WithChild { 12 | public: 13 | struct Props { 14 | std::optional > onClick; 15 | std::optional > onButton; 16 | const std::shared_ptr & child = nullptr; 17 | }; 18 | 19 | explicit Clickable(const std::shared_ptr & ctx) 20 | : Element(ctx) { 21 | } 22 | 23 | Clickable(const std::shared_ptr & ctx, const Props& props) 24 | : Element(ctx) { 25 | onClick(props.onClick); 26 | onButton(props.onButton); 27 | setChild(props.child); 28 | } 29 | 30 | static std::shared_ptr New( 31 | const std::shared_ptr & ctx, 32 | const Props& props 33 | ) { 34 | return std::make_shared(ctx, props); 35 | } 36 | 37 | static std::shared_ptr New( 38 | const std::shared_ptr & ctx, 39 | std::shared_ptr & elementRef, 40 | const Props& props 41 | ) { 42 | auto element = New(ctx, props); 43 | elementRef = element; 44 | return element; 45 | } 46 | 47 | static std::shared_ptr New(const std::shared_ptr & ctx) { 48 | return New(ctx, {}); 49 | } 50 | 51 | void onClick( 52 | const std::optional >& newCallback 53 | ) { 54 | callbackClick = newCallback; 55 | } 56 | 57 | void onButton( 58 | const std::optional >& newCallback 59 | ) { 60 | callbackButton = newCallback; 61 | } 62 | 63 | void setChild(const std::shared_ptr & newChild) { 64 | markChanged(); 65 | child = newChild; 66 | } 67 | 68 | Size getSize(const Boundaries& boundaries) override; 69 | 70 | std::vector getChildren(const ElementRect& rect) override; 71 | 72 | std::vector > getEventsHandlers() override; 73 | 74 | private: 75 | bool hovered = false; 76 | 77 | std::optional > callbackClick; 78 | std::optional > callbackButton; 79 | }; 80 | } 81 | 82 | 83 | #endif //ELEMENTOR_CLICKABLE_H 84 | -------------------------------------------------------------------------------- /src/components/IconButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 16.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_ICON_BUTTON_H 6 | #define ELEMENTOR_COMPONENTS_ICON_BUTTON_H 7 | 8 | #include "elementor.h" 9 | 10 | #include "./Button.h" 11 | 12 | namespace elementor::components { 13 | class IconButton : public Component { 14 | public: 15 | struct Props { 16 | std::string src; 17 | float size = 18.0f; 18 | std::optional width; 19 | std::optional height; 20 | std::string backgroundColor = "#ffffff"; 21 | std::string backgroundHoverColor = "#ebf0f0"; 22 | std::optional> onClick; 23 | }; 24 | 25 | explicit IconButton(const std::shared_ptr& ctx, const Props& props) 26 | : Component(ctx) { 27 | element = Hoverable::New(ctx, { 28 | .onEnter = [this, props] { 29 | iconBackground->setColor(props.backgroundHoverColor); 30 | return EventCallbackResponse::None; 31 | }, 32 | .onLeave = [this, props] { 33 | iconBackground->setColor(props.backgroundColor); 34 | return EventCallbackResponse::None; 35 | }, 36 | .child = Button::New(ctx, { 37 | .onClick = props.onClick, 38 | .child = Rounded::New(ctx, { 39 | .all = std::max( 40 | props.width.value_or(props.size), 41 | props.height.value_or(props.size) 42 | ) / 2, 43 | .child = Background::New(ctx, iconBackground, { 44 | .color = props.backgroundColor, 45 | .child = Padding::New(ctx, { 46 | .all = 4, 47 | .child = Width::New(ctx, { 48 | .width = props.width.value_or(props.size), 49 | .child = Height::New(ctx, { 50 | .height = props.height.value_or(props.size), 51 | .child = SVG::New(ctx, { 52 | .src = props.src, 53 | }) 54 | }) 55 | }) 56 | }), 57 | }) 58 | }) 59 | }), 60 | }); 61 | } 62 | 63 | static std::shared_ptr New( 64 | const std::shared_ptr& ctx, 65 | const Props& props 66 | ) { 67 | return std::make_shared(ctx, props); 68 | } 69 | 70 | static std::shared_ptr New( 71 | const std::shared_ptr& ctx, 72 | std::shared_ptr& elementRef, 73 | const Props& props 74 | ) { 75 | auto element = New(ctx, props); 76 | elementRef = element; 77 | return element; 78 | } 79 | 80 | static std::shared_ptr New(const std::shared_ptr& ctx) { 81 | return New(ctx, {}); 82 | } 83 | private: 84 | std::shared_ptr iconBackground; 85 | }; 86 | } 87 | 88 | #endif //ELEMENTOR_COMPONENTS_ICON_BUTTON_H 89 | -------------------------------------------------------------------------------- /src/components/ClickableOutside.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 02.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_CLICKABLE_OUTSIDE_H 6 | #define ELEMENTOR_COMPONENTS_CLICKABLE_OUTSIDE_H 7 | 8 | #include "utility.h" 9 | #include "elementor.h" 10 | 11 | #include "./Eventable.h" 12 | 13 | namespace elementor::components { 14 | struct ClickableOutsideProps { 15 | std::optional > onClickOutside; 16 | const std::shared_ptr & child = nullptr; 17 | }; 18 | 19 | class ClickableOutside : public Component { 20 | public: 21 | explicit ClickableOutside(const std::shared_ptr & ctx, const ClickableOutsideProps& props) 22 | : Component(ctx) { 23 | element = Eventable::New(ctx, { 24 | .globalEvents = { 25 | MouseButtonEvent::Handle([this](const auto& event) { 26 | if (!hovered && 27 | event->button == MouseButton::Left && 28 | event->action == KeyAction::Release && 29 | clickOutsideCallback.has_value()) { 30 | clickOutsideCallback.value()(); 31 | } 32 | 33 | return EventCallbackResponse::None; 34 | }) 35 | }, 36 | .child = Hoverable::New(ctx, hoverable, { 37 | .onEnter = [this]() { 38 | hovered = true; 39 | return EventCallbackResponse::None; 40 | }, 41 | .onLeave = [this]() { 42 | hovered = false; 43 | return EventCallbackResponse::None; 44 | }, 45 | }), 46 | }); 47 | 48 | if (props.onClickOutside.has_value()) onClickOutsize(props.onClickOutside.value()); 49 | if (props.child) setChild(props.child); 50 | } 51 | 52 | static std::shared_ptr New( 53 | const std::shared_ptr & ctx, 54 | const ClickableOutsideProps& props 55 | ) { 56 | return std::make_shared(ctx, props); 57 | } 58 | 59 | static std::shared_ptr New( 60 | const std::shared_ptr& ctx, 61 | std::shared_ptr& elementRef, 62 | const ClickableOutsideProps& props 63 | ) { 64 | auto element = New(ctx, props); 65 | elementRef = element; 66 | return element; 67 | } 68 | 69 | static std::shared_ptr New(const std::shared_ptr & ctx) { 70 | return New(ctx, {}); 71 | } 72 | 73 | void onClickOutsize(const std::function& callback) { 74 | clickOutsideCallback = callback; 75 | } 76 | 77 | void setChild(const std::shared_ptr & child) { 78 | markChanged(); 79 | hoverable->setChild(child); 80 | } 81 | 82 | private: 83 | bool hovered = false; 84 | std::shared_ptr hoverable; 85 | 86 | std::optional > clickOutsideCallback; 87 | }; 88 | } 89 | 90 | #endif //ELEMENTOR_COMPONENTS_CLICKABLE_OUTSIDE_H 91 | -------------------------------------------------------------------------------- /src/examples/button/ButtonDemo.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by admin on 08.05.2024. 3 | // 4 | 5 | #ifndef EXAMPLES_BUTTON_DEMO_H 6 | #define EXAMPLES_BUTTON_DEMO_H 7 | 8 | #include "../elementor.h" 9 | #include "../assets/include.h" 10 | 11 | class ButtonDemo : public Component { 12 | public: 13 | explicit ButtonDemo(const std::shared_ptr& ctx) 14 | : Component(ctx) { 15 | auto lipsumText = "Lorem ipsum dolor sit amet"; 16 | 17 | element = Background::New(ctx, { 18 | .color = "#fff", 19 | .child = Scrollbar::New(ctx, { 20 | .thickness = 8, 21 | .spacing = 2, 22 | .thumb = Rounded::New(ctx, { 23 | .all = 8, 24 | .child = Background::New(ctx, { 25 | .color = "#0a0", 26 | }) 27 | }), 28 | .child = Scrollable::New(ctx, { 29 | .direction = ScrollDirection::Vertical, 30 | .child = Padding::New(ctx, { 31 | .all = 24, 32 | .child = Align::New(ctx, { 33 | .coefficient = 0.5, 34 | .child = Flex::New(ctx, { 35 | .spacing = 2, 36 | .direction = FlexDirection::Column, 37 | .children = { 38 | TextButton::New(ctx, { 39 | .text = "Добавить", 40 | .fontColor = "#fff", 41 | .backgroundColor = "#ff5020", 42 | }), 43 | LikeButton::New(ctx), 44 | IconButton::New(ctx, { 45 | .src = asset("add.svg"), 46 | }), 47 | IconButton::New(ctx, { 48 | .src = asset("delete.svg"), 49 | }) 50 | } 51 | }) 52 | }) 53 | }) 54 | }) 55 | }) 56 | }); 57 | } 58 | 59 | static std::shared_ptr New(const std::shared_ptr& ctx) { 60 | return std::make_shared(ctx); 61 | } 62 | 63 | private: 64 | class LikeButton : public Component { 65 | public: 66 | explicit LikeButton(const std::shared_ptr& ctx) 67 | : Component(ctx) { 68 | element = TextButton::New(ctx, button, { 69 | .text = "Лайкнуть (0)", 70 | .fontColor = "#fff", 71 | .backgroundColor = "#ff5020", 72 | .onClick = [this]() { 73 | incCount(); 74 | return EventCallbackResponse::StopPropagation; 75 | } 76 | }); 77 | } 78 | 79 | static std::shared_ptr New(const std::shared_ptr& ctx) { 80 | return std::make_shared(ctx); 81 | } 82 | 83 | private: 84 | int count = 0; 85 | 86 | std::shared_ptr button; 87 | 88 | void setCount(int newCount) { 89 | count = newCount; 90 | button->setText(std::format("Лайкнуть ({})", count)); 91 | } 92 | 93 | void incCount() { 94 | setCount(count + 1); 95 | } 96 | }; 97 | }; 98 | 99 | #endif //EXAMPLES_BUTTON_DEMO_H 100 | -------------------------------------------------------------------------------- /src/library/platforms/gl/utility.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem 15.08.2022. 3 | // 4 | 5 | #include "utility.h" 6 | #include "../../debug.h" 7 | 8 | #include 9 | 10 | namespace elementor::platforms::gl { 11 | Size getWindowSize(GLFWwindow* window) { 12 | int width, height; 13 | glfwGetFramebufferSize(window, &width, &height); 14 | return { 15 | .width = (float)width, 16 | .height = (float)height 17 | }; 18 | } 19 | 20 | Position getWindowPosition(GLFWwindow* window) { 21 | int x, y; 22 | glfwGetWindowPos(window, &x, &y); 23 | return { 24 | .x = (float)x, 25 | .y = (float)y 26 | }; 27 | } 28 | 29 | Rect getWindowRect(GLFWwindow* window) { 30 | return { 31 | .size = getWindowSize(window), 32 | .position = getWindowPosition(window) 33 | }; 34 | } 35 | 36 | Size getMonitorSize(GLFWmonitor* monitor) { 37 | const GLFWvidmode* mode = glfwGetVideoMode(monitor); 38 | return { 39 | .width = (float)mode->width, 40 | .height = (float)mode->height 41 | }; 42 | } 43 | 44 | Size getMonitorPhysicalSize(GLFWmonitor* monitor) { 45 | int width, height; 46 | glfwGetMonitorPhysicalSize(monitor, &width, &height); 47 | return { 48 | .width = (float)width, 49 | .height = (float)height 50 | }; 51 | } 52 | 53 | Position getMonitorPosition(GLFWmonitor* monitor) { 54 | int x, y; 55 | glfwGetMonitorPos(monitor, &x, &y); 56 | return { 57 | .x = (float)x, 58 | .y = (float)y 59 | }; 60 | } 61 | 62 | Rect getMonitorRect(GLFWmonitor* monitor) { 63 | return { getMonitorSize(monitor), getMonitorPosition(monitor) }; 64 | } 65 | 66 | GLFWmonitor* getWindowMonitor(GLFWwindow* window) { 67 | Rect windowRect = getWindowRect(window); 68 | 69 | int monitorsSize = 0; 70 | GLFWmonitor** monitors = glfwGetMonitors(&monitorsSize); 71 | 72 | GLFWmonitor* closestMonitor; 73 | int maxOverlapArea = 0; 74 | 75 | for (unsigned int i = 0; i < monitorsSize; i++) { 76 | GLFWmonitor* monitor = monitors[i]; 77 | Rect monitorRect = getMonitorRect(monitor); 78 | 79 | float overlapWidth = 80 | windowRect.size.width - std::max((monitorRect.position.x - windowRect.position.x), 0.0f) - std::max( 81 | (windowRect.position.x + windowRect.size.width) - 82 | (monitorRect.position.x + monitorRect.size.width), 0.0f); 83 | float overlapHeight = 84 | windowRect.size.height - std::max((monitorRect.position.y - windowRect.position.y), 0.0f) - 85 | std::max((windowRect.position.y + windowRect.size.height) - 86 | (monitorRect.position.y + monitorRect.size.height), 0.0f); 87 | float overlapArea = overlapWidth * overlapHeight; 88 | if (overlapArea > maxOverlapArea) { 89 | closestMonitor = monitor; 90 | maxOverlapArea = overlapArea; 91 | } 92 | } 93 | 94 | return closestMonitor; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/library/platforms/gl/GLPlatform.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 14.04.2022. 3 | // 4 | 5 | #include "GLPlatform.h" 6 | 7 | #include "GLFW/glfw3.h" 8 | 9 | #include "include/gpu/gl/GrGLInterface.h" 10 | #include "include/core/SkColorSpace.h" 11 | #include "include/core/SkSurface.h" 12 | 13 | namespace elementor::platforms::gl { 14 | void onGLFWError([[maybe_unused]] int error, const char* description) { 15 | fputs(description, stderr); 16 | } 17 | 18 | GLPlatform::GLPlatform() { 19 | glfwInit(); 20 | glfwSetErrorCallback(onGLFWError); 21 | 22 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 23 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 24 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); 25 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 26 | glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); 27 | glfwWindowHint(GLFW_STENCIL_BITS, 0); 28 | glfwWindowHint(GLFW_ALPHA_BITS, 0); 29 | glfwWindowHint(GLFW_DEPTH_BITS, 0); 30 | 31 | eventLoop = std::make_unique([this]() { 32 | tick(); 33 | return windows.empty(); 34 | }); 35 | 36 | clipboard = std::make_shared(); 37 | perfomance = std::make_shared(); 38 | fontManager = std::make_shared(); 39 | } 40 | 41 | void GLPlatform::tick() { 42 | applyRnfQueue(); 43 | 44 | for (const std::shared_ptr& window: windows) { 45 | window->tick(); 46 | } 47 | 48 | perfomance->incrementFramesCount(); 49 | } 50 | 51 | void GLPlatform::run() { 52 | eventLoop->run(); 53 | glfwTerminate(); 54 | } 55 | 56 | void GLPlatform::requestNextFrame(const std::function& callback) { 57 | rnfQueue.push_back(callback); 58 | eventLoop->pend(); 59 | } 60 | 61 | void GLPlatform::requestNextFrame() { 62 | eventLoop->pend(); 63 | } 64 | 65 | void GLPlatform::applyRnfQueue() { 66 | if (rnfQueue.empty()) { 67 | return; 68 | } 69 | 70 | auto rnfCurrentQueue = rnfQueue; 71 | rnfQueue = {}; 72 | for (const std::function& callback: rnfCurrentQueue) { 73 | callback(); 74 | } 75 | } 76 | 77 | void GLPlatform::addWindow(const std::shared_ptr& window) { 78 | window->onClose([this, window]() { 79 | removeWindow(window); 80 | }); 81 | 82 | windows.push_back(window); 83 | } 84 | 85 | void GLPlatform::removeWindow(unsigned int index) { 86 | if (index < windows.size()) { 87 | windows.erase(windows.begin() + index); 88 | } 89 | } 90 | 91 | void GLPlatform::removeWindow(const std::shared_ptr& window) { 92 | for (unsigned int i = 0; i < windows.size(); ++i) { 93 | if (windows[i] == window) { 94 | removeWindow(i); 95 | return; 96 | } 97 | } 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/library/elements/Image.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 26.07.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_IMAGE_H 6 | #define ELEMENTOR_IMAGE_H 7 | 8 | #include "../include.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace elementor::elements { 15 | struct ImageProps { 16 | std::optional > fromImage = std::nullopt; 17 | std::optional > fromData = std::nullopt; 18 | std::optional src = std::nullopt; 19 | SkSamplingOptions samplingOptions = SkSamplingOptions(SkCubicResampler::Mitchell()); 20 | }; 21 | 22 | class Image : public Element { 23 | public: 24 | explicit Image(const std::shared_ptr & ctx) 25 | : Element(ctx) { 26 | } 27 | 28 | Image(const std::shared_ptr & ctx, const ImageProps& props) 29 | : Element(ctx) { 30 | if (props.fromImage.has_value()) fromSkImage(props.fromImage.value()); 31 | if (props.fromData.has_value()) fromSkData(props.fromData.value()); 32 | if (props.src.has_value()) fromPath(props.src.value()); 33 | setSamplingOptions(samplingOptions); 34 | } 35 | 36 | static std::shared_ptr New( 37 | const std::shared_ptr & ctx, 38 | const ImageProps& props 39 | ) { 40 | return std::make_shared(ctx, props); 41 | } 42 | 43 | static std::shared_ptr New( 44 | const std::shared_ptr & ctx, 45 | std::shared_ptr & elementRef, 46 | const ImageProps& props 47 | ) { 48 | auto element = New(ctx, props); 49 | elementRef = element; 50 | return element; 51 | } 52 | 53 | static std::shared_ptr New(const std::shared_ptr & ctx) { 54 | return New(ctx, {}); 55 | } 56 | 57 | sk_sp getSkImage() const { 58 | return skImage; 59 | } 60 | 61 | void fromSkImage(const sk_sp & newSkImage) { 62 | markChanged(); 63 | skImage = newSkImage; 64 | } 65 | 66 | void fromSkData(const sk_sp & data) { 67 | fromSkImage(SkImage::MakeFromEncoded(data)); 68 | } 69 | 70 | void fromPath(const std::string& path) { 71 | fromSkImage(makeImageFromFile(path)); 72 | } 73 | 74 | SkSamplingOptions getSamplingOptions() const { 75 | return samplingOptions; 76 | } 77 | 78 | void setSamplingOptions(const SkSamplingOptions& newSamplingOptions) { 79 | markChanged(); 80 | samplingOptions = newSamplingOptions; 81 | } 82 | 83 | void paintBackground(SkCanvas* canvas, const ElementRect& rect) override; 84 | 85 | Size getSize(const Boundaries& boundaries) override; 86 | 87 | private: 88 | sk_sp skImage; 89 | SkSamplingOptions samplingOptions; 90 | 91 | sk_sp makeImageFromFile(const std::string& path); 92 | }; 93 | } 94 | 95 | 96 | #endif //ELEMENTOR_IMAGE_H 97 | -------------------------------------------------------------------------------- /src/library/elements/Rounded.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 21.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_ROUNDED_H 6 | #define ELEMENTOR_ROUNDED_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct RectRadius { 12 | float topLeft; 13 | float topRight; 14 | float bottomLeft; 15 | float bottomRight; 16 | }; 17 | 18 | struct RoundedProps { 19 | public: 20 | std::optional all; 21 | std::optional top; 22 | std::optional bottom; 23 | std::optional left; 24 | std::optional right; 25 | std::optional topLeft; 26 | std::optional topRight; 27 | std::optional bottomLeft; 28 | std::optional bottomRight; 29 | const std::shared_ptr & child = nullptr; 30 | 31 | [[nodiscard]] RectRadius getRectRadius() const { 32 | return { 33 | topLeft.value_or(left.value_or(top.value_or(all.value_or(0)))), 34 | topRight.value_or(right.value_or(top.value_or(all.value_or(0)))), 35 | bottomLeft.value_or(left.value_or(bottom.value_or(all.value_or(0)))), 36 | bottomRight.value_or(right.value_or(bottom.value_or(all.value_or(0)))) 37 | }; 38 | } 39 | }; 40 | 41 | class Rounded : public Element, public WithChild { 42 | public: 43 | Rounded(const std::shared_ptr & ctx, const RoundedProps& props) 44 | : Element(ctx) { 45 | setRadius(props.getRectRadius()); 46 | setChild(props.child); 47 | } 48 | 49 | static std::shared_ptr New( 50 | const std::shared_ptr & ctx, 51 | const RoundedProps& props 52 | ) { 53 | return std::make_shared(ctx, props); 54 | } 55 | 56 | static std::shared_ptr New( 57 | const std::shared_ptr & ctx, 58 | std::shared_ptr & elementRef, 59 | const RoundedProps& props 60 | ) { 61 | auto element = New(ctx, props); 62 | elementRef = element; 63 | return element; 64 | } 65 | 66 | static std::shared_ptr New(const std::shared_ptr & ctx) { 67 | return New(ctx, {}); 68 | } 69 | 70 | void setRadius(RectRadius newValue) { 71 | markChanged(); 72 | rectRadius = newValue; 73 | } 74 | 75 | [[nodiscard]] RectRadius getRadius() const { 76 | return rectRadius; 77 | } 78 | 79 | void setChild(const std::shared_ptr & newChild) { 80 | markChanged(); 81 | child = newChild; 82 | } 83 | 84 | void paintBackground(SkCanvas* canvas, const ElementRect& rect) override; 85 | 86 | Size getSize(const Boundaries& boundaries) override; 87 | 88 | std::vector getChildren(const ElementRect& rect) override; 89 | 90 | ClipBehavior getClipBehaviour() override { 91 | return ClipBehavior::AntiAlias; 92 | } 93 | 94 | private: 95 | RectRadius rectRadius{}; 96 | }; 97 | } 98 | 99 | 100 | #endif //ELEMENTOR_ROUNDED_H 101 | -------------------------------------------------------------------------------- /src/examples/text/TextDemo.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by admin on 08.05.2024. 3 | // 4 | 5 | #ifndef EXAMPLES_TEXT_DEMO_H 6 | #define EXAMPLES_TEXT_DEMO_H 7 | 8 | #include "../elementor.h" 9 | 10 | class TextDemo : public Component { 11 | public: 12 | explicit TextDemo(const std::shared_ptr& ctx) 13 | : Component(ctx) { 14 | auto lipsumText = "Lorem ipsum dolor sit amet"; 15 | 16 | element = Background::New(ctx, { 17 | .color = "#fff", 18 | .child = Scrollbar::New(ctx, { 19 | .thickness = 8, 20 | .spacing = 2, 21 | .thumb = Rounded::New(ctx, { 22 | .all = 8, 23 | .child = Background::New(ctx, { 24 | .color = "#0a0", 25 | }) 26 | }), 27 | .child = Scrollable::New(ctx, { 28 | .direction = ScrollDirection::Vertical, 29 | .child = Padding::New(ctx, { 30 | .all = 24, 31 | .child = Align::New(ctx, { 32 | .coefficient = 0.5, 33 | .child = Flex::New(ctx, { 34 | .spacing = 2, 35 | .direction = FlexDirection::Column, 36 | .children = { 37 | Text::New(ctx, { 38 | .text = lipsumText, 39 | .fontSize = 12, 40 | .fontFamily = "Fira Code", 41 | }), 42 | Text::New(ctx, { 43 | .text = lipsumText, 44 | .fontSize = 14, 45 | .fontFamily = "Fira Code", 46 | }), 47 | Text::New(ctx, { 48 | .text = lipsumText, 49 | .fontSize = 16, 50 | .fontFamily = "Fira Code", 51 | }), 52 | Text::New(ctx, { 53 | .text = lipsumText, 54 | .fontSize = 18, 55 | .fontFamily = "Fira Code", 56 | }), 57 | Text::New(ctx, { 58 | .text = lipsumText, 59 | .fontSize = 20, 60 | .fontFamily = "Fira Code", 61 | }), 62 | Text::New(ctx, { 63 | .text = lipsumText, 64 | .fontSize = 22, 65 | .fontFamily = "Fira Code", 66 | }), 67 | Text::New(ctx, { 68 | .text = lipsumText, 69 | .fontSize = 22, 70 | .fontSlant = FontSlant::Italic, 71 | .fontFamily = "Fira Code", 72 | }), 73 | Text::New(ctx, { 74 | .text = lipsumText, 75 | .fontSize = 22, 76 | .fontWeight = 600, 77 | .fontSlant = FontSlant::Italic, 78 | .fontFamily = "Fira Code", 79 | }), 80 | Text::New(ctx, { 81 | .text = lipsumText, 82 | .fontSize = 22, 83 | .fontWeight = 1000, 84 | .fontSlant = FontSlant::Oblique, 85 | .fontFamily = "Fira Code", 86 | }), 87 | } 88 | }) 89 | }) 90 | }) 91 | }) 92 | }) 93 | }); 94 | } 95 | 96 | static std::shared_ptr New(const std::shared_ptr& ctx) { 97 | return std::make_shared(ctx); 98 | } 99 | }; 100 | 101 | #endif //EXAMPLES_TEXT_DEMO_H 102 | -------------------------------------------------------------------------------- /src/library/elements/Padding.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_PADDING_H 6 | #define ELEMENTOR_PADDING_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct PaddingsValue { 12 | float top = 0; 13 | float right = 0; 14 | float bottom = 0; 15 | float left = 0; 16 | }; 17 | 18 | struct PaddingProps { 19 | float all = 0; 20 | float x = 0; 21 | float y = 0; 22 | float top = 0; 23 | float right = 0; 24 | float bottom = 0; 25 | float left = 0; 26 | const std::shared_ptr & child = nullptr; 27 | }; 28 | 29 | class Padding : public Element, public WithChild { 30 | public: 31 | Padding(const std::shared_ptr & ctx, const PaddingProps& props) 32 | : Element(ctx) { 33 | setPaddings( 34 | (props.top == 0) ? ((props.y == 0) ? props.all : props.y) : props.top, 35 | (props.right == 0) ? ((props.x == 0) ? props.all : props.x) : props.right, 36 | (props.bottom == 0) ? ((props.y == 0) ? props.all : props.y) : props.bottom, 37 | (props.left == 0) ? ((props.x == 0) ? props.all : props.x) : props.left 38 | ); 39 | setChild(props.child); 40 | } 41 | 42 | static std::shared_ptr New( 43 | const std::shared_ptr & ctx, 44 | const PaddingProps& props 45 | ) { 46 | return std::make_shared(ctx, props); 47 | } 48 | 49 | static std::shared_ptr New( 50 | const std::shared_ptr & ctx, 51 | std::shared_ptr & elementRef, 52 | const PaddingProps& props 53 | ) { 54 | auto element = New(ctx, props); 55 | elementRef = element; 56 | return element; 57 | } 58 | 59 | static std::shared_ptr New(const std::shared_ptr & ctx) { 60 | return New(ctx, {}); 61 | } 62 | 63 | void setPaddings(float paddingTop, float paddingRight, float paddingBottom, float paddingLeft) { 64 | markChanged(); 65 | paddings = { paddingTop, paddingRight, paddingBottom, paddingLeft }; 66 | } 67 | 68 | void setPaddings(float paddingTop, float paddingX, float paddingBottom) { 69 | this->setPaddings(paddingTop, paddingX, paddingBottom, paddingX); 70 | } 71 | 72 | void setPaddings(float paddingY, float paddingX) { 73 | this->setPaddings(paddingY, paddingX, paddingY, paddingX); 74 | } 75 | 76 | void setPaddings(float paddingYX) { 77 | this->setPaddings(paddingYX, paddingYX, paddingYX, paddingYX); 78 | } 79 | 80 | [[nodiscard]] PaddingsValue getPaddings() { 81 | return paddings; 82 | } 83 | 84 | void setChild(const std::shared_ptr & newChild) { 85 | markChanged(); 86 | child = newChild; 87 | } 88 | 89 | Size getSize(const Boundaries& boundaries) override; 90 | 91 | std::vector getChildren(const ElementRect& rect) override; 92 | 93 | private: 94 | PaddingsValue paddings = { 0, 0, 0, 0 }; 95 | }; 96 | } 97 | 98 | 99 | #endif //ELEMENTOR_PADDING_H 100 | -------------------------------------------------------------------------------- /src/examples/tempconv/TempConv.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by admin on 17.02.2024. 3 | // 4 | 5 | #ifndef EXAMPLES_TEMP_CONV_H 6 | #define EXAMPLES_TEMP_CONV_H 7 | 8 | #include "../elementor.h" 9 | 10 | float parseFloat(const std::string& value) { 11 | try { 12 | return std::stof(value); 13 | } 14 | catch (...) { 15 | return 0.0f; 16 | } 17 | } 18 | 19 | float parseFloat(const std::u32string& valueU32) { 20 | return parseFloat(toUTF8(valueU32)); 21 | } 22 | 23 | class TempConv : public Component { 24 | public: 25 | explicit TempConv(const std::shared_ptr& ctx) 26 | : Component(ctx) { 27 | element = Background::New(ctx, { 28 | .color = "#fff", 29 | .child = Flex::New(ctx, { 30 | .spacing = 12, 31 | .alignment = FlexAlignment::Center, 32 | .crossAlignment = FlexCrossAlignment::Center, 33 | .children = { 34 | Flex::New(ctx, { 35 | .spacing = 4, 36 | .alignment = FlexAlignment::Center, 37 | .children = { 38 | Width::New(ctx, { 39 | .width = 100, 40 | .child = TextInput::New(ctx, textInputC, { 41 | .onInput = [this](const std::u32string& value) { 42 | setTempC(parseFloat(value)); 43 | } 44 | }), 45 | }), 46 | Text::New(ctx, { 47 | .text = "Celsius", 48 | .fontColor = "#000", 49 | .fontSize = 14, 50 | .fontFamily = "Arial", 51 | }), 52 | } 53 | }), 54 | Text::New(ctx, { 55 | .text = "=", 56 | .fontColor = "#000", 57 | .fontSize = 14, 58 | .fontFamily = "Arial", 59 | }), 60 | Flex::New(ctx, { 61 | .spacing = 4, 62 | .alignment = FlexAlignment::Center, 63 | .children = { 64 | Width::New(ctx, { 65 | .width = 100, 66 | .child = TextInput::New(ctx, textInputF, { 67 | .onInput = [this](const std::u32string& value) { 68 | setTempF(parseFloat(value)); 69 | } 70 | }), 71 | }), 72 | Text::New(ctx, { 73 | .text = "Fahrenheit", 74 | .fontColor = "#000", 75 | .fontSize = 14, 76 | .fontFamily = "Arial", 77 | }), 78 | } 79 | }) 80 | } 81 | }) 82 | }); 83 | 84 | setTempC(0); 85 | } 86 | 87 | static std::shared_ptr New(const std::shared_ptr& ctx) { 88 | return std::make_shared(ctx); 89 | } 90 | 91 | private: 92 | float tempC = 0; 93 | float tempF = 0; 94 | 95 | std::shared_ptr textInputC; 96 | std::shared_ptr textInputF; 97 | 98 | void setTempC(float newValue) { 99 | tempC = newValue; 100 | textInputC->setValue(std::format("{:.2f}", tempC)); 101 | 102 | tempF = tempC * 1.8f + 32.0f; 103 | textInputF->setValue(std::format("{:.2f}", tempF)); 104 | } 105 | 106 | void setTempF(float newValue) { 107 | tempF = newValue; 108 | textInputF->setValue(std::format("{:.2f}", tempF)); 109 | 110 | tempC = (tempF - 32.0f) / 1.8f; 111 | textInputC->setValue(std::format("{:.2f}", tempC)); 112 | } 113 | }; 114 | 115 | #endif //EXAMPLES_TEMP_CONV_H 116 | -------------------------------------------------------------------------------- /tests/compare_screenshots.py: -------------------------------------------------------------------------------- 1 | import os 2 | from os import path 3 | import shutil 4 | import unittest 5 | 6 | from PIL import Image, ImageChops, ImageEnhance, ImageOps 7 | 8 | 9 | def link(uri, label=None): 10 | if label is None: 11 | label = uri 12 | parameters = '' 13 | 14 | # OSC 8 ; params ; URI ST OSC 8 ;; ST 15 | escape_mask = '\033]8;{};{}\033\\{}\033]8;;\033\\' 16 | 17 | return escape_mask.format(parameters, uri, label) 18 | 19 | 20 | def calc_diff_percentage(diff): 21 | diff_count = sum(1 for _ in diff.getdata() if sum(_) > 0) 22 | pixels_count = diff.size[0] * diff.size[1] 23 | return diff_count / pixels_count 24 | 25 | 26 | def image_with_diff(img, diff): 27 | diff = ImageOps.invert(diff.convert('L')) 28 | 29 | img_darken = ImageEnhance.Brightness(img) 30 | img_darken = img_darken.enhance(0.64) 31 | 32 | red = Image.new('RGBA', img.size, color='#F00') 33 | img_diffed = Image.composite(img_darken, red, diff) 34 | return img_diffed 35 | 36 | 37 | class MyTestCase(unittest.TestCase): 38 | def test_screenshots(self): 39 | current_directory_path = path.dirname(__file__) 40 | 41 | screenshots_path = path.join(current_directory_path, 'screenshots') 42 | if not path.exists(screenshots_path): 43 | os.makedirs(screenshots_path) 44 | 45 | screenshots_new_path = path.join(current_directory_path, 'screenshots_new') 46 | if not path.exists(screenshots_new_path): 47 | os.makedirs(screenshots_new_path) 48 | 49 | screenshots_diff_path = path.join(current_directory_path, 'screenshots_diff') 50 | if path.exists(screenshots_diff_path): 51 | for diff in os.listdir(screenshots_diff_path): 52 | os.remove(path.join(screenshots_diff_path, diff)) 53 | else: 54 | os.makedirs(screenshots_diff_path) 55 | 56 | screenshots = set(os.listdir(screenshots_path)) 57 | screenshots_new = set(os.listdir(screenshots_new_path)) 58 | 59 | for screenshot in screenshots_new: 60 | if screenshot not in screenshots: 61 | shutil.copy(path.join(screenshots_new_path, screenshot), path.join(screenshots_path, screenshot)) 62 | screenshots.add(screenshot) 63 | 64 | for screenshot in screenshots: 65 | msg = f"\nMissing new screenshot for {link(path.join(screenshots_path, screenshot))}" 66 | self.assertIn(screenshot, screenshots_new, msg=msg) 67 | 68 | for screenshot in screenshots: 69 | old = Image.open(path.join(screenshots_path, screenshot)).convert('RGBA') 70 | new = Image.open(path.join(screenshots_new_path, screenshot)).convert('RGBA') 71 | self.assertEqual(old.size, new.size, msg='Screenshots sizes are different') 72 | 73 | diff = ImageChops.difference(old, new) 74 | image_with_diff(old, diff).save(path.join(screenshots_diff_path, screenshot)) 75 | msg = f"\nScreenshots have difference, checkout it here: {link(path.join(screenshots_diff_path, screenshot))}" 76 | self.assertAlmostEqual(calc_diff_percentage(diff), 0, delta=1e-7, msg=msg) 77 | 78 | 79 | if __name__ == '__main__': 80 | unittest.main() 81 | -------------------------------------------------------------------------------- /src/library/elements/Focusable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_FOCUSABLE_H 6 | #define ELEMENTOR_FOCUSABLE_H 7 | 8 | #include "../Element.h" 9 | #include "../Event.h" 10 | #include "../WithChild.h" 11 | 12 | namespace elementor::elements { 13 | struct FocusableProps { 14 | std::optional > onFocusChange; 15 | const std::shared_ptr & child = nullptr; 16 | }; 17 | 18 | class Focusable : public Element, public WithEventsHandlers, public WithChild { 19 | public: 20 | Focusable(const std::shared_ptr & ctx, const FocusableProps& props) 21 | : Element(ctx) { 22 | if (props.onFocusChange.has_value()) onFocusChange(props.onFocusChange.value()); 23 | setChild(props.child); 24 | } 25 | 26 | static std::shared_ptr New( 27 | const std::shared_ptr & ctx, 28 | const FocusableProps& props 29 | ) { 30 | return std::make_shared(ctx, props); 31 | } 32 | 33 | static std::shared_ptr New( 34 | const std::shared_ptr & ctx, 35 | std::shared_ptr & elementRef, 36 | const FocusableProps& props 37 | ) { 38 | auto element = New(ctx, props); 39 | elementRef = element; 40 | return element; 41 | } 42 | 43 | static std::shared_ptr New(const std::shared_ptr & ctx) { 44 | return New(ctx, {}); 45 | } 46 | 47 | void onFocusChange(const std::optional >& newCallback) { 48 | callbackFocusChange = newCallback; 49 | } 50 | 51 | [[nodiscard]] bool isFocusAllowed() const { 52 | return focusAllowed; 53 | } 54 | 55 | void disableFocus() { 56 | focusAllowed = false; 57 | } 58 | 59 | void enableFocus() { 60 | focusAllowed = true; 61 | } 62 | 63 | [[nodiscard]] bool isPendingBlur() const { 64 | return pendingBlur; 65 | } 66 | 67 | void blur() { 68 | if (!focused) { 69 | return; 70 | } 71 | 72 | pendingFocus = false; 73 | pendingBlur = true; 74 | } 75 | 76 | [[nodiscard]] bool isPendingFocus() const { 77 | return pendingFocus; 78 | } 79 | 80 | void focus() { 81 | if (focused) { 82 | return; 83 | } 84 | 85 | pendingFocus = true; 86 | pendingBlur = false; 87 | } 88 | 89 | [[nodiscard]] bool isFocused() const { 90 | return focused; 91 | } 92 | 93 | void setFocused(bool newFocused) { 94 | if (newFocused) { 95 | focus(); 96 | } else { 97 | blur(); 98 | } 99 | } 100 | 101 | void setChild(const std::shared_ptr & newChild) { 102 | markChanged(); 103 | child = newChild; 104 | } 105 | 106 | Size getSize(const Boundaries& boundaries) override; 107 | 108 | std::vector getChildren(const ElementRect& rect) override; 109 | 110 | std::vector > getEventsHandlers() override; 111 | 112 | private: 113 | bool focusAllowed = true; 114 | bool focused = false; 115 | bool pendingFocus = false; 116 | bool pendingBlur = false; 117 | 118 | std::optional > callbackFocusChange; 119 | }; 120 | } 121 | 122 | 123 | #endif //ELEMENTOR_FOCUSABLE_H 124 | -------------------------------------------------------------------------------- /src/library/elements/Draggable.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 21.05.2022. 3 | // 4 | 5 | #include "Draggable.h" 6 | 7 | namespace elementor::elements { 8 | Size Draggable::getSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | return child->getSize(boundaries); 14 | } 15 | 16 | void Draggable::paintBackground(SkCanvas* canvas, const ElementRect& rect) { 17 | lastRect = rect; 18 | } 19 | 20 | std::vector Draggable::getChildren(const ElementRect& rect) { 21 | if (doesNotHaveChild()) { 22 | return {}; 23 | } 24 | 25 | Rect childRect = { 26 | .size = rect.size, 27 | .position = { .x = 0, .y = 0 }, 28 | }; 29 | 30 | ElementWithRect childElement(child, childRect); 31 | return { childElement }; 32 | } 33 | 34 | std::vector> Draggable::getGlobalEventsHandlers() { 35 | return { 36 | MouseMoveEvent::Handle([this](const auto& event) { 37 | onApplicationMouseMoveEvent(event); 38 | return EventCallbackResponse::None; 39 | }), 40 | MouseButtonEvent::Handle([this](const auto& event) { 41 | onApplicationMouseButtonEvent(event); 42 | return EventCallbackResponse::None; 43 | }), 44 | }; 45 | } 46 | 47 | void Draggable::onApplicationMouseMoveEvent(const std::shared_ptr& event) { 48 | if (!dragging) { 49 | return; 50 | } 51 | 52 | previousCursorAbsolutePosition = cursorAbsolutePosition; 53 | cursorAbsolutePosition = { .x = event->x, .y = event->y }; 54 | 55 | if (previousCursorAbsolutePosition == InvalidPosition) { 56 | return; 57 | } 58 | 59 | cursorPosition = lastRect.absolutePositionToContained(cursorAbsolutePosition); 60 | 61 | if (callbackMove.has_value()) { 62 | Position cursorPositionDiff = { 63 | .x = cursorAbsolutePosition.x - previousCursorAbsolutePosition.x, 64 | .y = cursorAbsolutePosition.y - previousCursorAbsolutePosition.y, 65 | }; 66 | callbackMove.value()( 67 | cursorPosition, 68 | cursorAbsolutePosition, 69 | cursorPositionDiff 70 | ); 71 | } 72 | } 73 | 74 | void Draggable::onApplicationMouseButtonEvent(const std::shared_ptr& event) { 75 | if (!dragging || event->button != MouseButton::Left || event->action != KeyAction::Release) { 76 | return; 77 | } 78 | 79 | if (callbackEnd.has_value()) { 80 | callbackEnd.value()(cursorPosition, cursorAbsolutePosition); 81 | } 82 | 83 | setDragging(false); 84 | } 85 | 86 | std::vector> Draggable::getEventsHandlers() { 87 | return { 88 | HoverEvent::Handle([this](const auto& event) { 89 | hovered = event->hovered; 90 | return EventCallbackResponse::None; 91 | }), 92 | MouseButtonEvent::Handle([this](const auto& event) { 93 | if (dragging || !hovered || event->button != MouseButton::Left || event->action != KeyAction::Press) { 94 | return EventCallbackResponse::None; 95 | } 96 | 97 | if (callbackStart.has_value()) { 98 | bool startAllowed = callbackStart.value()(cursorPosition, cursorAbsolutePosition); 99 | if (!startAllowed) { 100 | return EventCallbackResponse::None; 101 | } 102 | } 103 | 104 | setDragging(true); 105 | 106 | return EventCallbackResponse::StopPropagation; 107 | }), 108 | }; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tests/make_screenshots.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by admin on 31.03.2024. 3 | // 4 | 5 | #define TEST 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../src/examples/elementor.h" 14 | #include "../src/examples/basic/Basic.h" 15 | #include "../src/examples/button/ButtonDemo.h" 16 | #include "../src/examples/counter/Counter.h" 17 | #include "../src/examples/crud/Crud.h" 18 | #include "../src/examples/flex/FlexDemo.h" 19 | #include "../src/examples/tempconv/TempConv.h" 20 | #include "../src/examples/text/TextDemo.h" 21 | #include "../src/examples/todo/Todo.h" 22 | 23 | struct Example { 24 | std::string name; 25 | Size size; 26 | std::function(const std::shared_ptr& ctx)> make; 27 | }; 28 | 29 | const std::vector Examples = { 30 | { 31 | .name = "basic", 32 | .size = { 420, 320 }, 33 | .make = [](const std::shared_ptr& ctx) { 34 | return Basic::New(ctx); 35 | } 36 | }, 37 | { 38 | .name = "button", 39 | .size = { 420, 320 }, 40 | .make = [](const std::shared_ptr& ctx) { 41 | return ButtonDemo::New(ctx); 42 | } 43 | }, 44 | { 45 | .name = "counter", 46 | .size = { 256, 256 }, 47 | .make = [](const std::shared_ptr& ctx) { 48 | return Counter::New(ctx); 49 | } 50 | }, 51 | { 52 | .name = "crud", 53 | .size = { 512, 256 }, 54 | .make = [](const std::shared_ptr& ctx) { 55 | return Crud::New(ctx); 56 | } 57 | }, 58 | { 59 | .name = "flex", 60 | .size = { 320, 420 }, 61 | .make = [](const std::shared_ptr& ctx) { 62 | return FlexDemo::New(ctx); 63 | } 64 | }, 65 | { 66 | .name = "tempconv", 67 | .size = { 384, 64 }, 68 | .make = [](const std::shared_ptr& ctx) { 69 | return TempConv::New(ctx); 70 | } 71 | }, 72 | { 73 | .name = "text", 74 | .size = { 512, 256 }, 75 | .make = [](const std::shared_ptr& ctx) { 76 | return TextDemo::New(ctx); 77 | } 78 | }, 79 | { 80 | .name = "todo", 81 | .size = { 320, 420 }, 82 | .make = [](const std::shared_ptr& ctx) { 83 | return Todo::New(ctx, { 84 | .filename = asset("test.md"), 85 | }); 86 | } 87 | } 88 | }; 89 | 90 | void saveImageToFile(const sk_sp& image, const std::string& filename) { 91 | sk_sp encodedData = image->encodeToData(SkEncodedImageFormat::kPNG, 100); 92 | if (!encodedData) { 93 | return; 94 | } 95 | 96 | std::ofstream file(filename, std::ios::binary); 97 | if (!file) { 98 | return; 99 | } 100 | 101 | file.write(static_cast(encodedData->data()), encodedData->size()); 102 | 103 | file.close(); 104 | } 105 | 106 | int main() { 107 | auto platform = std::make_shared(); 108 | 109 | auto window = std::make_shared(platform); 110 | platform->addWindow(window); 111 | 112 | for (const auto& example: Examples) { 113 | window->setSize(example.size); 114 | window->setRoot(example.make(window)); 115 | platform->__T_tick(2); 116 | saveImageToFile( 117 | window->__T_screenshot(), 118 | "../../../../tests/screenshots_new/" + example.name + ".png" 119 | ); 120 | } 121 | 122 | window->close(); 123 | } -------------------------------------------------------------------------------- /src/library/elements/ParagraphPlaceholder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 04.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_PARAGRAPH_PLACEHOLDER_H 6 | #define ELEMENTOR_PARAGRAPH_PLACEHOLDER_H 7 | 8 | #include "../include.h" 9 | 10 | #include "Text.h" 11 | 12 | namespace elementor::elements { 13 | enum class ParagraphPlaceholderAlignment { 14 | Baseline, 15 | AboveBaseline, 16 | BelowBaseline, 17 | Top, 18 | Bottom, 19 | Middle, 20 | }; 21 | 22 | struct ParagraphPlaceholderProps { 23 | std::optional alignment; 24 | std::optional baseline; 25 | std::optional offset; 26 | const std::shared_ptr & child = nullptr; 27 | }; 28 | 29 | class ParagraphPlaceholder : public Element, public WithChild { 30 | public: 31 | ParagraphPlaceholder(const std::shared_ptr & ctx, const ParagraphPlaceholderProps& props) 32 | : Element(ctx) { 33 | if (props.alignment.has_value()) setAlignment(props.alignment.value()); 34 | if (props.baseline.has_value()) setBaseline(props.baseline.value()); 35 | if (props.offset.has_value()) setOffset(props.offset.value()); 36 | setChild(props.child); 37 | } 38 | 39 | static std::shared_ptr New( 40 | const std::shared_ptr & ctx, 41 | const ParagraphPlaceholderProps& props 42 | ) { 43 | return std::make_shared(ctx, props); 44 | } 45 | 46 | static std::shared_ptr New( 47 | const std::shared_ptr & ctx, 48 | std::shared_ptr & elementRef, 49 | const ParagraphPlaceholderProps& props 50 | ) { 51 | auto element = New(ctx, props); 52 | elementRef = element; 53 | return element; 54 | } 55 | 56 | static std::shared_ptr New(const std::shared_ptr & ctx) { 57 | return New(ctx, {}); 58 | } 59 | 60 | ParagraphPlaceholderAlignment getAlignment() const { 61 | return alignment; 62 | } 63 | 64 | void setAlignment(ParagraphPlaceholderAlignment newAlignment) { 65 | markChanged(); 66 | alignment = newAlignment; 67 | } 68 | 69 | [[nodiscard]] TextBaseline getBaseline() const { 70 | return baseline; 71 | } 72 | 73 | void setBaseline(TextBaseline newBaseline) { 74 | markChanged(); 75 | baseline = newBaseline; 76 | } 77 | 78 | [[nodiscard]] float getOffset() const { 79 | return offset; 80 | } 81 | 82 | void setOffset(float newOffset) { 83 | markChanged(); 84 | offset = newOffset; 85 | } 86 | 87 | void setChild(const std::shared_ptr & newChild) { 88 | markChanged(); 89 | child = newChild; 90 | } 91 | 92 | skia::textlayout::PlaceholderStyle getSkPlaceholderStyle(const Size& size) const; 93 | 94 | Size getSize(const Boundaries& boundaries) override; 95 | 96 | std::vector getChildren(const ElementRect& rect) override; 97 | 98 | private: 99 | ParagraphPlaceholderAlignment alignment = ParagraphPlaceholderAlignment::Middle; 100 | TextBaseline baseline = TextBaseline::Alphabetic; 101 | float offset = 0; 102 | 103 | skia::textlayout::PlaceholderAlignment getSkPlaceholderAlignment() const; 104 | 105 | skia::textlayout::TextBaseline getSkBaseline() const; 106 | }; 107 | } 108 | 109 | 110 | #endif //ELEMENTOR_PARAGRAPH_PLACEHOLDER_H 111 | -------------------------------------------------------------------------------- /src/library/elements/Border.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 13.04.2022. 3 | // 4 | 5 | #include "Border.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace elementor::elements { 14 | Size Border::getSize(const Boundaries& boundaries) { 15 | if (doesNotHaveChild()) { 16 | return boundaries.max; 17 | } 18 | 19 | auto pixelScale = ctx->getPixelScale(); 20 | float widthScaled = width * pixelScale; 21 | 22 | Boundaries childBoundaries = { 23 | .min = { 24 | .width = std::max(boundaries.min.width - 2 * widthScaled, 0.0f), 25 | .height = std::max(boundaries.min.height - 2 * widthScaled, 0.0f), 26 | }, 27 | .max = { 28 | .width = std::max(boundaries.max.width - 2 * widthScaled, 0.0f), 29 | .height = std::max(boundaries.max.height - 2 * widthScaled, 0.0f) 30 | } 31 | }; 32 | 33 | Size childSize = child->getSize(childBoundaries); 34 | 35 | Size elementSize = { 36 | .width = childSize.width + 2 * widthScaled, 37 | .height = childSize.height + 2 * widthScaled 38 | }; 39 | 40 | return elementSize; 41 | } 42 | 43 | SkPaint Border::makeSkPaint() { 44 | auto pixelScale = ctx->getPixelScale(); 45 | float widthScaled = width * pixelScale; 46 | 47 | SkPaint paint; 48 | paint.setColor(color); 49 | paint.setStrokeWidth(widthScaled); 50 | paint.setStyle(SkPaint::kStroke_Style); 51 | paint.setAntiAlias(true); 52 | 53 | if (style == BorderStyle::Dotted) { 54 | SkPath path; 55 | path.addOval(SkRect::MakeWH(widthScaled / 2, widthScaled / 2)); 56 | auto pathEffect = SkPath1DPathEffect::Make(path, widthScaled, 0.0f, SkPath1DPathEffect::kRotate_Style); 57 | paint.setPathEffect(pathEffect); 58 | } 59 | 60 | if (style == BorderStyle::Dashed) { 61 | const SkScalar intervals[] = { 10.0f, 5.0f }; 62 | auto pathEffect = SkDashPathEffect::Make(intervals, 2, 0.0f); 63 | paint.setPathEffect(pathEffect); 64 | } 65 | 66 | return paint; 67 | } 68 | 69 | SkRRect Border::makeSkRRect(const ElementRect& rect) { 70 | auto pixelScale = ctx->getPixelScale(); 71 | float radiusXScaled = radiusX * pixelScale; 72 | float radiusYScaled = radiusY * pixelScale; 73 | float widthScaled = width * pixelScale; 74 | 75 | auto skPaint = makeSkPaint(); 76 | 77 | SkRect skRect = SkRect::MakeXYWH(widthScaled / 2, widthScaled / 2, rect.size.width - widthScaled, rect.size.height - widthScaled); 78 | SkRRect skRRect = SkRRect::MakeRectXY(skRect, radiusXScaled, radiusYScaled); 79 | 80 | return skRRect; 81 | } 82 | 83 | void Border::paintBackground(SkCanvas* canvas, const ElementRect& rect) { 84 | canvas->drawRRect(makeSkRRect(rect), makeSkPaint()); 85 | } 86 | 87 | std::vector Border::getChildren(const ElementRect& rect) { 88 | if (doesNotHaveChild()) { 89 | return {}; 90 | } 91 | 92 | auto pixelScale = ctx->getPixelScale(); 93 | float widthScaled = width * pixelScale; 94 | 95 | Rect childRect = { 96 | .size = { 97 | .width = rect.size.width - 2 * widthScaled, 98 | .height = rect.size.height - 2 * widthScaled 99 | }, 100 | .position = { 101 | .x = widthScaled, 102 | .y = widthScaled 103 | } 104 | }; 105 | 106 | ElementWithRect childElement(child, childRect); 107 | return { childElement }; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/library/elements/Scrollable.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 22.05.2022. 3 | // 4 | 5 | #include "Scrollable.h" 6 | 7 | namespace elementor::elements { 8 | Size Scrollable::getChildSize(const Boundaries& boundaries) { 9 | if (doesNotHaveChild()) { 10 | return boundaries.max; 11 | } 12 | 13 | Boundaries childBoundaries = { 14 | .min = boundaries.min, 15 | .max = { 16 | .width = isHorizontalScroll() ? Infinity : boundaries.max.width, 17 | .height = isVerticalScroll() ? Infinity : boundaries.max.height, 18 | } 19 | }; 20 | 21 | return child->getSize(childBoundaries); 22 | } 23 | 24 | Size Scrollable::getSize(const Boundaries& boundaries) { 25 | return fitSizeInBoundaries(getChildSize(boundaries), boundaries); 26 | } 27 | 28 | std::vector Scrollable::getChildren(const ElementRect& rect) { 29 | if (doesNotHaveChild()) { 30 | return {}; 31 | } 32 | 33 | Size childSize = getChildSize({ 34 | .min = ZeroSize, 35 | .max = { 36 | .width = rect.size.width, 37 | .height = rect.size.height, 38 | } 39 | }); 40 | 41 | if (lastChildSize != childSize || lastSize != rect.size) { 42 | markChanged(); 43 | } 44 | 45 | lastChildSize = childSize; 46 | lastSize = rect.size; 47 | 48 | setScrollLeft(scrollLeft); 49 | setScrollTop(scrollTop); 50 | 51 | Rect childRect = { 52 | .size = childSize, 53 | .position = { 54 | .x = -1 * scrollLeft, 55 | .y = -1 * scrollTop 56 | }, 57 | }; 58 | 59 | ElementWithRect childElement(child, childRect); 60 | return { childElement }; 61 | } 62 | 63 | std::vector> Scrollable::getEventsHandlers() { 64 | return { 65 | HoverEvent::Handle([this](const auto& event) { 66 | hovered = event->hovered; 67 | return EventCallbackResponse::None; 68 | }), 69 | ScrollEvent::Handle([this](const auto& event) { 70 | if (!hovered) { 71 | return EventCallbackResponse::None; 72 | } 73 | 74 | setScrollLeft(scrollLeft - event->xOffset * scrollAcceleration); 75 | setScrollTop(scrollTop - event->yOffset * scrollAcceleration); 76 | 77 | return EventCallbackResponse::StopPropagation; 78 | }) 79 | }; 80 | } 81 | 82 | std::vector> Scrollable::getGlobalEventsHandlers() { 83 | return { 84 | KeyboardEvent::Handle([this](const auto& event) { 85 | if (event->action == KeyAction::Release) { 86 | return EventCallbackResponse::None; 87 | } 88 | 89 | switch (event->key) { 90 | case KeyboardKey::Left: 91 | setScrollLeft(scrollLeft - scrollAcceleration); 92 | return EventCallbackResponse::StopPropagation; 93 | case KeyboardKey::Right: 94 | setScrollLeft(scrollLeft + scrollAcceleration); 95 | return EventCallbackResponse::StopPropagation; 96 | case KeyboardKey::Up: 97 | setScrollTop(scrollTop - scrollAcceleration); 98 | return EventCallbackResponse::StopPropagation; 99 | case KeyboardKey::Down: 100 | setScrollTop(scrollTop + scrollAcceleration); 101 | return EventCallbackResponse::StopPropagation; 102 | case KeyboardKey::Home: 103 | setScrollLeft(0); 104 | setScrollTop(0); 105 | return EventCallbackResponse::StopPropagation; 106 | case KeyboardKey::End: 107 | setScrollLeft(getMaxScrollLeft()); 108 | setScrollTop(getMaxScrollTop()); 109 | return EventCallbackResponse::StopPropagation; 110 | default: 111 | return EventCallbackResponse::None; 112 | } 113 | }) 114 | }; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/library/elements/Flex.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_FLEX_H 6 | #define ELEMENTOR_FLEX_H 7 | 8 | #include "../include.h" 9 | 10 | // TODO: https://css-tricks.com/snippets/css/a-guide-to-flexbox/ 11 | // - flex-wrap 12 | // - row/column gap 13 | // - align-content 14 | 15 | // TODO: Move to components 16 | 17 | // TODO: Add css-grid like 18 | 19 | namespace elementor::elements { 20 | enum class FlexDirection { 21 | Row, 22 | Column, 23 | }; 24 | 25 | enum class FlexAlignment { 26 | Start, 27 | Center, 28 | End, 29 | Stretch, 30 | }; 31 | 32 | enum class FlexCrossAlignment { 33 | Start, 34 | Center, 35 | End, 36 | SpaceBetween, 37 | SpaceEvenly, 38 | }; 39 | 40 | struct FlexProps { 41 | float spacing = 0; 42 | FlexDirection direction = FlexDirection::Row; 43 | FlexAlignment alignment = FlexAlignment::Start; 44 | FlexCrossAlignment crossAlignment = FlexCrossAlignment::Start; 45 | const std::vector >& children = {}; 46 | }; 47 | 48 | class Flex : public Element, public WithChildren { 49 | public: 50 | Flex(const std::shared_ptr & ctx, const FlexProps& props) 51 | : Element(ctx) { 52 | setSpacing(props.spacing); 53 | setDirection(props.direction); 54 | setAlignment(props.alignment); 55 | setCrossAlignment(props.crossAlignment); 56 | setChildren(props.children); 57 | } 58 | 59 | static std::shared_ptr New( 60 | const std::shared_ptr & ctx, 61 | const FlexProps& props 62 | ) { 63 | return std::make_shared(ctx, props); 64 | } 65 | 66 | static std::shared_ptr New( 67 | const std::shared_ptr & ctx, 68 | std::shared_ptr & elementRef, 69 | const FlexProps& props 70 | ) { 71 | auto element = New(ctx, props); 72 | elementRef = element; 73 | return element; 74 | } 75 | 76 | static std::shared_ptr New(const std::shared_ptr & ctx) { 77 | return New(ctx, {}); 78 | } 79 | 80 | [[nodiscard]] float getSpacing() const { 81 | return spacing; 82 | } 83 | 84 | void setSpacing(float newSpacing) { 85 | markChanged(); 86 | spacing = newSpacing; 87 | } 88 | 89 | [[nodiscard]] FlexDirection getDirection() { 90 | return direction; 91 | } 92 | 93 | void setDirection(FlexDirection newDirection) { 94 | markChanged(); 95 | direction = newDirection; 96 | } 97 | 98 | [[nodiscard]] FlexAlignment getAlignment() { 99 | return alignment; 100 | } 101 | 102 | void setAlignment(FlexAlignment newAlignment) { 103 | markChanged(); 104 | alignment = newAlignment; 105 | } 106 | 107 | [[nodiscard]] FlexCrossAlignment getCrossAlignment() { 108 | return crossAlignment; 109 | } 110 | 111 | void setCrossAlignment(FlexCrossAlignment newAlignment) { 112 | markChanged(); 113 | crossAlignment = newAlignment; 114 | } 115 | 116 | void setChildren(const std::vector >& newChildren) { 117 | markChanged(); 118 | children = newChildren; 119 | } 120 | 121 | Size getSize(const Boundaries& boundaries) override; 122 | 123 | std::vector getChildren(const ElementRect& rect) override; 124 | 125 | private: 126 | float spacing = 0; 127 | FlexDirection direction = FlexDirection::Row; 128 | FlexAlignment alignment = FlexAlignment::Start; 129 | FlexCrossAlignment crossAlignment = FlexCrossAlignment::Start; 130 | }; 131 | 132 | std::shared_ptr flex(); 133 | } 134 | 135 | 136 | #endif //ELEMENTOR_FLEX_H 137 | -------------------------------------------------------------------------------- /src/examples/flex/FlexDemo.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by admin on 08.05.2024. 3 | // 4 | 5 | #ifndef EXAMPLES_FLEX_DEMO_H 6 | #define EXAMPLES_FLEX_DEMO_H 7 | 8 | #include "../elementor.h" 9 | 10 | std::shared_ptr 11 | Box(const std::shared_ptr& ctx, std::string text) { 12 | return Rounded::New(ctx, { 13 | .all = 8, 14 | .child = Background::New(ctx, { 15 | .color = "#ff5020", 16 | .child = Align::New(ctx, { 17 | .coefficient = 0.5, 18 | .child = Padding::New(ctx, { 19 | .bottom = 4, 20 | .child = Text::New(ctx, { 21 | .text = text, 22 | .fontColor = "#fff", 23 | .fontSize = 14, 24 | .fontWeight = 700, 25 | .fontFamily = "Fira Code", 26 | }) 27 | }) 28 | }) 29 | }) 30 | }); 31 | } 32 | 33 | class FlexDemo : public Component { 34 | public: 35 | explicit FlexDemo(const std::shared_ptr& ctx) 36 | : Component(ctx) { 37 | auto lipsumText = "Lorem ipsum dolor sit amet"; 38 | 39 | element = Background::New(ctx, { 40 | .color = "#fff", 41 | .child = Padding::New(ctx, { 42 | .all = 24, 43 | .child = Align::New(ctx, { 44 | .coefficient = 0.5, 45 | .child = Flex::New(ctx, { 46 | .spacing = 8, 47 | .direction = FlexDirection::Column, 48 | .children = { 49 | Height::New(ctx, { 50 | .height = 50, 51 | .child = Flex::New(ctx, { 52 | .spacing = 8, 53 | .alignment = FlexAlignment::Stretch, 54 | .children = { 55 | Width::New(ctx, { 56 | .width = 100, 57 | .child = Box(ctx, "100px 50px"), 58 | }), 59 | Width::New(ctx, { 60 | .width = 150, 61 | .child = Box(ctx, "150px 50px"), 62 | }), 63 | Flexible::New(ctx, { 64 | .grow = 1, 65 | .child = Box(ctx, "1fl 50px"), 66 | }) 67 | } 68 | }), 69 | }), 70 | Flexible::New(ctx, { 71 | .grow = 1, 72 | .child = Flex::New(ctx, { 73 | .spacing = 8, 74 | .alignment = FlexAlignment::Stretch, 75 | .children = { 76 | Flexible::New(ctx, { 77 | .grow = 1, 78 | .child = Box(ctx, "1fl 1fl") 79 | }), 80 | Flexible::New(ctx, { 81 | .grow = 2, 82 | .child = Box(ctx, "2fl 1lf") 83 | }), 84 | Flexible::New(ctx, { 85 | .grow = 3, 86 | .child = Box(ctx, "3fl 1fl") 87 | }), 88 | } 89 | }), 90 | }), 91 | Flexible::New(ctx, { 92 | .grow = 2, 93 | .child = Flex::New(ctx, { 94 | .spacing = 8, 95 | .alignment = FlexAlignment::Stretch, 96 | .children = { 97 | Flexible::New(ctx, { 98 | .grow = 2, 99 | .child = Box(ctx, "2fl 2fl") 100 | }), 101 | Flexible::New(ctx, { 102 | .grow = 2, 103 | .child = Box(ctx, "2fl 2lf") 104 | }), 105 | Width::New(ctx, { 106 | .width = 100, 107 | .child = Box(ctx, "100px 2lf"), 108 | }), 109 | Flexible::New(ctx, { 110 | .grow = 2, 111 | .child = Box(ctx, "2fl 2fl") 112 | }), 113 | } 114 | }), 115 | }), 116 | } 117 | }) 118 | }) 119 | }) 120 | }); 121 | } 122 | 123 | static std::shared_ptr New(const std::shared_ptr& ctx) { 124 | return std::make_shared(ctx); 125 | } 126 | }; 127 | 128 | #endif //EXAMPLES_FLEX_DEMO_H 129 | -------------------------------------------------------------------------------- /src/library/ApplicationTree.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 04.02.2024. 3 | // 4 | 5 | #ifndef ELEMENTOR_APPLICATION_TREE_H 6 | #define ELEMENTOR_APPLICATION_TREE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "Element.h" 16 | 17 | namespace elementor { 18 | class ApplicationTree { 19 | public: 20 | class Node : public std::enable_shared_from_this { 21 | friend ApplicationTree; 22 | public: 23 | constexpr static int DEFAULT_BEFORE_CACHE_RENDERS = 60; 24 | 25 | static std::shared_ptr New( 26 | const std::shared_ptr& element, 27 | const ElementRect& rect, 28 | const Rect& boundary, 29 | const std::shared_ptr& parent = nullptr 30 | ); 31 | 32 | bool operator==(const Node& other) const { 33 | return element == other.element; 34 | } 35 | 36 | [[nodiscard]] const std::shared_ptr& getElement() const { 37 | return element; 38 | } 39 | 40 | [[nodiscard]] const Rect& getRect() const { 41 | return rect; 42 | } 43 | 44 | EventCallbackResponse onEvent(const std::shared_ptr& event); 45 | 46 | EventCallbackResponse bubbleEvent(const std::shared_ptr& event); 47 | 48 | void print(std::ostream& os, unsigned level = 0) const; 49 | 50 | void removeCache(); 51 | 52 | void removeCacheDeep(); 53 | 54 | void checkIfChanged(); 55 | 56 | private: 57 | std::shared_ptr element; 58 | ElementRect rect; 59 | Rect childBoundary; 60 | std::vector> children; 61 | std::shared_ptr parent; 62 | 63 | std::vector> eventsHandlers; 64 | std::vector> globalEventsHandlers; 65 | 66 | sk_sp drawCachedImage; 67 | int beforeCacheRenders = DEFAULT_BEFORE_CACHE_RENDERS; 68 | 69 | bool changed = false; 70 | bool childrenChanged = false; 71 | bool childrenCached = false; 72 | bool deepChanged = false; 73 | 74 | std::shared_ptr findFirstNode( 75 | const std::function& node)>& predicate 76 | ); 77 | 78 | std::shared_ptr findLastNode( 79 | const std::function& node)>& predicate 80 | ); 81 | 82 | void updateChildren(); 83 | 84 | bool isChildrenChanged(); 85 | 86 | void updateChildrenIfChanged(); 87 | 88 | void clipCanvas(SkCanvas* canvas); 89 | 90 | void draw(SkCanvas* canvas, bool withChildrenCache); 91 | 92 | void drawToCache(SkCanvas* canvas); 93 | 94 | void drawCache(SkCanvas* canvas); 95 | 96 | void drawWithCache(SkCanvas* canvas); 97 | }; 98 | 99 | ApplicationTree(const std::shared_ptr& rootElement, const Size& rootSize); 100 | 101 | void print(std::ostream& os) const { 102 | root->print(os, 0); 103 | } 104 | 105 | void print() const { 106 | print(std::cout); 107 | } 108 | 109 | [[nodiscard]] std::shared_ptr findFirstNode( 110 | const std::function& node)>& predicate 111 | ) const; 112 | 113 | [[nodiscard]] std::shared_ptr findLastNode( 114 | const std::function& node)>& predicate 115 | ) const; 116 | 117 | [[nodiscard]] EventsHandlersMap getGlobalEventsHandlers() const; 118 | 119 | void visit(const std::function& node)>& visitor) const; 120 | 121 | void resize(const Size& size); 122 | 123 | void draw(SkCanvas* canvas); 124 | 125 | void checkIfChanged(); 126 | 127 | void updateChanged(); 128 | 129 | private: 130 | std::shared_ptr root; 131 | }; 132 | } 133 | 134 | 135 | #endif //ELEMENTOR_APPLICATION_TREE_H 136 | -------------------------------------------------------------------------------- /src/library/elements/Draggable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 18.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_DRAGGABLE_H 6 | #define ELEMENTOR_DRAGGABLE_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | struct DraggableProps { 12 | std::optional> onStart; 13 | std::optional> onEnd; 14 | std::optional> onMove; 15 | const std::shared_ptr& child = nullptr; 16 | }; 17 | 18 | class Draggable : public Element, public WithChild, public WithEventsHandlers, public WithGlobalEventsHandlers { 19 | public: 20 | using StartCallback = std::function; 21 | using EndCallback = std::function; 22 | using MoveCallback = std::function; 23 | 24 | Draggable(const std::shared_ptr& ctx, const DraggableProps& props) 25 | : Element(ctx) { 26 | if (props.onStart.has_value()) onStart(props.onStart.value()); 27 | if (props.onEnd.has_value()) onEnd(props.onEnd.value()); 28 | if (props.onMove.has_value()) onMove(props.onMove.value()); 29 | setChild(props.child); 30 | } 31 | 32 | static std::shared_ptr New( 33 | const std::shared_ptr& ctx, 34 | const DraggableProps& props 35 | ) { 36 | return std::make_shared(ctx, props); 37 | } 38 | 39 | static std::shared_ptr New( 40 | const std::shared_ptr& ctx, 41 | std::shared_ptr& elementRef, 42 | const DraggableProps& props 43 | ) { 44 | auto element = New(ctx, props); 45 | elementRef = element; 46 | return element; 47 | } 48 | 49 | static std::shared_ptr New(const std::shared_ptr& ctx) { 50 | return New(ctx, {}); 51 | } 52 | 53 | void setDragging(bool newDragging) { 54 | dragging = newDragging; 55 | 56 | cursorPosition = InvalidPosition; 57 | cursorAbsolutePosition = InvalidPosition; 58 | previousCursorAbsolutePosition = InvalidPosition; 59 | } 60 | 61 | void onStart(const std::optional& newCallback) { 62 | callbackStart = newCallback; 63 | } 64 | 65 | void onEnd(const std::optional& newCallback) { 66 | callbackEnd = newCallback; 67 | } 68 | 69 | void onMove(const std::optional& newCallback) { 70 | callbackMove = newCallback; 71 | } 72 | 73 | void setChild(const std::shared_ptr& newChild) { 74 | markChanged(); 75 | child = newChild; 76 | } 77 | 78 | Size getSize(const Boundaries& boundaries) override; 79 | 80 | void paintBackground(SkCanvas* canvas, const ElementRect& rect) override; 81 | 82 | std::vector getChildren(const ElementRect& rect) override; 83 | 84 | std::vector> getEventsHandlers() override; 85 | 86 | std::vector> getGlobalEventsHandlers() override; 87 | 88 | private: 89 | ElementRect lastRect; 90 | Position cursorPosition; 91 | Position cursorAbsolutePosition; 92 | Position previousCursorAbsolutePosition; 93 | bool hovered = false; 94 | bool dragging = false; 95 | 96 | void onApplicationMouseMoveEvent(const std::shared_ptr& event); 97 | 98 | void onApplicationMouseButtonEvent(const std::shared_ptr& event); 99 | 100 | std::optional callbackStart; 101 | std::optional callbackEnd; 102 | std::optional callbackMove; 103 | }; 104 | } 105 | 106 | 107 | #endif //ELEMENTOR_DRAGGABLE_H 108 | -------------------------------------------------------------------------------- /src/library/elements/Border.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 27.04.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_BORDER_H 6 | #define ELEMENTOR_BORDER_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | enum class BorderStyle { 12 | Solid, 13 | Dotted, 14 | Dashed, 15 | }; 16 | 17 | struct BorderProps { 18 | std::optional radiusX = std::nullopt; 19 | std::optional radiusY = std::nullopt; 20 | float radius = 0; 21 | float width = 1; 22 | const std::string_view& color = ""; 23 | BorderStyle style = BorderStyle::Solid; 24 | const std::shared_ptr & child = nullptr; 25 | }; 26 | 27 | class Border : public Element, public WithChild { 28 | public: 29 | Border(const std::shared_ptr & ctx, const BorderProps& props) 30 | : Element(ctx) { 31 | setRadius( 32 | props.radiusX.value_or(props.radius), 33 | props.radiusY.value_or(props.radius) 34 | ); 35 | setWidth(props.width); 36 | setStyle(props.style); 37 | setColor(props.color); 38 | setChild(props.child); 39 | } 40 | 41 | static std::shared_ptr New( 42 | const std::shared_ptr & ctx, 43 | const BorderProps& props 44 | ) { 45 | return std::make_shared(ctx, props); 46 | } 47 | 48 | static std::shared_ptr New( 49 | const std::shared_ptr & ctx, 50 | std::shared_ptr & elementRef, 51 | const BorderProps& props 52 | ) { 53 | auto element = New(ctx, props); 54 | elementRef = element; 55 | return element; 56 | } 57 | 58 | static std::shared_ptr New(const std::shared_ptr & ctx) { 59 | return New(ctx, {}); 60 | } 61 | 62 | [[nodiscard]] SkColor getColor() const { 63 | return color; 64 | } 65 | 66 | void setColor(SkColor skColor) { 67 | markChanged(); 68 | color = skColor; 69 | } 70 | 71 | void setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 72 | setColor(makeSkColorFromRGBA(r, g, b, a)); 73 | } 74 | 75 | void setColor(uint8_t r, uint8_t g, uint8_t b) { 76 | setColor(makeSkColorFromRGB(r, g, b)); 77 | } 78 | 79 | void setColor(const std::string_view& hex) { 80 | setColor(makeSkColorFromHex(std::string(hex))); 81 | } 82 | 83 | [[nodiscard]] float getWidth() const { 84 | return width; 85 | } 86 | 87 | void setWidth(float newWidth) { 88 | markChanged(); 89 | width = newWidth; 90 | } 91 | 92 | [[nodiscard]] float getRadiusX() const { 93 | return radiusX; 94 | } 95 | 96 | [[nodiscard]] float getRadiusY() const { 97 | return radiusY; 98 | } 99 | 100 | void setRadius(float newRadiusX, float newRadiusY) { 101 | markChanged(); 102 | radiusX = newRadiusX; 103 | radiusY = newRadiusY; 104 | } 105 | 106 | void setRadius(float radiusXY) { 107 | setRadius(radiusXY, radiusXY); 108 | } 109 | 110 | [[nodiscard]] BorderStyle getStyle() const { 111 | return style; 112 | } 113 | 114 | void setStyle(BorderStyle newStyle) { 115 | markChanged(); 116 | style = newStyle; 117 | } 118 | 119 | void setChild(const std::shared_ptr & newChild) { 120 | markChanged(); 121 | child = newChild; 122 | } 123 | 124 | Size getSize(const Boundaries& boundaries) override; 125 | 126 | void paintBackground(SkCanvas* canvas, const ElementRect& rect) override; 127 | 128 | std::vector getChildren(const ElementRect& rect) override; 129 | 130 | private: 131 | float width = 0.0; 132 | float radiusX = 0.0; 133 | float radiusY = 0.0; 134 | SkColor color; 135 | BorderStyle style = BorderStyle::Solid; 136 | 137 | SkPaint makeSkPaint(); 138 | SkRRect makeSkRRect(const ElementRect& rect); 139 | }; 140 | 141 | std::shared_ptr border(); 142 | } 143 | 144 | 145 | #endif //ELEMENTOR_BORDER_H 146 | -------------------------------------------------------------------------------- /src/library/elements/Paragraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 04.08.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_PARAGRAPH_H 6 | #define ELEMENTOR_PARAGRAPH_H 7 | 8 | #include "../include.h" 9 | 10 | #include 11 | 12 | #include "Text.h" 13 | 14 | namespace elementor::elements { 15 | 16 | struct ParagraphProps { 17 | std::optional textAlign; 18 | std::optional textDirection; 19 | const std::vector >& children = {}; 20 | }; 21 | 22 | class Paragraph : public Element, private WithChildren { 23 | public: 24 | Paragraph(const std::shared_ptr & ctx, const ParagraphProps& props) 25 | : Element(ctx) { 26 | if (props.textAlign.has_value()) setTextAlign(props.textAlign.value()); 27 | if (props.textDirection.has_value()) setTextDirection(props.textDirection.value()); 28 | setChildren(props.children); 29 | } 30 | 31 | static std::shared_ptr New( 32 | const std::shared_ptr & ctx, 33 | const ParagraphProps& props 34 | ) { 35 | return std::make_shared(ctx, props); 36 | } 37 | 38 | static std::shared_ptr New( 39 | const std::shared_ptr & ctx, 40 | std::shared_ptr & elementRef, 41 | const ParagraphProps& props 42 | ) { 43 | auto element = New(ctx, props); 44 | elementRef = element; 45 | return element; 46 | } 47 | 48 | static std::shared_ptr New(const std::shared_ptr & ctx) { 49 | return New(ctx, {}); 50 | } 51 | 52 | [[nodiscard]] TextAlign getTextAlign() const { 53 | return textAlign; 54 | } 55 | 56 | void setTextAlign(TextAlign newTextAlign) { 57 | markChanged(); 58 | textAlign = newTextAlign; 59 | } 60 | 61 | [[nodiscard]] TextDirection getTextDirection() const { 62 | return textDirection; 63 | } 64 | 65 | void setTextDirection(TextDirection newTextDirection) { 66 | markChanged(); 67 | textDirection = newTextDirection; 68 | } 69 | 70 | std::optional getGlyphRect(unsigned glyphOffset); 71 | 72 | Size getSize(const Boundaries& boundaries) override; 73 | 74 | void paintBackground(SkCanvas* canvas, const ElementRect& rect) override; 75 | 76 | std::vector getChildren(const ElementRect& rect) override; 77 | 78 | void setChildren(const std::vector >& newChildren); 79 | 80 | bool popChanged() override; 81 | 82 | protected: 83 | void markChanged() override { 84 | Element::markChanged(); 85 | skParagraph = nullptr; 86 | } 87 | 88 | private: 89 | TextAlign textAlign = TextAlign::Left; 90 | TextDirection textDirection = TextDirection::LeftToRight; 91 | 92 | std::unique_ptr skParagraph; 93 | std::vector > placeholdersChildren; 94 | 95 | float lastPixelScale; 96 | 97 | bool popTextChildrenChanged(); 98 | 99 | sk_sp makeFontCollection( 100 | const std::shared_ptr & ctx 101 | ) const; 102 | 103 | skia::textlayout::TextStyle makeDefaultTextStyle() const; 104 | 105 | skia::textlayout::ParagraphStyle makeParagraphStyle() const; 106 | 107 | skia::textlayout::ParagraphBuilderImpl makeBuilder(const std::shared_ptr & ctx) const; 108 | 109 | skia::textlayout::TextAlign getSkTextAlign() const; 110 | 111 | skia::textlayout::TextDirection getSkTextDirection() const; 112 | 113 | skia::textlayout::PlaceholderStyle makeElementChildPlaceholderStyle(const Size& childSize) const; 114 | 115 | skia::textlayout::PlaceholderStyle makeChildPlaceholderStyle(const std::shared_ptr & child) const; 116 | 117 | std::unique_ptr makeSkParagraph() const; 118 | 119 | void updateSkParagraph(); 120 | }; 121 | 122 | void paragraph(); 123 | } 124 | 125 | 126 | #endif //ELEMENTOR_PARAGRAPH_H 127 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.debug", "mode.release") 2 | 3 | set_languages("c99", "cxx23") 4 | 5 | includes("third_party/skia-build.lua") 6 | add_requires("skia-build") 7 | 8 | add_requires("glfw 3.3.8") 9 | if is_plat("windows") then 10 | add_requires("glew") 11 | end 12 | 13 | if is_mode("debug") then 14 | add_defines("DEBUG") 15 | -- set_policy("build.sanitizer.address") 16 | set_policy('build.sanitizer.undefined') 17 | set_policy('build.sanitizer.leak') 18 | end 19 | 20 | if is_plat("windows") then 21 | add_defines("OS_HOST_WINDOWS") 22 | elseif is_plat("linux") then 23 | add_defines("OS_HOST_LINUX") 24 | elseif is_plat("macosx") then 25 | add_defines("OS_HOST_MACOS") 26 | end 27 | 28 | target("elementor") 29 | set_kind("static") 30 | add_packages("skia-build") 31 | add_files("src/library/*.cpp") 32 | add_files("src/library/elements/*.cpp") 33 | 34 | -- if is_plat("windows") then 35 | -- after_build(function (target) 36 | -- local project = target._PROJECT 37 | -- local skiaBuild = project.required_packages()['skia-build'] 38 | -- os.cp( 39 | -- path.join(skiaBuild:get('linkdirs'), "icudtl.dat"), 40 | -- path.join(project.directory(), "build", "$(plat)", "$(arch)", "$(mode)", "icudtl.dat") 41 | -- ) 42 | -- end) 43 | -- end 44 | 45 | target("elementor-components") 46 | set_kind("static") 47 | add_deps("elementor") 48 | add_packages("skia-build") 49 | add_files("src/components/*.cpp") 50 | 51 | target("elementor-glfw") 52 | set_kind("static") 53 | add_packages("skia-build", "glfw") 54 | add_files("src/library/platforms/*/*.cpp") 55 | if is_plat("windows") then 56 | add_packages("glew") 57 | end 58 | 59 | target("example-basic") 60 | set_kind("binary") 61 | add_deps("elementor", "elementor-components", "elementor-glfw") 62 | add_packages("skia-build", "glfw") 63 | add_files("src/examples/basic/*.cpp") 64 | 65 | target("example-button") 66 | set_kind("binary") 67 | add_deps("elementor", "elementor-components", "elementor-glfw") 68 | add_packages("skia-build", "glfw") 69 | add_files("src/examples/button/*.cpp") 70 | 71 | target("example-counter") 72 | set_kind("binary") 73 | add_deps("elementor", "elementor-components", "elementor-glfw") 74 | add_packages("skia-build", "glfw") 75 | add_files("src/examples/counter/*.cpp") 76 | 77 | target("example-crud") 78 | set_kind("binary") 79 | add_deps("elementor", "elementor-components", "elementor-glfw") 80 | add_packages("skia-build", "glfw") 81 | add_files("src/examples/crud/*.cpp") 82 | 83 | target("example-flex") 84 | set_kind("binary") 85 | add_deps("elementor", "elementor-components", "elementor-glfw") 86 | add_packages("skia-build", "glfw") 87 | add_files("src/examples/flex/*.cpp") 88 | 89 | target("example-tempconv") 90 | set_kind("binary") 91 | add_deps("elementor", "elementor-components", "elementor-glfw") 92 | add_packages("skia-build", "glfw") 93 | add_files("src/examples/tempconv/*.cpp") 94 | 95 | target("example-text") 96 | set_kind("binary") 97 | add_deps("elementor", "elementor-components", "elementor-glfw") 98 | add_packages("skia-build", "glfw") 99 | add_files("src/examples/text/*.cpp") 100 | 101 | target("example-todo") 102 | set_kind("binary") 103 | add_deps("elementor", "elementor-components", "elementor-glfw") 104 | add_packages("skia-build", "glfw") 105 | add_files("src/examples/todo/*.cpp") 106 | 107 | target("examples") 108 | set_kind("binary") 109 | add_deps("elementor", "elementor-components", "elementor-glfw") 110 | add_packages("skia-build", "glfw") 111 | add_files("src/examples/*.cpp") 112 | 113 | target("make-screenshots") 114 | set_kind("binary") 115 | add_deps("elementor", "elementor-components", "elementor-glfw") 116 | add_packages("skia-build", "glfw") 117 | add_files("tests/make_screenshots.cpp") 118 | -------------------------------------------------------------------------------- /src/components/Button.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 02.02.2023. 3 | // 4 | 5 | #ifndef ELEMENTOR_COMPONENTS_BUTTON_H 6 | #define ELEMENTOR_COMPONENTS_BUTTON_H 7 | 8 | #include "utility.h" 9 | #include "elementor.h" 10 | 11 | #include "./ClickableOutside.h" 12 | #include "./Cursorable.h" 13 | #include "./Outline.h" 14 | 15 | namespace elementor::components { 16 | class Button : public Component, public WithEventsHandlers { 17 | public: 18 | struct Props { 19 | std::optional> onClick; 20 | std::optional> onMiddleClick; 21 | std::shared_ptr child; 22 | }; 23 | 24 | explicit Button(const std::shared_ptr& ctx, const Props& props) 25 | : Component(ctx) { 26 | 27 | element = Outline::New(ctx, { 28 | .border = { 29 | .radius = 6, 30 | .width = 3, 31 | .color = "#21CFFF", 32 | .style = BorderStyle::Dashed, 33 | }, 34 | .offset = 6, 35 | .child = Focusable::New(ctx, focusable, { 36 | .child = Cursorable::New(ctx, { 37 | .cursorShape = CursorShape::Hand, 38 | .child = ClickableOutside::New(ctx, { 39 | .onClickOutside = [this]() { 40 | blur(); 41 | }, 42 | .child = Clickable::New(ctx, { 43 | .onButton = [this](MouseButton button, int _) { 44 | if (button == MouseButton::Middle) { 45 | if (callbackMiddleClick.has_value()) { 46 | return callbackMiddleClick.value()(); 47 | } 48 | } 49 | 50 | if (button != MouseButton::Left) { 51 | return EventCallbackResponse::None; 52 | } 53 | 54 | focus(); 55 | 56 | if (!callbackClick.has_value()) { 57 | return EventCallbackResponse::None; 58 | } 59 | 60 | return callbackClick.value()(); 61 | }, 62 | .child = props.child 63 | }) 64 | }) 65 | }) 66 | }) 67 | }); 68 | 69 | onClick(props.onClick); 70 | onMiddleClick(props.onMiddleClick); 71 | } 72 | 73 | static std::shared_ptr
85 | Basic example screnshot 86 |
87 | 88 | ### 2. Button 89 | 90 | - Clickable buttons of different types 91 | 92 | ```bash 93 | xmake run example-button 94 | ``` 95 | 96 |
97 | Button example screnshot 98 |
99 | 100 | ### 3. Counter 101 | 102 | - Clickable button 103 | - Updating state 104 | 105 | ```bash 106 | xmake run example-counter 107 | ``` 108 | 109 |
110 | Counter example screnshot 111 |
112 | 113 | ### 4. CRUD (readonly) 114 | 115 | - Template for a generic CRUD application 116 | - For now, read-only 117 | 118 | ```bash 119 | xmake run example-crud 120 | ``` 121 | 122 |
123 | CRUD example screnshot 124 |
125 | 126 | ### 5. Flexbox Layout 127 | 128 | - Layout 129 | - Nested layout 130 | 131 | ```bash 132 | xmake run example-flex 133 | ``` 134 | 135 |
136 | Flex layout example screnshot 137 |
138 | 139 | ### 6. Temperature Converter 140 | 141 | - Text input 142 | - State 143 | - Transforming state 144 | 145 | ```bash 146 | xmake run example-tempconv 147 | ``` 148 | 149 |
150 | Temperature converter example screnshot 151 |
152 | 153 | ### 7. Text 154 | 155 | - Rich text rendering 156 | 157 | ```bash 158 | xmake run example-text 159 | ``` 160 | 161 |
162 | Text example screnshot 163 |
164 | 165 | ### 8. TODO 166 | 167 | - Complex demo application with all previous features combined 168 | - Sync with markdown file `examples/assets/todo.md` 169 | 170 | ```bash 171 | xmake run example-todo 172 | ``` 173 | 174 |
175 | TODO example screnshot 176 |
177 | 178 | ### All combined 179 | 180 | There is also an example application providing all of the previous examples in one window. 181 | 182 | ```bash 183 | xmake run examples 184 | ``` 185 | -------------------------------------------------------------------------------- /src/library/elements/Scrollable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 22.05.2022. 3 | // 4 | 5 | #ifndef ELEMENTOR_SCROLLABLE_H 6 | #define ELEMENTOR_SCROLLABLE_H 7 | 8 | #include "../include.h" 9 | 10 | namespace elementor::elements { 11 | enum class ScrollDirection { 12 | Vertical, 13 | Horizontal, 14 | Both, 15 | }; 16 | 17 | struct ScrollableProps { 18 | std::optional direction; 19 | std::optional scrollAcceleration; 20 | const std::shared_ptr& child = nullptr; 21 | }; 22 | 23 | class Scrollable : public Element, public WithEventsHandlers, public WithGlobalEventsHandlers, public WithChild { 24 | public: 25 | Scrollable(const std::shared_ptr& ctx, const ScrollableProps& props) 26 | : Element(ctx) { 27 | if (props.direction.has_value()) setDirection(props.direction.value()); 28 | if (props.scrollAcceleration.has_value()) setScrollAcceleration(props.scrollAcceleration.value()); 29 | setChild(props.child); 30 | } 31 | 32 | // TODO: Add ELEMENT_NEW macro 33 | 34 | static std::shared_ptr New( 35 | const std::shared_ptr& ctx, 36 | const ScrollableProps& props 37 | ) { 38 | return std::make_shared(ctx, props); 39 | } 40 | 41 | static std::shared_ptr New( 42 | const std::shared_ptr& ctx, 43 | std::shared_ptr& elementRef, 44 | const ScrollableProps& props 45 | ) { 46 | auto element = New(ctx, props); 47 | elementRef = element; 48 | return element; 49 | } 50 | 51 | static std::shared_ptr New(const std::shared_ptr& ctx) { 52 | return New(ctx, {}); 53 | } 54 | 55 | void setDirection(ScrollDirection newDirection) { 56 | markChanged(); 57 | direction = newDirection; 58 | } 59 | 60 | [[nodiscard]] ScrollDirection getDirection() const { 61 | return direction; 62 | } 63 | 64 | bool isHorizontalScroll() { 65 | return direction != ScrollDirection::Vertical; 66 | } 67 | 68 | bool isVerticalScroll() { 69 | return direction != ScrollDirection::Horizontal; 70 | } 71 | 72 | [[nodiscard]] Size getSize() const { 73 | return lastSize; 74 | } 75 | 76 | [[nodiscard]] Size getScrollSize() const { 77 | return lastChildSize; 78 | } 79 | 80 | [[nodiscard]] float getMaxScrollLeft() const { 81 | return std::max(lastChildSize.width - lastSize.width, 0.0f); 82 | } 83 | 84 | [[nodiscard]] float getMaxScrollTop() const { 85 | return std::max(lastChildSize.height - lastSize.height, 0.0f); 86 | } 87 | 88 | void setScrollTop(float newScrollTop) { 89 | newScrollTop = std::min(std::max(newScrollTop, 0.0f), getMaxScrollTop()); 90 | 91 | if (newScrollTop == scrollTop) { 92 | return; 93 | } 94 | 95 | markChanged(); 96 | scrollTop = newScrollTop; 97 | } 98 | 99 | [[nodiscard]] float getScrollTop() const { 100 | return scrollTop; 101 | } 102 | 103 | void setScrollLeft(float newScrollLeft) { 104 | newScrollLeft = std::min(std::max(newScrollLeft, 0.0f), getMaxScrollLeft()); 105 | 106 | if (newScrollLeft == scrollLeft) { 107 | return; 108 | } 109 | 110 | markChanged(); 111 | scrollLeft = newScrollLeft; 112 | } 113 | 114 | [[nodiscard]] float getScrollLeft() const { 115 | return scrollLeft; 116 | } 117 | 118 | void setScrollAcceleration(float newScrollAcceleration) { 119 | scrollAcceleration = newScrollAcceleration; 120 | } 121 | 122 | [[nodiscard]] float getScrollAcceleration() const { 123 | return scrollAcceleration; 124 | } 125 | 126 | void setChild(const std::shared_ptr& newChild) { 127 | markChanged(); 128 | child = newChild; 129 | } 130 | 131 | Size getSize(const Boundaries& boundaries) override; 132 | 133 | std::vector getChildren(const ElementRect& rect) override; 134 | 135 | ClipBehavior getClipBehaviour() override { 136 | return ClipBehavior::AntiAlias; 137 | } 138 | 139 | std::vector> getEventsHandlers() override; 140 | 141 | std::vector> getGlobalEventsHandlers() override; 142 | 143 | private: 144 | ScrollDirection direction = ScrollDirection::Both; 145 | float scrollLeft; 146 | float scrollTop; 147 | float scrollAcceleration = 64.0; 148 | 149 | bool hovered; 150 | 151 | Size lastSize = { 0, 0 }; 152 | Size lastChildSize = { 0, 0 }; 153 | 154 | Size getChildSize(const Boundaries& boundaries); 155 | }; 156 | } 157 | 158 | 159 | #endif //ELEMENTOR_SCROLLABLE_H 160 | -------------------------------------------------------------------------------- /src/examples/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by noartem on 15.02.2024. 3 | // 4 | 5 | #include "elementor.h" 6 | 7 | #include "./basic/Basic.h" 8 | #include "./button/ButtonDemo.h" 9 | #include "./counter/Counter.h" 10 | #include "./crud/Crud.h" 11 | #include "./flex/FlexDemo.h" 12 | #include "./tempconv/Tempconv.h" 13 | #include "./text/TextDemo.h" 14 | #include "./todo/Todo.h" 15 | 16 | class Examples : public Component { 17 | public: 18 | struct Props { 19 | std::shared_ptr platform; 20 | }; 21 | 22 | explicit Examples(const std::shared_ptr& ctx, const Props& props) 23 | : Component(ctx) { 24 | platform = props.platform; 25 | 26 | element = Background::New(ctx, { 27 | .color = "#fff", 28 | .child = Flex::New(ctx, { 29 | .alignment = FlexAlignment::Stretch, 30 | .children = { 31 | Background::New(ctx, { 32 | .color = "#36419a", 33 | .child = Flex::New(ctx, { 34 | .spacing = 8, 35 | .direction = FlexDirection::Column, 36 | .alignment = FlexAlignment::Start, 37 | .crossAlignment = FlexCrossAlignment::SpaceBetween, 38 | .children = { 39 | Scrollable::New(ctx, { 40 | .direction = ScrollDirection::Vertical, 41 | .child = Padding::New(ctx, { 42 | .all = 8, 43 | .bottom = 0, 44 | .child = makeItemsNamesElement(), 45 | }), 46 | }), 47 | Padding::New(ctx, { 48 | .all = 8, 49 | .top = 0, 50 | .child = FPSLabel::New(ctx), 51 | }) 52 | } 53 | }), 54 | }), 55 | Flexible::New(ctx, activeItemElementSlot, { 56 | .grow = 1, 57 | }) 58 | } 59 | }) 60 | } 61 | ); 62 | } 63 | 64 | static std::shared_ptr New( 65 | const std::shared_ptr& ctx, 66 | const Props& props 67 | ) { 68 | return std::make_shared(ctx, props); 69 | } 70 | 71 | private: 72 | std::shared_ptr platform; 73 | 74 | std::string activeItemName; 75 | std::shared_ptr activeItemElementSlot; 76 | 77 | const std::vector()>>> items = { 78 | { "Basic", [this]() { 79 | return Basic::New(ctx); 80 | }}, 81 | { "Text", [this]() { 82 | return TextDemo::New(ctx); 83 | }}, 84 | { "Button", [this]() { 85 | return ButtonDemo::New(ctx); 86 | }}, 87 | { "Flex", [this]() { 88 | return FlexDemo::New(ctx); 89 | }}, 90 | { "Counter", [this]() { 91 | return Counter::New(ctx); 92 | }}, 93 | { "CRUD", [this]() { 94 | return Crud::New(ctx); 95 | }}, 96 | { "TempConv", [this]() { 97 | return TempConv::New(ctx); 98 | }}, 99 | { "Todo", [this]() { 100 | return Todo::New(ctx, { 101 | .filename = asset("todo.md"), 102 | }); 103 | }} 104 | }; 105 | 106 | std::vector> makeItemsNamesElements() { 107 | std::vector> itemsNamesElements; 108 | 109 | for (auto [name, makeElement]: items) { 110 | itemsNamesElements.push_back( 111 | TextButton::New(ctx, { 112 | .text = name, 113 | .fontColor = "#36419a", 114 | .backgroundColor = "#ffffff", 115 | .onClick = [this, name, makeElement]() { 116 | if (activeItemName != name) { 117 | activeItemName = name; 118 | activeItemElementSlot->setChild(makeElement()); 119 | } 120 | return EventCallbackResponse::None; 121 | }, 122 | .onMiddleClick = [this, name, makeElement]() { 123 | auto window = std::make_shared(platform); 124 | window->setTitle(name); 125 | window->setSize({ 640, 920 }); 126 | window->setMinSize({ 640, 920 }); 127 | platform->addWindow(window); 128 | 129 | window->setRoot( 130 | makeElement() 131 | ); 132 | 133 | return EventCallbackResponse::None; 134 | } 135 | }) 136 | ); 137 | } 138 | 139 | return itemsNamesElements; 140 | } 141 | 142 | std::shared_ptr makeItemsNamesElement() { 143 | return Flex::New(ctx, { 144 | .spacing = 8, 145 | .direction = FlexDirection::Column, 146 | .children = makeItemsNamesElements(), 147 | }); 148 | } 149 | }; 150 | 151 | int main() { 152 | auto platform = std::make_shared(); 153 | 154 | auto window = std::make_shared(platform); 155 | window->setTitle("Elementor Examples"); 156 | window->setSize({ 640, 320 }); 157 | window->setMinSize({ 320, 320 }); 158 | platform->addWindow(window); 159 | 160 | window->setRoot( 161 | Examples::New(window, { 162 | .platform = platform 163 | }) 164 | ); 165 | 166 | platform->run(); 167 | } --------------------------------------------------------------------------------