Replace Old Widgets

This commit is contained in:
IlyaShurupov 2024-10-18 10:56:42 +03:00
parent cce8f0e1bc
commit fcd5eb2d2a
81 changed files with 388 additions and 278 deletions

View file

@ -0,0 +1,21 @@
project(Widgets)
### ---------------------- Static Library --------------------- ###
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
file(GLOB HEADERS "./public/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Math Graphics Imgui)
### -------------------------- Applications -------------------------- ###
add_executable(SimpleGui examples/SimpleGUI.cpp)
target_link_libraries(SimpleGui ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(SimpleGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
add_executable(ChatGui examples/ChatGUI.cpp)
target_link_libraries(ChatGui ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(ChatGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

View file

@ -1,12 +1,12 @@
#include "EditorWidget.hpp" #include "EditorWidget.hpp"
#include "GraphicApplication.hpp" #include "WidgetApplication.hpp"
#include "RootWidget.hpp"
using namespace tp; using namespace tp;
class EditorGUI : public WidgetApplication {
class EditorGUI : public Application {
public: public:
EditorGUI() { EditorGUI() {
auto canvas = this->mGraphics->getCanvas(); auto canvas = this->mGraphics->getCanvas();
@ -14,25 +14,14 @@ public:
mEditor.loadDefaults(); mEditor.loadDefaults();
setRoot(mGui);
// mScene.mCamera.lookAtPoint({ 0, 0, 0 }, { 3, 3, 2 }, { 0, 0, 1 }); // mScene.mCamera.lookAtPoint({ 0, 0, 0 }, { 3, 3, 2 }, { 0, 0, 1 });
} }
~EditorGUI() override { delete mGui; } ~EditorGUI() override { delete mGui; }
void processFrame(EventHandler* eventHandler, halnf delta) override {
auto rec = RectF({ 0, 0 }, mWindow->getSize());
mGui->setArea(rec);
mGui->setVisible(true);
mGui->updateConfigWrapper(mWidgetManager);
mGui->procWrapper(*eventHandler, rec);
}
void drawFrame(Canvas* canvas) override { mGui->drawWrapper(*canvas); }
private: private:
Editor mEditor; Editor mEditor;
WidgetManager mWidgetManager;
EditorWidget* mGui; EditorWidget* mGui;
}; };

View file

@ -57,6 +57,8 @@ void Editor::loadDefaults() {
} }
void Editor::setViewportSize(const Vec2F& size) { void Editor::setViewportSize(const Vec2F& size) {
if (size.x < 0 || size.y < 0) return;
// TODO remove // TODO remove
// mScene.mCamera.rotate(0.01f, 0.0); // mScene.mCamera.rotate(0.01f, 0.0);

View file

@ -1,6 +1,8 @@
#include "Widgets.hpp" #include "Widget.hpp"
#include "DockWidget.hpp"
#include "Editor.hpp" #include "Editor.hpp"
#include "FloatingWidget.hpp"
namespace tp { namespace tp {
@ -12,15 +14,17 @@ namespace tp {
mCanvas = canvas; mCanvas = canvas;
} }
~ViewportWidget() { mCanvas->deleteImageHandle(mImage); } ~ViewportWidget() override { mCanvas->deleteImageHandle(mImage); }
void eventProcess(const Events& events) override { mEditor->setViewportSize(this->mArea.size); } void process(const EventHandler& events) override {
mEditor->setViewportSize(getArea().size);
}
void eventDraw(Canvas& canvas) override { void draw(Canvas& canvas) override {
mEditor->renderViewport(); mEditor->renderViewport();
canvas.updateTextureID(mImage, mEditor->getViewportTexID()); canvas.updateTextureID(mImage, mEditor->getViewportTexID());
canvas.drawImage(this->mArea, &mImage, PI); canvas.drawImage(getArea().relative(), &mImage, PI);
} }
public: public:
@ -30,98 +34,96 @@ namespace tp {
Canvas::ImageHandle mImage; Canvas::ImageHandle mImage;
}; };
class EditorWidget : public WorkspaceWidget { class EditorWidget : public DockWidget {
public: public:
EditorWidget(Canvas* canvas, Editor* editor) : EditorWidget(Canvas* canvas, Editor* editor) :
mViewport(canvas, editor) { mViewport(canvas, editor) {
mEditor = editor; mEditor = editor;
mDockSpace.addSideWidget(&mNavigationMenu, DockWidget::RIGHT); mPanel.setText("Controls");
mDockSpace.addSideWidget(&mRenderMenu, DockWidget::LEFT); mPanel.addToMenu(&mNavigationMenu);
mDockSpace.setCenterWidget(&mViewport); mPanel.addToMenu(&mRenderMenu);
dockWidget(&mPanel, DockLayout::RIGHT);
setCenterWidget(&mViewport);
// Render // Render
{ {
mRenderPathTracer.setLabel("Render with Path Tracer"); mRenderPathTracer.setText("Render with Path Tracer");
mRenderRaster.setLabel("Render with Raster"); mRenderRaster.setText("Render with Raster");
mRenderDeNoise.setLabel("Denoise (IntelOpenImage)"); mRenderDeNoise.setText("Denoise (IntelOpenImage)");
mRenderMenu.addWidgetToMenu(&mRenderPathTracer); mRenderMenu.addToMenu(&mRenderPathTracer);
mRenderMenu.addWidgetToMenu(&mRenderRaster); mRenderMenu.addToMenu(&mRenderRaster);
mRenderMenu.addWidgetToMenu(&mRenderDeNoise); mRenderMenu.addToMenu(&mRenderDeNoise);
mRenderMenu.setLabel("Render"); mRenderMenu.setText("Render");
} }
// Navigation // Navigation
{ {
mNavigationPan.setLabel("Pan"); mNavigationPan.setText("Pan");
mNavigationOrbit.setLabel("Orbit"); mNavigationOrbit.setText("Orbit");
mNavigationZoom.setLabel("Zoom"); mNavigationZoom.setText("Zoom");
mNavigationReset.setLabel("Reset"); mNavigationReset.setText("Reset");
mNavigationMenu.addWidgetToMenu(&mNavigationPan); mNavigationMenu.addToMenu(&mNavigationPan);
mNavigationMenu.addWidgetToMenu(&mNavigationOrbit); mNavigationMenu.addToMenu(&mNavigationOrbit);
mNavigationMenu.addWidgetToMenu(&mNavigationZoom); mNavigationMenu.addToMenu(&mNavigationZoom);
mNavigationMenu.addWidgetToMenu(&mNavigationReset); mNavigationMenu.addToMenu(&mNavigationReset);
mNavigationMenu.setLabel("Navigation"); mNavigationMenu.setText("Navigation");
} }
mRenderPathTracer.mCallback = [this]() { mRenderPathTracer.setAction([this]() {
mEditor->renderPathFrame(); mEditor->renderPathFrame();
mEditor->setRenderType(Editor::RenderType::PATH_TRACER); mEditor->setRenderType(Editor::RenderType::PATH_TRACER);
}; });
mRenderRaster.mCallback = [this]() { mEditor->setRenderType(Editor::RenderType::RASTER); }; mRenderRaster.setAction( [this]() { mEditor->setRenderType(Editor::RenderType::RASTER); });
mRenderDeNoise.mCallback = [this]() { mEditor->denoisePathRenderBuffers(); }; mRenderDeNoise.setAction( [this]() { mEditor->denoisePathRenderBuffers(); });
mNavigationOrbit.mCallback = [this]() { mNavigationType = ORBIT; }; mNavigationOrbit.setAction( [this]() { mNavigationType = ORBIT; });
mNavigationPan.mCallback = [this]() { mNavigationType = PAN; }; mNavigationPan.setAction( [this]() { mNavigationType = PAN; });
mNavigationZoom.mCallback = [this](){ mNavigationType = ZOOM; }; mNavigationZoom.setAction( [this](){ mNavigationType = ZOOM; });
mNavigationReset.mCallback = [this]() { mEditor->navigationReset(); }; mNavigationReset.setAction( [this]() { mEditor->navigationReset(); });
} }
void eventProcess(const Events& events) override { void process(const EventHandler& events) override {
// navigation DockWidget::process(events);
{
const auto& activeArea = mViewport.mArea;
if (mViewport.isHolding()) {
switch (mNavigationType) {
case ORBIT: mEditor->navigationOrbit(events.getPointerDelta() / activeArea.size * 3); break;
case PAN: auto pointer = events.getPointer();
{ auto pointerPrev = events.getPointerPrev();
auto pointer = (((events.getPointer() - activeArea.pos) / activeArea.size) - 0.5f) * 2;
auto prevPointer = (((events.getPointerPrev() - activeArea.pos) / activeArea.size) - 0.5f) * 2;
mEditor->navigationPan(prevPointer, pointer);
}
break;
case ZOOM: mEditor->navigationZoom(1 + (events.getPointerDelta().y / activeArea.size.y)); break; const auto& activeArea = mViewport.getArea();
}
if (activeArea.isInside(pointer) && events.isDown(InputID::MOUSE1)) {
switch (mNavigationType) {
case ORBIT: mEditor->navigationOrbit(events.getPointerDelta() / activeArea.size * 3); break;
case PAN:
{
auto pointerRelative = (((pointer - activeArea.pos) / activeArea.size) - 0.5f) * 2;
auto prevPointerRelative = (((pointerPrev - activeArea.pos) / activeArea.size) - 0.5f) * 2;
mEditor->navigationPan(prevPointerRelative, pointerRelative);
}
break;
case ZOOM: mEditor->navigationZoom(1 + (events.getPointerDelta().y / activeArea.size.y)); break;
} }
} }
WorkspaceWidget::eventProcess(events);
} }
void eventDraw(Canvas& canvas) override { canvas.rect(this->mArea, mBaseColor); } void draw(Canvas& canvas) override { canvas.rect(getArea().relative(), mBaseColor); }
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("3DEditor");
mBaseColor = wm.getColor("Base", "Base");
}
public: public:
Editor* mEditor = nullptr; Editor* mEditor = nullptr;
// SplitView mSplitView;
ViewportWidget mViewport; ViewportWidget mViewport;
// ScrollableWindow mSettingsWidget;
FloatingMenu mPanel;
// Controls // Controls
FloatingWidget mRenderMenu; FloatingMenu mRenderMenu;
ButtonWidget mRenderPathTracer; ButtonWidget mRenderPathTracer;
ButtonWidget mRenderRaster; ButtonWidget mRenderRaster;
ButtonWidget mRenderDeNoise; ButtonWidget mRenderDeNoise;
@ -129,7 +131,7 @@ namespace tp {
// Navigation // Navigation
enum NavigationType { ORBIT, PAN, ZOOM } mNavigationType = ORBIT; enum NavigationType { ORBIT, PAN, ZOOM } mNavigationType = ORBIT;
FloatingWidget mNavigationMenu; FloatingMenu mNavigationMenu;
ButtonWidget mNavigationPan; ButtonWidget mNavigationPan;
ButtonWidget mNavigationOrbit; ButtonWidget mNavigationOrbit;
ButtonWidget mNavigationZoom; ButtonWidget mNavigationZoom;

View file

@ -20,7 +20,6 @@ add_subdirectory(Graphics)
add_subdirectory(DataAnalysis) add_subdirectory(DataAnalysis)
add_subdirectory(Objects) add_subdirectory(Objects)
add_subdirectory(Widgets) add_subdirectory(Widgets)
add_subdirectory(WidgetsNew)
add_subdirectory(LibraryViewer) add_subdirectory(LibraryViewer)
add_subdirectory(RasterRender) add_subdirectory(RasterRender)
add_subdirectory(3DScene) add_subdirectory(3DScene)

View file

@ -117,6 +117,10 @@ void Canvas::popClamp() {
} }
} }
const RectF& Canvas::getClampedArea() const {
return mScissors.last();
}
void Canvas::text( void Canvas::text(
const char* string, const RectF& aRec, halnf size, Align align, halnf marging, const RGBA& col const char* string, const RectF& aRec, halnf size, Align align, halnf marging, const RGBA& col
) { ) {

View file

@ -59,6 +59,7 @@ namespace tp {
void pushClamp(const RectF& rec); void pushClamp(const RectF& rec);
void popClamp(); void popClamp();
const RectF& getClampedArea() const;
void debugCross(RectF rec, const RGBA& col); void debugCross(RectF rec, const RGBA& col);
void rect(RectF rec, const RGBA& col, halnf round = 0); void rect(RectF rec, const RGBA& col, halnf round = 0);

View file

@ -1,10 +1,10 @@
#include "GUI.hpp" #include "GUI.hpp"
#include "GraphicApplication.hpp" #include "WidgetApplication.hpp"
using namespace tp; using namespace tp;
class LibraryViewer : public Application { class LibraryViewer : public WidgetApplication {
public: public:
LibraryViewer() : LibraryViewer() :
gui(&library, &player) { gui(&library, &player) {
@ -12,24 +12,14 @@ public:
library.checkExisting(); library.checkExisting();
gui.updateTracks(); gui.updateTracks();
setRoot(&gui);
} }
void processFrame(EventHandler* eventHandler, halnf delta) override {
auto rec = RectF{ { 0, 0 }, mWindow->getSize() };
gui.setVisible(true);
gui.setArea(rec);
gui.updateConfigWrapper(mWidgetManager);
gui.procWrapper(*eventHandler, rec);
}
void drawFrame(Canvas* canvas) override { gui.drawWrapper(*canvas); }
private: private:
Player player; Player player;
Library library; Library library;
tp::WidgetManager mWidgetManager;
LibraryWidget gui; LibraryWidget gui;
}; };

View file

@ -1,7 +1,6 @@
#include "Library.hpp" #include "Library.hpp"
#include "LocalConnection.hpp" #include "LocalConnection.hpp"
#include "WidgetBase.hpp"
#include "picojson.h" #include "picojson.h"
#include <filesystem> #include <filesystem>

View file

@ -2,7 +2,8 @@
#include "Library.hpp" #include "Library.hpp"
#include "Player.hpp" #include "Player.hpp"
#include "Widgets.hpp" #include "FloatingWidget.hpp"
#include "DockWidget.hpp"
#include "imgui.h" #include "imgui.h"
@ -10,54 +11,83 @@ namespace tp {
class TrackWidget : public Widget { class TrackWidget : public Widget {
public: public:
explicit TrackWidget(const Track* track = nullptr) : enum State {
mTrack(track) { IDLE,
this->mArea.w = 70; HOVER,
col.mColor.setAnimTime(0); SELECTED,
col.mColor.setNoTransition({ 0.15f, 0.15f, 0.15f, 0.f });
}; };
void eventProcess(const Events& events) override { explicit TrackWidget(const Track* track = nullptr) :
mSelected = false; mTrack(track) {
if (!mTrack) return;
if (this->mArea.isInside(events.getPointer())) { getLayout()->setMinSize({ 70, 70 });
col.set({ 0.15f, 0.15f, 0.15f, 1.f }); };
mSelected = events.isReleased(InputID::MOUSE1);
} else { void process(const EventHandler& events) override {
col.set({ 0.15f, 0.15f, 0.15f, 0.f }); mState = IDLE;
if (!mTrack) {
mState = IDLE;
return;
}
switch (mState) {
case HOVER:
case IDLE:
mState = getArea().relative().isInside(events.getPointer()) ? HOVER : IDLE;
if (events.isPressed(InputID::MOUSE1)) {
mState = SELECTED;
}
break;
case SELECTED:
break;
} }
} }
void eventDraw(Canvas& canvas) override { void draw(Canvas& canvas) override {
if (!mTrack) return; if (!mTrack) return;
auto area = getArea().relative();
canvas.rect(this->mArea, col.get(), 4.f); switch (mState) {
case IDLE: canvas.rect(area, colorIdle, rounding); break;
case HOVER: canvas.rect(area, colorHover, rounding); break;
case SELECTED: canvas.rect(area, colorSelected, rounding); break;
}
const RectF imageArea = {
this->mArea.x + margin, this->mArea.y + margin, this->mArea.w - margin * 2, this->mArea.w - margin * 2 const RectF imageArea = { area.x + margin, area.y + margin, area.w - margin * 2, area.w - margin * 2 };
canvas.rect(imageArea, colorImage, rounding);
const RectF textArea = {
area.x + area.w + margin, area.y + margin, area.z - area.w - margin * 2, area.w - margin * 2
}; };
canvas.rect(imageArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
const RectF textArea = { this->mArea.x + this->mArea.w + margin,
this->mArea.y + margin,
this->mArea.z - this->mArea.w - margin * 2,
this->mArea.w - margin * 2 };
// canvas.rect(textArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f); // canvas.rect(textArea, { 0.25f, 0.25f, 0.25f, 1.f }, 4.f);
const RectF textAreaName = { textArea.x, textArea.y, textArea.z, textArea.w * 0.5f }; const RectF textAreaName = { textArea.x, textArea.y, textArea.z, textArea.w * 0.5f };
const RectF textAreaAuthor = { textArea.x, textArea.y + textArea.w * 0.5f, textArea.z, textArea.w * 0.5f }; const RectF textAreaAuthor = { textArea.x, textArea.y + textArea.w * 0.5f, textArea.z, textArea.w * 0.5f };
canvas.text(mTrack->mName.c_str(), textAreaName, 15.f, Canvas::LC, 4.f, { 0.9f, 0.9f, 0.9f, 1.f }); canvas.text(mTrack->mName.c_str(), textAreaName, 15.f, Canvas::LC, rounding, colorNameTrack);
canvas.text(mTrack->mArtist.c_str(), textAreaAuthor, 12.f, Canvas::LC, 4.f, { 0.8f, 0.8f, 0.8f, 1.f }); canvas.text(mTrack->mArtist.c_str(), textAreaAuthor, 12.f, Canvas::LC, rounding, colorNameArtist);
} }
[[nodiscard]] bool processesEvents() const override { return true; }
public: public:
halnf margin = 5.f;
AnimColor col;
const Track* mTrack; const Track* mTrack;
bool mSelected = false; State mState = IDLE;
private:
halnf margin = 5.f;
halnf rounding = 5.f;
RGBA colorIdle = 0;
RGBA colorNameTrack = { 0.9f, 0.9f, 0.9f, 1.f };
RGBA colorNameArtist = { 0.8f, 0.8f, 0.8f, 1.f };
RGBA colorImage = { 0.5, 0.5, 0.5, 1.0 };
RGBA colorHover = { 0.13, 0.13, 0.13, 0.9 };
RGBA colorSelected = { 0.43, 0.43, 0.43, 0.9 };
}; };
class TrackInfoWidget : public Widget { class TrackInfoWidget : public Widget {
@ -74,21 +104,23 @@ namespace tp {
items.append({ "Date Last Played" }); items.append({ "Date Last Played" });
} }
void eventDraw(Canvas&) override { void draw(Canvas&) override {
if (!mTrack) return; if (!mTrack) return;
// canvas.rect(this->mArea, { 0.13f, 0.13f, 0.13f, 1.f }, 4.f); // canvas.rect(this->mArea, { 0.13f, 0.13f, 0.13f, 1.f }, 4.f);
renderUI(); renderUI();
} }
void renderUI() { void renderUI() {
ImGui::SetNextWindowPos({ this->mArea.x, this->mArea.y }); // auto area = getArea();
ImGui::SetNextWindowSize({ this->mArea.z, this->mArea.w });
// ImGui::SetNextWindowPos({ area.x, this->mArea.y });
// ImGui::SetNextWindowSize({ area.z, this->mArea.w });
ImGui::Begin( ImGui::Begin(
"InfoWindow", "InfoWindow",
nullptr, nullptr //,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground | //ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoResize // ImGuiWindowFlags_NoResize
); );
if (mTrack) { if (mTrack) {
@ -184,18 +216,18 @@ namespace tp {
int filterExisting = 0; // all existing no-existing int filterExisting = 0; // all existing no-existing
}; };
class LibraryWidget : public WorkspaceWidget { class LibraryWidget : public DockWidget {
public: public:
LibraryWidget(Library* lib, Player* player) { LibraryWidget(Library* lib, Player* player) {
mLibrary = (lib); mLibrary = (lib);
mPlayer = (player); mPlayer = (player);
updateTracks(); // updateTracks();
mCurrentTrackInfo.mPlayer = mPlayer; mCurrentTrackInfo.mPlayer = mPlayer;
mDockSpace.setCenterWidget(&mSongList); setCenterWidget(&mSongList);
mDockSpace.addSideWidget(&mCurrentTrackInfo, DockWidget::RIGHT); dockWidget(&mCurrentTrackInfo, DockLayout::RIGHT);
// this->mChildWidgets.pushBack(&mCurrentTrackInfo); // this->mChildWidgets.pushBack(&mCurrentTrackInfo);
// this->mChildWidgets.pushBack(&mCurrentTrack); // this->mChildWidgets.pushBack(&mCurrentTrack);
@ -203,24 +235,30 @@ namespace tp {
void updateTracks() { void updateTracks() {
mTracks.clear(); mTracks.clear();
for (auto track : mTracks) {
delete track.data();
}
for (auto track : mLibrary->mTraks) { for (auto track : mLibrary->mTraks) {
mTracks.append(TrackWidget(&track.data())); mTracks.append(new TrackWidget(&track.data()));
} }
} }
void eventProcess(const Events& events) override { void process(const EventHandler& events) override {
filter(); filter();
for (auto track : mSongList.getContent()) { for (auto track : mSongList.getContent()) {
auto trackWidget = (TrackWidget*) track.data(); if (auto trackWidget = dynamic_cast<TrackWidget*>(track.data())) {
if (trackWidget->mSelected) { if (trackWidget->mState == TrackWidget::SELECTED) {
mCurrentTrackInfo.mTrack = trackWidget->mTrack; mCurrentTrackInfo.mTrack = trackWidget->mTrack;
}
} }
} }
// mCurrentTrack.proc(events, this->mArea, mSplitView.getFirst()); // mCurrentTrack.proc(events, this->mArea, mSplitView.getFirst());
WorkspaceWidget::eventProcess(events); DockWidget::process(events);
} }
// void eventDraw(Canvas& canvas) override { // void eventDraw(Canvas& canvas) override {
@ -230,7 +268,7 @@ namespace tp {
void filter() { void filter() {
if (!mCurrentTrackInfo.isSongFilterChanged) return; if (!mCurrentTrackInfo.isSongFilterChanged) return;
mSongList.clearContent(); mSongList.clearChildren();
for (auto track : mTracks) { for (auto track : mTracks) {
if (!mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mName.c_str()) && if (!mCurrentTrackInfo.songFilter.PassFilter(track->mTrack->mName.c_str()) &&
@ -252,7 +290,7 @@ namespace tp {
break; break;
} }
mSongList.addWidget(&track.data()); mSongList.addToMenu(track.data());
} }
mCurrentTrackInfo.isSongFilterChanged = false; mCurrentTrackInfo.isSongFilterChanged = false;
@ -262,9 +300,9 @@ namespace tp {
Library* mLibrary = nullptr; Library* mLibrary = nullptr;
Player* mPlayer = nullptr; Player* mPlayer = nullptr;
Buffer<TrackWidget> mTracks; Buffer<TrackWidget*> mTracks;
ScrollableWindow mSongList; FloatingMenu mSongList;
TrackInfoWidget mCurrentTrackInfo; TrackInfoWidget mCurrentTrackInfo;
TrackWidget mCurrentTrack; TrackWidget mCurrentTrack;
}; };

View file

@ -1,29 +1,19 @@
#include "GraphicApplication.hpp" #include "WidgetApplication.hpp"
#include "Sketch3D.hpp" #include "Sketch3D.hpp"
#include "Sketch3DWidget.hpp" #include "Sketch3DWidget.hpp"
using namespace tp; using namespace tp;
class Sketch3DApplication : public Application { class Sketch3DApplication : public WidgetApplication {
public: public:
Sketch3DApplication() : Sketch3DApplication() :
mGui(*mGraphics->getCanvas(), { 1920, 1080 }) {} mGui(*mGraphics->getCanvas(), { 1920, 1080 }) {
setRoot(&mGui);
void processFrame(EventHandler* eventHandler, halnf delta) override {
auto rec = RectF({ 0, 0 }, mWindow->getSize());
mGui.setVisible(true);
mGui.setArea(rec);
mGui.updateConfigWrapper(mWidgetManager);
mGui.procWrapper(*eventHandler, rec);
} }
void drawFrame(Canvas* canvas) override { mGui.drawWrapper(*canvas); }
private: private:
WidgetManager mWidgetManager;
Sketch3DGUI mGui; Sketch3DGUI mGui;
}; };

View file

@ -1,7 +1,9 @@
#pragma once #pragma once
#include "Sketch3D.hpp" #include "Sketch3D.hpp"
#include "Widgets.hpp" #include "Widget.hpp"
#include "DockWidget.hpp"
#include "FloatingWidget.hpp"
namespace tp { namespace tp {
@ -17,30 +19,31 @@ namespace tp {
~Sketch3DWidget() { mCanvas->deleteImageHandle(mImage); } ~Sketch3DWidget() { mCanvas->deleteImageHandle(mImage); }
void eventProcess(const Events& events) override { void process(const EventHandler& events) override {
if (!this->mArea.isInside(events.getPointer())) { auto area = getArea().relative();
Vec2F pointer = events.getPointer();
auto crs = events.getPointer();
if (!area.isInside(events.getPointer())) {
return; return;
} }
auto crs = (events.getPointer() - this->mArea.pos); crs.x /= area.z;
crs.x /= this->mArea.z; crs.y /= area.w;
crs.y /= this->mArea.w;
crs = (crs - 0.5) * 2; crs = (crs - 0.5) * 2;
// TODO : make better api for events
Vec2F absolutePos = events.getPointer() - this->mArea.pos;
if (events.isPressed(InputID::MOUSE1)) { if (events.isPressed(InputID::MOUSE1)) {
mAction = true; mAction = true;
} else if (events.isReleased(InputID::MOUSE1)) { } else if (events.isReleased(InputID::MOUSE1)) {
mAction = false; mAction = false;
} }
Vec2F relativePos = ((absolutePos / this->mArea.size) - 0.5f) * 2.f; Vec2F relativePos = ((pointer / area.size) - 0.5f) * 2.f;
Vec2F relativePosPrev = ((mActionPosAbsolutePrev / this->mArea.size) - 0.5f) * 2.f; Vec2F relativePosPrev = ((mActionPosAbsolutePrev / area.size) - 0.5f) * 2.f;
Vec2F relativeDelta = relativePos - relativePosPrev; Vec2F relativeDelta = relativePos - relativePosPrev;
mProject.mCamera.setRatio(this->mArea.w / this->mArea.z); mProject.mCamera.setRatio(area.w / area.z);
switch (mMode) { switch (mMode) {
case Mode::MOVE: case Mode::MOVE:
@ -50,7 +53,7 @@ namespace tp {
} }
case Mode::ZOOM: case Mode::ZOOM:
{ {
halnf factor = absolutePos.y / mActionPosAbsolutePrev.y; halnf factor = pointer.y / mActionPosAbsolutePrev.y;
if (mAction) mProject.mCamera.zoom(factor); if (mAction) mProject.mCamera.zoom(factor);
break; break;
} }
@ -61,22 +64,25 @@ namespace tp {
} }
case Mode::DRAW: case Mode::DRAW:
{ {
mProject.sample(events.getPointerPressure(), this->mArea.w / this->mArea.z, crs); mProject.sample(events.getPointerPressure(), area.w / area.z, crs);
break; break;
} }
default: break; default: break;
} }
mActionPosAbsolutePrev = absolutePos; mActionPosAbsolutePrev = pointer;
} }
void eventDraw(Canvas& canvas) override { void draw(Canvas& canvas) override {
mRenderer.renderToTexture(&mProject, this->mArea.size); auto area = getArea().relative();
canvas.drawImage(this->mArea, &mImage, 0, 1, 12); mRenderer.renderToTexture(&mProject, area.size);
canvas.drawImage(area, &mImage, 0, 1, 12);
} }
void setColor(const RGBA& color) { ((PencilBrush*) mProject.mBrushes.get("pencil"))->mCol = color; } void setColor(const RGBA& color) { ((PencilBrush*) mProject.mBrushes.get("pencil"))->mCol = color; }
[[nodiscard]] bool processesEvents() const override { return true; }
public: public:
enum class Mode { MOVE, ROTATE, ZOOM, DRAW, NONE } mMode = Mode::NONE; enum class Mode { MOVE, ROTATE, ZOOM, DRAW, NONE } mMode = Mode::NONE;
@ -90,62 +96,72 @@ namespace tp {
Canvas* mCanvas = nullptr; Canvas* mCanvas = nullptr;
}; };
class Sketch3DGUI : public DockWidget {
class Sketch3DGUI : public WorkspaceWidget {
public: public:
Sketch3DGUI(Canvas& canvas, Vec2F renderResolution) : explicit Sketch3DGUI(Canvas& canvas, Vec2F renderResolution) :
mViewport(canvas, renderResolution) DockWidget(),
{ mViewport(canvas, renderResolution) {
mControls.addWidgetToMenu(&mDrawButton); mControls.addToMenu(&mDrawButton);
mControls.addWidgetToMenu(&mMoveButton); mControls.addToMenu(&mMoveButton);
mControls.addWidgetToMenu(&mRotateButton); mControls.addToMenu(&mRotateButton);
mControls.addWidgetToMenu(&mZoomButton); mControls.addToMenu(&mZoomButton);
mColorPicker.addWidgetToMenu(&mRed); mColorPicker.addToMenu(&mRed);
mColorPicker.addWidgetToMenu(&mGreen); mColorPicker.addToMenu(&mGreen);
mColorPicker.addWidgetToMenu(&mBlue); mColorPicker.addToMenu(&mBlue);
mDockSpace.setCenterWidget(&mViewport); setCenterWidget(&mViewport);
mFloatingLayer.mChildWidgets.pushBack(&mControls); mPanel.addToMenu(&mControls);
mFloatingLayer.mChildWidgets.pushBack(&mColorPicker); mPanel.addToMenu(&mColorPicker);
// addChild(&mPanel);
dockWidget(&mPanel, DockLayout::RIGHT);
mDrawButton.setAction([this]() { mViewport.mMode = Sketch3DWidget::Mode::DRAW; });
mMoveButton.setAction([this]() { mViewport.mMode = Sketch3DWidget::Mode::MOVE; });
mRotateButton.setAction([this]() { mViewport.mMode = Sketch3DWidget::Mode::ROTATE; });
mZoomButton.setAction([this]() { mViewport.mMode = Sketch3DWidget::Mode::ZOOM; });
mDrawButton.setText("Draw");
mMoveButton.setText("Pan");
mRotateButton.setText("Orbit");
mZoomButton.setText("Zoom");
mPanel.setText("Controls");
mControls.setText("Tools");
mColorPicker.setText("Color");
mPanel.setArea({110, 110, 300, 500 });
mControls.setArea({0, 0, 300, 400 });
mColorPicker.setArea({0, 0, 300, 300 });
mDrawButton.mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::DRAW; };
mMoveButton.mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::MOVE; };
mRotateButton.mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::ROTATE; };
mZoomButton.mCallback = [this]() { mViewport.mMode = Sketch3DWidget::Mode::ZOOM; };
} }
void eventProcess(const Events& events) override { void process(const EventHandler& events) override {
WorkspaceWidget::eventProcess(events); DockWidget::process(events);
mViewport.setColor(RGBA(mRed.mSlider.mFactor, mGreen.mSlider.mFactor, mBlue.mSlider.mFactor, 1.f)); mViewport.setColor(RGBA(mRed.val(), mGreen.val(), mBlue.val(), 1.f));
} }
void eventDraw(Canvas& canvas) override { canvas.rect(this->mArea, mBackgroundColor, mRounding); } void draw(Canvas& canvas) override { canvas.rect(getArea().relative(), mBackgroundColor, mRounding); }
void eventUpdateConfiguration(WidgetManager& wm) override {
wm.setActiveId("Sketch3DGui");
mBackgroundColor = wm.getColor("Background", "Background");
mRounding = wm.getNumber("Rounding", "Rounding");
}
private: private:
Sketch3DWidget mViewport; Sketch3DWidget mViewport;
ScrollableWindow mOptions;
FloatingWidget mControls; FloatingMenu mPanel;
ButtonWidget mDrawButton = ButtonWidget("Draw", { 0, 0, 100, 30 });
ButtonWidget mMoveButton = ButtonWidget("Pan", { 0, 0, 100, 30 });
ButtonWidget mRotateButton = ButtonWidget("Orbit", { 0, 0, 100, 30 });
ButtonWidget mZoomButton = ButtonWidget("Zoom", { 0, 0, 100, 30 });
FloatingWidget mColorPicker; FloatingMenu mControls;
NamedSliderWidget mRed = NamedSliderWidget("Red"); ButtonWidget mDrawButton;
NamedSliderWidget mGreen = NamedSliderWidget("Green"); ButtonWidget mMoveButton;
NamedSliderWidget mBlue = NamedSliderWidget("Blue"); ButtonWidget mRotateButton;
ButtonWidget mZoomButton;
FloatingMenu mColorPicker;
SliderWidget mRed;
SliderWidget mGreen;
SliderWidget mBlue;
RGBA mBackgroundColor; RGBA mBackgroundColor;
halnf mRounding = 0; halnf mRounding = 0;

View file

@ -5,17 +5,13 @@ file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
file(GLOB HEADERS "./public/*.hpp") file(GLOB HEADERS "./public/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/) target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ./public/layouts ./public/mangers ./public/widgets)
target_link_libraries(${PROJECT_NAME} PUBLIC Math Graphics Imgui) target_link_libraries(${PROJECT_NAME} PUBLIC Math Graphics Imgui)
### -------------------------- Applications -------------------------- ### ### -------------------------- Applications -------------------------- ###
add_executable(SimpleGui examples/SimpleGUI.cpp) add_executable(Widgets2Example examples/Example.cpp)
target_link_libraries(SimpleGui ${PROJECT_NAME} ${GLEW_LIB}) target_link_libraries(Widgets2Example ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(SimpleGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR}) target_include_directories(Widgets2Example PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
add_executable(ChatGui examples/ChatGUI.cpp)
target_link_libraries(ChatGui ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(ChatGui PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

View file

@ -1,5 +1,5 @@
#include "GraphicApplication.hpp" #include "WidgetApplication.hpp"
#include "RootWidget.hpp" #include "RootWidget.hpp"
#include "FloatingWidget.hpp" #include "FloatingWidget.hpp"
@ -10,10 +10,10 @@
using namespace tp; using namespace tp;
class WidgetApplication : public Application { class Example : public WidgetApplication {
public: public:
WidgetApplication() { Example() {
exampleBasic(); exampleScrolling();
} }
void exampleAll() { void exampleAll() {
@ -33,7 +33,7 @@ public:
menus[0].addToMenu(&menu); menus[0].addToMenu(&menu);
} }
mRootWidget.setRootWidget(&dock); setRoot(&dock);
} }
void exampleBasic() { void exampleBasic() {
@ -46,7 +46,7 @@ public:
widget.addChild(&button); widget.addChild(&button);
widget.addChild(&slider); widget.addChild(&slider);
mRootWidget.setRootWidget(&widget); setRoot(&widget);
} }
void exampleScrolling() { void exampleScrolling() {
@ -55,7 +55,7 @@ public:
static ButtonWidget buttons[10]; static ButtonWidget buttons[10];
static ScrollableBarWidget scrollBar; static ScrollableBarWidget scrollBar;
mRootWidget.setRootWidget(&widget); setRoot(&widget);
widget.addChild(&scrollBar); widget.addChild(&scrollBar);
widget.addChild(&content); widget.addChild(&content);
@ -91,7 +91,7 @@ public:
RootWidget::setWidgetArea(openButton, { 333, 333, 222, 222 }); RootWidget::setWidgetArea(openButton, { 333, 333, 222, 222 });
mRootWidget.setRootWidget(&root); setRoot(&root);
} }
void exampleNestedMenus() { void exampleNestedMenus() {
@ -100,7 +100,7 @@ public:
static FloatingMenu menu2; static FloatingMenu menu2;
static ButtonWidget buttons[15]; static ButtonWidget buttons[15];
mRootWidget.setRootWidget(&dock); setRoot(&dock);
dock.addChild(&menu1); dock.addChild(&menu1);
dock.addChild(&menu2); dock.addChild(&menu2);
@ -140,7 +140,7 @@ public:
static Widget widgets[15]; static Widget widgets[15];
static ButtonWidget buttons[15]; static ButtonWidget buttons[15];
mRootWidget.setRootWidget(&widgets[0]); setRoot(&widgets[0]);
widgets[0].addChild(&widgets[1]); widgets[0].addChild(&widgets[1]);
@ -173,7 +173,7 @@ public:
static FloatingMenu menu2; static FloatingMenu menu2;
static ButtonWidget buttons[15]; static ButtonWidget buttons[15];
mRootWidget.setRootWidget(&dock); setRoot(&dock);
dock.addChild(&menu2); dock.addChild(&menu2);
dock.addChild(&menu1); dock.addChild(&menu1);
@ -203,29 +203,11 @@ public:
RootWidget::setWidgetArea(menu2, { 300, 100, 150, 500 }); RootWidget::setWidgetArea(menu2, { 300, 100, 150, 500 });
RootWidget::setWidgetArea(menu1, { 100, 100, 150, 300 }); RootWidget::setWidgetArea(menu1, { 100, 100, 150, 300 });
} }
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
const auto rec = RectF({ 0, 0 }, mWindow->getSize());
mRootWidget.processFrame(eventHandler, rec);
}
void drawFrame(Canvas* canvas) override {
mRootWidget.drawFrame(*canvas);
// drawDebug();
}
bool forceNewFrame() override {
return mRootWidget.needsUpdate();
}
private:
RootWidget mRootWidget;
}; };
int main() { int main() {
{ {
WidgetApplication gui; Example gui;
gui.run(); gui.run();
} }
} }

View file

@ -80,10 +80,12 @@ void RootWidget::drawRecursion(Canvas& canvas, Widget* active, const Vec2F& pos)
canvas.setOrigin(pos); canvas.setOrigin(pos);
canvas.pushClamp({ pos, active->getArea().size }); canvas.pushClamp({ pos, active->getArea().size });
active->draw(canvas); if (canvas.getClampedArea().size.length2() > EPSILON) {
active->draw(canvas);
for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) { for (auto child = active->mDepthOrder.lastNode(); child; child = child->prev) {
drawRecursion(canvas, child->data, pos + child->data->getArea().pos); drawRecursion(canvas, child->data, pos + child->data->getArea().pos);
}
} }
canvas.setOrigin(pos); canvas.setOrigin(pos);
@ -150,3 +152,4 @@ void RootWidget::closePopup(Widget* widget) {
void RootWidget::lockFocus(Widget* widget) { mUpdateManager.lockFocus(widget); } void RootWidget::lockFocus(Widget* widget) { mUpdateManager.lockFocus(widget); }
void RootWidget::freeFocus(Widget* widget) { mUpdateManager.freeFocus(widget); } void RootWidget::freeFocus(Widget* widget) { mUpdateManager.freeFocus(widget); }
bool RootWidget::isDebug() const { return gDebugWidget.isDebug(); }

View file

@ -51,6 +51,11 @@ void Widget::removeChild(Widget* child) {
child->triggerWidgetUpdate("parent changed"); child->triggerWidgetUpdate("parent changed");
} }
void Widget::clear() {
mChildren.clear();
mDepthOrder.removeAll();
}
void Widget::endAnimations() { mArea.endAnimation(); } void Widget::endAnimations() { mArea.endAnimation(); }
bool Widget::processesEvents() const { return false; } bool Widget::processesEvents() const { return false; }
void Widget::updateAnimations() { mArea.updateCurrentRect(); } void Widget::updateAnimations() { mArea.updateCurrentRect(); }

View file

@ -0,0 +1,10 @@
#include "WidgetApplication.hpp"
#include "imgui.h"
using namespace tp;
void WidgetApplication::debugUI() {
ImGui::Text("Proc ms : %i", (int) mProcTime);
ImGui::SameLine(); ImGui::Text("Draw ms : %i", (int) mDrawTime);
}

View file

@ -169,7 +169,7 @@ void DockLayout::calculateHeaderAreas() {
} }
} }
ualni DockLayout::getVisibleSidesSize() { ualni DockLayout::getVisibleSidesSize() const {
ualni out = 0; ualni out = 0;
for (ualni i = 0; i < 4; i++) { for (ualni i = 0; i < 4; i++) {
if (isSideVisible(Side(i))) out++; if (isSideVisible(Side(i))) out++;

View file

@ -69,4 +69,8 @@ FloatingMenu::FloatingMenu() : FloatingWidget() {
// getLayout()->setLayoutPolicy(LayoutPolicy::Vertical); // getLayout()->setLayoutPolicy(LayoutPolicy::Vertical);
// mBodyLayout.getLayout()->setLayoutPolicy(LayoutPolicy::Vertical); // mBodyLayout.getLayout()->setLayoutPolicy(LayoutPolicy::Vertical);
} }
void FloatingMenu::setText(const std::string& text) {
mHeader.setText(text);
}

View file

@ -20,6 +20,7 @@ namespace tp {
class WidgetLayout { class WidgetLayout {
friend class DebugManager; friend class DebugManager;
friend class LayoutManager;
public: public:
explicit WidgetLayout(Widget* widget) { mWidget = widget; } explicit WidgetLayout(Widget* widget) { mWidget = widget; }
@ -62,5 +63,8 @@ namespace tp {
Vec2<SizePolicy> mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed }; Vec2<SizePolicy> mSizePolicy = { SizePolicy::Fixed, SizePolicy::Fixed };
Vec2F mMinSize = { 50, 50 }; Vec2F mMinSize = { 50, 50 };
Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 }; Vec2F mMaxSize = { FLT_MAX / 2, FLT_MAX / 2 };
private:
// RectF mPrevArea{};
}; };
} }

View file

@ -18,6 +18,8 @@ namespace tp {
void setRootWidget(Widget* widget); void setRootWidget(Widget* widget);
static void setWidgetArea(Widget& widget, const RectF& rect); static void setWidgetArea(Widget& widget, const RectF& rect);
[[nodiscard]] bool isDebug() const;
// Graphic Application Interface // Graphic Application Interface
public: public:
void processFrame(EventHandler* events, const RectF& screenArea); void processFrame(EventHandler* events, const RectF& screenArea);

View file

@ -39,11 +39,18 @@ namespace tp {
}; };
public: public:
Widget(const Widget&&) = delete;
Widget(const Widget&) = delete;
void operator=(const Widget&) = delete;
Widget(); Widget();
virtual ~Widget(); virtual ~Widget();
void addChild(Widget* child, bool front = false); void addChild(Widget* child, bool front = false);
void removeChild(Widget* child); void removeChild(Widget* child);
void clear();
const List<Widget*>& getChildren() { return mDepthOrder; }
void bringToFront(); void bringToFront();
void bringToBack(); void bringToBack();

View file

@ -0,0 +1,49 @@
#pragma once
#include "GraphicApplication.hpp"
#include "RootWidget.hpp"
#include "Timing.hpp"
namespace tp {
class WidgetApplication : public Application {
public:
WidgetApplication() = default;
void setRoot(Widget* widget) {
mRootWidget.setRootWidget(widget);
}
private:
void processFrame(EventHandler* eventHandler, halnf deltaTime) override {
const auto rec = RectF({ 0, 0 }, mWindow->getSize());
Timer timer;
mRootWidget.processFrame(eventHandler, rec);
mProcTime = timer.timePassed();
}
void drawFrame(Canvas* canvas) override {
Timer timer;
mRootWidget.drawFrame(*canvas);
mDrawTime = timer.timePassed();
if (mRootWidget.isDebug()) {
drawDebug();
debugUI();
}
}
bool forceNewFrame() override {
return mRootWidget.needsUpdate();
}
private:
void debugUI();
private:
time_ms mProcTime = 0;
time_ms mDrawTime = 0;
RootWidget mRootWidget;
};
}

View file

@ -17,7 +17,7 @@ namespace tp {
struct SideWidgetData { struct SideWidgetData {
Widget* widget = nullptr; Widget* widget = nullptr;
bool hidden = false; bool hidden = false;
halnf absoluteSize = 200; halnf absoluteSize = 300;
alni order = -1; alni order = -1;
Side side = { TOP }; Side side = { TOP };
@ -76,7 +76,7 @@ namespace tp {
const std::vector<Widget*>& getDockedWidgets(); const std::vector<Widget*>& getDockedWidgets();
private: private:
ualni getVisibleSidesSize(); ualni getVisibleSidesSize() const;
void calculateSideAreas(); void calculateSideAreas();
void calculateResizeHandles(); void calculateResizeHandles();

View file

@ -27,6 +27,8 @@ namespace tp {
} }
} }
[[nodiscard]] bool isDebug() const { return mDebug; }
private: private:
void recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder); void recursiveDraw(Canvas& canvas, Widget* active, const Vec2F& pos, int depthOrder);

View file

@ -39,6 +39,16 @@ namespace tp {
mContentWidget.addChild(widget); mContentWidget.addChild(widget);
} }
const List<Widget*>& getContent() {
return mContentWidget.getChildren();
}
void clearChildren() {
mContentWidget.clear();
}
void setText(const std::string& text);
private: private:
// VerticalLayout mMenuLayout; // VerticalLayout mMenuLayout;
Widget mBodyLayout; Widget mBodyLayout;

View file

@ -73,8 +73,10 @@ namespace tp {
[[nodiscard]] bool processesEvents() const override { return true; } [[nodiscard]] bool processesEvents() const override { return true; }
[[nodiscard]] bool needsNextFrame() const override { return mState != SLIDING; } [[nodiscard]] bool needsNextFrame() const override { return mState != SLIDING; }
[[nodiscard]] halnf val() const { return mFactor; }
private: private:
RectF getHandleArea() const; [[nodiscard]] RectF getHandleArea() const;
private: private:
halnf mFactor = 0; halnf mFactor = 0;

View file

@ -1,17 +0,0 @@
project(WidgetsNew)
### ---------------------- Static Library --------------------- ###
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
file(GLOB HEADERS "./public/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ./public/layouts ./public/mangers ./public/widgets)
target_link_libraries(${PROJECT_NAME} PUBLIC Math Graphics Imgui)
### -------------------------- Applications -------------------------- ###
add_executable(Widgets2Example examples/Example.cpp)
target_link_libraries(Widgets2Example ${PROJECT_NAME} ${GLEW_LIB})
target_include_directories(Widgets2Example PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")