diff --git a/examples/BasicWidgets/CMakeLists.txt b/examples/BasicWidgets/CMakeLists.txt
new file mode 100644
index 0000000..7801fc5
--- /dev/null
+++ b/examples/BasicWidgets/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.18)
+project(BasicWidgets)
+
+set(CMAKE_CXX_STANDARD 20)
+
+add_executable(BasicWidgets
+ src/main.cpp
+ src/MainContent.cpp
+ src/MainContent.h
+ src/ui/MainScene.cpp
+ src/ui/MainScene.h
+ src/ui/MainLayer.cpp
+ src/ui/MainLayer.h)
+
+target_link_libraries(BasicWidgets PRIVATE algine)
+
+if (WIN32)
+ algine_target_mklink(
+ TARGET BasicWidgets
+ ALGINE_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../algine"
+ BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../bin")
+endif()
diff --git a/examples/BasicWidgets/README.md b/examples/BasicWidgets/README.md
new file mode 100644
index 0000000..69f3b52
--- /dev/null
+++ b/examples/BasicWidgets/README.md
@@ -0,0 +1,110 @@
+# BasicWidgets
+
+Supported systems: Linux, Windows
+
+This very basic example shows how to use basic widgets using XML.
+
+> [!NOTE]
+> Working directory must be set to the `resources` directory.
+
+This example uses
+[Noto Sans Black](https://fonts.google.com/noto/specimen/Noto+Sans) font.
+
+## Introduction
+
+Algine is a modular, easy-to-use 3D rendering engine. Like any other
+framework, Algine has its own features.
+
+### Objects
+
+All classes are divided into two types: objects and ordinary classes.
+Objects are special classes, which inherit the `algine::Object` class.
+Such classes have some important features: they can have many children,
+but only one parent. All objects by default have `algine::GlobalScene`
+as the parent and must be created on the heap, using the `new` operator.
+
+#### Lifecycle
+
+As mentioned before, objects must be created using operator `new`.
+
+> [!IMPORTANT]
+> Allocating on the heap is crucial, since it will allow to correctly
+> destroy all children while destroying the parent. Otherwise, you will
+> get a segmentation fault.
+
+If you want to manually delete an object, you can use operator `delete`.
+In that case, all its children will be also deleted, moreover, the object
+will be automatically removed from the children list of its parent.
+
+> [!WARNING]
+> If you want to use objects like regular classes, you can just pass
+> `nullptr` as the parent. However, it is highly unrecommended and can be
+> really dangerous. Do it on your own risk.
+
+### Display hierarchy
+
+Okay, now you already know what is an object. But how we can render things?
+
+#### `Window`
+
+As the name suggests, `Window` is a Window. It is the base class for
+platform-depended windows like e.g. `GLFWWindow` or `QtWindow`. This class
+receives events, executes tasks, is responsible for the render loop. But
+how exactly this class renders your content?
+
+#### `Content`
+
+`Content` is an entry point for rendering. It has an abstract function
+`render` which needs to be overridden. It also has virtual functions like
+`onShow` and `onHide`. `Content` has width and height, received from the
+window. But how we can receive more events?
+
+##### `UnifiedEventHandler`
+
+`UnifiedEventHandler` is a cross-platform event handler. Typically, you
+want your custom content to inherit this class, and then, somewhere in
+`YourContent::onShow`, register a new event handler by using
+
+```cpp
+getWindow()->setEventHandler(this);
+```
+
+Interesting, but `Content` will not render my scene, will it?
+
+#### `Scene`
+
+As the name suggests, this is scene. Scene is an object, which has some
+useful functions like `show`, `hide`, `render`, `addChild` etc. `Scene`
+represents your scene. Feel free to create as many class members as you
+wish. By default, a scene is hidden, so you should call `show` before
+`render`, if you want to see something, of course. But can I render my
+whole scene just here? Well, yes, but no. In order to render something,
+you need to use `Renderer`.
+
+#### `Renderer` and `RenderPass`
+
+Finally! `Renderer` performs `RenderPass` rendering. `RenderPass` has
+virtual function `render`, which needs to be overridden. So yes, now you
+can place all your rendering code in `YourRenderPass::render` and add it
+to your renderer. Do not forget to call `Renderer::render` in `YourSceene`!
+
+## Summary
+
+So, generally speaking, normally we want to have the following structure:
+
+- `Window`
+ - `Content`
+ - `UnifiedEventHandler`
+ - `Scene`
+ - `Renderer`
+ - `RenderPass`
+
+Of course, in the current engine architecture, some stages are not obligatory,
+but who knows, what will be changed in the future?
+
+## Conclusion
+
+After reading this loooong document, you may have some basic knowledge about
+some parts of the engine. Congratulations! Now, you can enjoy the code :)
+
+[//]: # (TODO: move this documentation to the HelloCube example)
diff --git a/examples/BasicWidgets/resources/BasicWidgets.xml b/examples/BasicWidgets/resources/BasicWidgets.xml
new file mode 100644
index 0000000..6ce1c01
--- /dev/null
+++ b/examples/BasicWidgets/resources/BasicWidgets.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/BasicWidgets/resources/NotoSans-Black.ttf b/examples/BasicWidgets/resources/NotoSans-Black.ttf
new file mode 100644
index 0000000..05771e5
Binary files /dev/null and b/examples/BasicWidgets/resources/NotoSans-Black.ttf differ
diff --git a/examples/BasicWidgets/src/MainContent.cpp b/examples/BasicWidgets/src/MainContent.cpp
new file mode 100644
index 0000000..d386ea4
--- /dev/null
+++ b/examples/BasicWidgets/src/MainContent.cpp
@@ -0,0 +1,24 @@
+#include "MainContent.h"
+#include "ui/MainScene.h"
+
+#include
+#include
+
+MainContent::MainContent()
+ : m_scene(new UI::MainScene(this))
+{
+ m_scene->listen(this);
+}
+
+void MainContent::onShow() {
+ getWindow()->setEventHandler(this);
+ m_scene->setSize(width(), height());
+ m_scene->show();
+}
+
+void MainContent::render() {
+ Framebuffer::getDefault()->bind();
+ Framebuffer::getDefault()->clear(Framebuffer::ColorBuffer | Framebuffer::DepthBuffer);
+ Engine::setViewport(width(), height());
+ m_scene->render();
+}
diff --git a/examples/BasicWidgets/src/MainContent.h b/examples/BasicWidgets/src/MainContent.h
new file mode 100644
index 0000000..e75ab0f
--- /dev/null
+++ b/examples/BasicWidgets/src/MainContent.h
@@ -0,0 +1,24 @@
+#ifndef ALGINE_EXAMPLES_MAINCONTENT_H
+#define ALGINE_EXAMPLES_MAINCONTENT_H
+
+#include
+#include
+
+using namespace algine;
+
+namespace UI {
+class MainScene;
+}
+
+class MainContent: public Content, public UnifiedEventHandler {
+public:
+ MainContent();
+
+ void onShow() override;
+ void render() override;
+
+private:
+ UI::MainScene *m_scene;
+};
+
+#endif//ALGINE_EXAMPLES_MAINCONTENT_H
diff --git a/examples/BasicWidgets/src/main.cpp b/examples/BasicWidgets/src/main.cpp
new file mode 100644
index 0000000..734a4fc
--- /dev/null
+++ b/examples/BasicWidgets/src/main.cpp
@@ -0,0 +1,41 @@
+#include
+
+#ifdef ALGINE_QT_PLATFORM
+ #include
+#else
+ #include
+#endif
+
+#include "MainContent.h"
+
+using namespace algine;
+
+static void exec() {
+ Engine::setDPI(130);
+
+#ifdef ALGINE_QT_PLATFORM
+ auto window = new QtWindow();
+ window->setDimensions(512, 512);
+
+ window->addOnInitializedListener([window]() {
+ window->setContent(new MainContent());
+ window->renderLoop(1000);
+ });
+
+ window->show();
+#else
+ GLFWWindow window("BasicWidgets", 1366, 768);
+ window.setFullscreenDimensions(1366, 768);
+ window.setMouseTracking(true);
+ window.setKeyboardTracking(true);
+ window.setWindowStateTracking(true);
+ //window.setCursorMode(Window::CursorMode::Disabled);
+ window.setContentLater();
+ window.renderLoop();
+#endif
+}
+
+int main(int argc, char **argv) {
+ Engine::exec(argc, argv, &exec);
+ return 0;
+}
diff --git a/examples/BasicWidgets/src/ui/MainLayer.cpp b/examples/BasicWidgets/src/ui/MainLayer.cpp
new file mode 100644
index 0000000..02a1784
--- /dev/null
+++ b/examples/BasicWidgets/src/ui/MainLayer.cpp
@@ -0,0 +1,45 @@
+#include "MainLayer.h"
+
+#include
+#include
+#include
+
+#include
+
+namespace dmg = tulz::demangler;
+
+namespace algine {
+template
+Logger&& operator<<(L &&logger, const Point &p) {
+ auto tName = typeid(T).name();
+ logger << "Point<" << dmg::demangle(tName) << "> {" << p.getX() << ", " << p.getY() << "}";
+ return std::move(logger);
+}
+}
+
+namespace UI {
+MainLayer::MainLayer(Widgets::Scene *parent)
+ : Widgets::Layer(parent)
+{
+ auto container = Widget::constructFromXMLFile("BasicWidgets.xml", this);
+ setContainer(container);
+
+ container->findChild("label0")->setEventListener(Event::Click, [](Widget *widget, const Event &event) {
+ auto v2pi = [](const glm::vec<2, T> &v) -> PointI {
+ return {(int) v.x, (int) v.y};
+ };
+
+ auto &info = event.getPointerInfo();
+
+ Log::verbose("MainLayer") << "label0 has detected click:\n"
+ << "label0 local coords: " << info.getPos() << "\n"
+ << "parent local coords: " << widget->mapFromParent(v2pi(info.getPos()));
+ });
+
+ container->findChild("container1")->setEventListener(Event::Click, [](Widget*, const Event &event) {
+ auto &info = event.getPointerInfo();
+ Log::verbose("MainLayer") << "container1 has detected click:\n"
+ << "container1 local coords: " << info.getPos();
+ });
+}
+}// namespace ui
\ No newline at end of file
diff --git a/examples/BasicWidgets/src/ui/MainLayer.h b/examples/BasicWidgets/src/ui/MainLayer.h
new file mode 100644
index 0000000..d079e9f
--- /dev/null
+++ b/examples/BasicWidgets/src/ui/MainLayer.h
@@ -0,0 +1,15 @@
+#ifndef ALGINE_EXAMPLES_MAINLAYER_H
+#define ALGINE_EXAMPLES_MAINLAYER_H
+
+#include
+
+using namespace algine;
+
+namespace UI {
+class MainLayer: public Widgets::Layer {
+public:
+ explicit MainLayer(Widgets::Scene *parent);
+};
+}// namespace UI
+
+#endif//ALGINE_EXAMPLES_MAINLAYER_H
diff --git a/examples/BasicWidgets/src/ui/MainScene.cpp b/examples/BasicWidgets/src/ui/MainScene.cpp
new file mode 100644
index 0000000..e94c2d5
--- /dev/null
+++ b/examples/BasicWidgets/src/ui/MainScene.cpp
@@ -0,0 +1,11 @@
+#include "MainScene.h"
+#include "MainLayer.h"
+
+namespace UI {
+MainScene::MainScene(Object *parent)
+ : Widgets::Scene(parent)
+{
+ auto layer = new MainLayer(this);
+ layer->show();
+}
+}
diff --git a/examples/BasicWidgets/src/ui/MainScene.h b/examples/BasicWidgets/src/ui/MainScene.h
new file mode 100644
index 0000000..2320b3a
--- /dev/null
+++ b/examples/BasicWidgets/src/ui/MainScene.h
@@ -0,0 +1,15 @@
+#ifndef ALGINE_EXAMPLES_MAINSCENE_H
+#define ALGINE_EXAMPLES_MAINSCENE_H
+
+#include
+
+using namespace algine;
+
+namespace UI {
+class MainScene : public Widgets::Scene {
+public:
+ explicit MainScene(Object *parent);
+};
+}
+
+#endif//ALGINE_EXAMPLES_MAINSCENE_H
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..e8a341d
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.18)
+project(algine_examples)
+
+set(CMAKE_CXX_STANDARD 20)
+
+# silence warnings from sol2
+add_compile_options(-Wno-unknown-warning-option)
+
+include(../cmake/mold.cmake)
+try_use_mold()
+
+macro(set_global name value)
+ set(${name} ${value} CACHE INTERNAL ${name})
+endmacro()
+
+set_global(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF)
+set_global(ASSIMP_BUILD_OBJ_IMPORTER ON)
+set_global(ASSIMP_BUILD_ZLIB OFF)
+set_global(ASSIMP_BUILD_ASSIMP_TOOLS OFF)
+set_global(ASSIMP_BUILD_TESTS OFF)
+
+add_subdirectory(../ algine)
+
+add_subdirectory(BasicWidgets)
\ No newline at end of file
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..6e4dda5
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,20 @@
+# Examples
+
+> [!NOTE]
+> In order to load examples, open `CMakeLists.txt` in the current directory.
+> Do not open examples directly.
+
+## List
+
+1. [Basic widgets](BasicWidgets)
+
+## TODO
+
+- [ ] Hello Cube
+- [x] Basic widgets
+- [ ] UI Overlay: complete example (with all abstractions included)
+- [ ] Basic Lua scripting
+- [ ] Cross-platform example: Linux, Android, Windows
+
+These examples should be like mini-lessons, with all necessary documentation
+included.