From 815910ac9045eee658928fb4a8c483fc9a3e3069 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 25 Dec 2018 22:38:26 -0500 Subject: [PATCH] Add inspector --- tools/common/CMakeLists.txt | 7 +- tools/common/include/collapsesection.h | 28 ++++++++ tools/common/include/context.h | 9 ++- tools/common/include/vec3edit.h | 23 ++++++ tools/common/src/collapsesection.cpp | 75 ++++++++++++++++++++ tools/common/src/vec3edit.cpp | 58 ++++++++++++++++ tools/leveleditor/CMakeLists.txt | 4 +- tools/leveleditor/include/inspector.h | 28 ++++++++ tools/leveleditor/include/mainwindow.h | 1 + tools/leveleditor/src/hierarchy.cpp | 2 + tools/leveleditor/src/inspector.cpp | 96 ++++++++++++++++++++++++++ tools/leveleditor/src/mainwindow.cpp | 13 ++++ 12 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 tools/common/include/collapsesection.h create mode 100644 tools/common/include/vec3edit.h create mode 100644 tools/common/src/collapsesection.cpp create mode 100644 tools/common/src/vec3edit.cpp create mode 100644 tools/leveleditor/include/inspector.h create mode 100644 tools/leveleditor/src/inspector.cpp diff --git a/tools/common/CMakeLists.txt b/tools/common/CMakeLists.txt index 1ebf98f..7e41ab9 100644 --- a/tools/common/CMakeLists.txt +++ b/tools/common/CMakeLists.txt @@ -1,6 +1,9 @@ set(INCLUDE_FILES include/renderwindow.h - include/coloredit.h) + include/coloredit.h + include/context.h + include/vec3edit.h + include/collapsesection.h) qt5_wrap_cpp(EDITOR_SRC ${INCLUDE_FILES}) @@ -9,6 +12,8 @@ add_library(EditorCommon src/renderwindow.cpp src/editorstyle.cpp src/coloredit.cpp + src/vec3edit.cpp + src/collapsesection.cpp ${EDITOR_SRC}) target_include_directories(EditorCommon PUBLIC include) target_link_libraries(EditorCommon PRIVATE Engine Qt5::Widgets) diff --git a/tools/common/include/collapsesection.h b/tools/common/include/collapsesection.h new file mode 100644 index 0000000..630f3b8 --- /dev/null +++ b/tools/common/include/collapsesection.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +class CollapseSection : public QWidget { + Q_OBJECT +public: + CollapseSection(QString label, bool closable = false); + +signals: + void closeRequested(); + +protected: + void paintEvent(QPaintEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + +private: + QString label; + + bool closable; + QRect closeButtonRect; + bool closeButtonHovered = false; + bool collapsed = false; + + QWidget* wrapper; + QLayout* layout; +}; diff --git a/tools/common/include/context.h b/tools/common/include/context.h index 6ac2eb4..6bb6dd8 100644 --- a/tools/common/include/context.h +++ b/tools/common/include/context.h @@ -1,11 +1,18 @@ #pragma once +#include + class Renderer; using EntityID = uint64_t; -struct Context { +class Context : public QObject { + Q_OBJECT +public: Renderer* renderer; std::vector selectedEntities; + +signals: + void selectionChanged(); }; diff --git a/tools/common/include/vec3edit.h b/tools/common/include/vec3edit.h new file mode 100644 index 0000000..e38d448 --- /dev/null +++ b/tools/common/include/vec3edit.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +class Vector3Edit : public QWidget { + Q_OBJECT +public: + explicit Vector3Edit(glm::vec3& vec, QWidget* parent = nullptr); + ~Vector3Edit(); + +signals: + void onValueChanged(); + +private: + struct { + QDoubleSpinBox *x, *y, *z; + } spinBoxes; + + glm::vec3& vec; + QTimer* updateTimer; +}; diff --git a/tools/common/src/collapsesection.cpp b/tools/common/src/collapsesection.cpp new file mode 100644 index 0000000..c4e16c4 --- /dev/null +++ b/tools/common/src/collapsesection.cpp @@ -0,0 +1,75 @@ +#include "collapsesection.h" + +#include +#include +#include +#include +#include + +#include + +CollapseSection::CollapseSection(QString label, bool closable) : label(label), closable(closable) { + setContentsMargins(0, 25, 0, 0); + setMouseTracking(true); +} + +void CollapseSection::paintEvent(QPaintEvent* event) { + QPainter painter(this); + painter.setPen(QColor(75, 75, 80)); + painter.setBrush(QColor(50, 50, 55)); + + QRect r = event->rect().adjusted(1, 2, -1, 0); + r.setHeight(25); + + painter.drawRect(r); + + painter.setPen(Qt::white); + painter.drawText(event->rect().adjusted(6, 5, 0, 0), label); + + if(closable) { + QStyleOption option; + option.rect.adjust(event->rect().width() - 20, 7, 0, 0); + option.rect.setHeight(16); + option.rect.setWidth(16); + option.state = QStyle::State_Active|QStyle::State_Enabled|QStyle::State_AutoRaise; + + if(closeButtonHovered) + option.state |= QStyle::State_Raised | QStyle::State_MouseOver; + + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &option, &painter, this); + } +} + +void CollapseSection::mouseMoveEvent(QMouseEvent* event) { + if(closable) { + QRect r(width() - 20, 0, width(), 25); // close button + + if(r.contains(mapFromGlobal(QCursor::pos()))) + closeButtonHovered = true; + else + closeButtonHovered = false; + + repaint(); + } +} + +void CollapseSection::mousePressEvent(QMouseEvent* event) { + QRect r(0, 0, width() - 20, 30); // header + + if(r.contains(mapFromGlobal(QCursor::pos()))) { + if(!collapsed) { + setFixedHeight(30); + collapsed = true; + } else { + setFixedHeight(QWIDGETSIZE_MAX); + collapsed = false; + } + } + + if(closable) { + QRect r(width() - 20, 0, width(), 25); // close button + + if(r.contains(mapFromGlobal(QCursor::pos()))) + emit closeRequested(); + } +} diff --git a/tools/common/src/vec3edit.cpp b/tools/common/src/vec3edit.cpp new file mode 100644 index 0000000..05fa378 --- /dev/null +++ b/tools/common/src/vec3edit.cpp @@ -0,0 +1,58 @@ +#include "vec3edit.h" + +#include +#include + +Vector3Edit::Vector3Edit(glm::vec3& vec, QWidget* parent) : QWidget(parent), vec(vec) { + QHBoxLayout* itemsLayout = new QHBoxLayout(this); + + spinBoxes.x = new QDoubleSpinBox(); + spinBoxes.y = new QDoubleSpinBox(); + spinBoxes.z = new QDoubleSpinBox(); + + spinBoxes.x->setMinimum(-10000.0); + spinBoxes.x->setMaximum(10000.0); + + spinBoxes.y->setMinimum(-10000.0); + spinBoxes.y->setMaximum(10000.0); + + spinBoxes.z->setMinimum(-10000.0); + spinBoxes.z->setMaximum(10000.0); + + itemsLayout->addWidget(spinBoxes.x); + itemsLayout->addWidget(spinBoxes.y); + itemsLayout->addWidget(spinBoxes.z); + + spinBoxes.x->setValue(vec.x); + spinBoxes.y->setValue(vec.y); + spinBoxes.z->setValue(vec.z); + + connect(spinBoxes.x, static_cast(&QDoubleSpinBox::valueChanged), [this, &vec](double d) { + vec.x = d; + emit onValueChanged(); + }); + connect(spinBoxes.y, static_cast(&QDoubleSpinBox::valueChanged), [this, &vec](double d) { + vec.y = d; + emit onValueChanged(); + }); + connect(spinBoxes.z, static_cast(&QDoubleSpinBox::valueChanged), [this, &vec](double d) { + vec.z = d; + emit onValueChanged(); + }); + + // TODO: find a better way to do this + updateTimer = new QTimer(); + connect(updateTimer, &QTimer::timeout, [this, &vec]() { + if (vec.x != spinBoxes.x->value() || vec.y != spinBoxes.y->value() || vec.z != spinBoxes.z->value()) { + spinBoxes.x->setValue(vec.x); + spinBoxes.y->setValue(vec.y); + spinBoxes.z->setValue(vec.z); + } + }); + + updateTimer->start(1); +} + +Vector3Edit::~Vector3Edit() { + updateTimer->stop(); +} diff --git a/tools/leveleditor/CMakeLists.txt b/tools/leveleditor/CMakeLists.txt index 7731351..7e34a06 100644 --- a/tools/leveleditor/CMakeLists.txt +++ b/tools/leveleditor/CMakeLists.txt @@ -1,6 +1,7 @@ set(INCLUDE_FILES include/mainwindow.h - include/hierarchy.h) + include/hierarchy.h + include/inspector.h) qt5_wrap_cpp(EDITOR_SRC ${INCLUDE_FILES}) @@ -8,6 +9,7 @@ add_executable(LevelEditor src/main.cpp src/mainwindow.cpp src/hierarchy.cpp + src/inspector.cpp ${EDITOR_SRC}) target_include_directories(LevelEditor PRIVATE include) target_link_libraries(LevelEditor Qt5::Widgets Engine ToolWindowManager EditorCommon) diff --git a/tools/leveleditor/include/inspector.h b/tools/leveleditor/include/inspector.h new file mode 100644 index 0000000..acb870d --- /dev/null +++ b/tools/leveleditor/include/inspector.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +struct Context; +struct InfoComponent; +struct TransformComponent; +struct MeshComponent; + +class Inspector : public QWidget { + Q_OBJECT +public: + Inspector(Context& context, QWidget* parent = nullptr); + +private: + void rebuild(); + + void addInfoInspector(InfoComponent* info); + void addTransformInspector(TransformComponent* transform); + void addMeshInspector(MeshComponent* mesh); + + QVBoxLayout* layout = nullptr; + + QList sections; + + Context& context; +}; diff --git a/tools/leveleditor/include/mainwindow.h b/tools/leveleditor/include/mainwindow.h index 4a70b3c..6bc0a09 100644 --- a/tools/leveleditor/include/mainwindow.h +++ b/tools/leveleditor/include/mainwindow.h @@ -13,6 +13,7 @@ public: private: void addSceneView(); void addHierarchy(); + void addInspector(); Context& context; ToolWindowManager* manager = nullptr; diff --git a/tools/leveleditor/src/hierarchy.cpp b/tools/leveleditor/src/hierarchy.cpp index be8a121..e50eb12 100644 --- a/tools/leveleditor/src/hierarchy.cpp +++ b/tools/leveleditor/src/hierarchy.cpp @@ -24,6 +24,8 @@ Hierarchy::Hierarchy(Context& context, QWidget* parent) : QWidget(parent), conte context.selectedEntities.push_back(entity); } + + emit context.selectionChanged(); }); listModel = new QStringListModel(); diff --git a/tools/leveleditor/src/inspector.cpp b/tools/leveleditor/src/inspector.cpp new file mode 100644 index 0000000..180d453 --- /dev/null +++ b/tools/leveleditor/src/inspector.cpp @@ -0,0 +1,96 @@ +#include "inspector.h" + +#include +#include +#include + +#include "context.h" +#include "ecs.h" +#include "worldmanager.h" +#include "vec3edit.h" +#include "collapsesection.h" + +Inspector::Inspector(Context& context, QWidget* parent) : QWidget(parent), context(context) { + setWindowTitle("Inspector"); + setWindowIcon(QIcon::fromTheme("edit-cut")); + + layout = new QVBoxLayout(); + layout->setAlignment(Qt::AlignTop); + layout->setSpacing(0); + + setLayout(layout); + + rebuild(); + + connect(&context, &Context::selectionChanged, this, &Inspector::rebuild); +} + +void Inspector::rebuild() { + for(auto section : sections) { + layout->removeWidget(section); + delete section; + } + + sections.clear(); + + if(context.selectedEntities.size() == 0) + return; + + const auto& entity = context.selectedEntities[0]; + + InfoComponent* info = ECS::getComponent(entity); + if(info) + addInfoInspector(info); + + TransformComponent* transform = ECS::getComponent(entity); + if(transform) + addTransformInspector(transform); + + MeshComponent* mesh = ECS::getComponent(entity); + if(mesh) + addMeshInspector(mesh); +} + +void Inspector::addInfoInspector(InfoComponent* info) { + CollapseSection* section = new CollapseSection("Info"); + layout->addWidget(section); + sections.push_back(section); + + QGridLayout* layout = new QGridLayout(); + section->setLayout(layout); + + QLabel* nameLabel = new QLabel("Name"); + layout->addWidget(nameLabel); + + QLineEdit* nameEdit = new QLineEdit(); + nameEdit->setText(info->name.c_str()); + + connect(nameEdit, &QLineEdit::editingFinished, [info, nameEdit] { + info->name = nameEdit->text().toStdString(); + }); + layout->addWidget(nameEdit, 0, 1); +} + +void Inspector::addTransformInspector(TransformComponent* transform) { + CollapseSection* section = new CollapseSection("Transform"); + layout->addWidget(section); + sections.push_back(section); + + QGridLayout* layout = new QGridLayout(); + section->setLayout(layout); + + QLabel* positionLabel = new QLabel("Position"); + layout->addWidget(positionLabel); + + Vector3Edit* positionEdit = new Vector3Edit(transform->position); + layout->addWidget(positionEdit, 0, 1); +} + +void Inspector::addMeshInspector(MeshComponent* mesh) { + CollapseSection* section = new CollapseSection("Mesh", true); + layout->addWidget(section); + sections.push_back(section); + + QGridLayout* layout = new QGridLayout(); + section->setLayout(layout); +} diff --git a/tools/leveleditor/src/mainwindow.cpp b/tools/leveleditor/src/mainwindow.cpp index 4219e85..b538f22 100644 --- a/tools/leveleditor/src/mainwindow.cpp +++ b/tools/leveleditor/src/mainwindow.cpp @@ -11,6 +11,7 @@ #include "renderwindow.h" #include "renderer.h" #include "hierarchy.h" +#include "inspector.h" MainWindow::MainWindow(Context& context) : context(context) { setWindowTitle("Level Editor"); @@ -122,6 +123,11 @@ MainWindow::MainWindow(Context& context) : context(context) { connect(addHierarchy, &QAction::triggered, this, &MainWindow::addHierarchy); addHierarchy->setIcon(QIcon::fromTheme("view-sort-ascending")); newViewMenu->addAction(addHierarchy); + + QAction* addInspector = new QAction("Inspector"); + connect(addInspector, &QAction::triggered, this, &MainWindow::addInspector); + addInspector->setIcon(QIcon::fromTheme("edit-cut")); + newViewMenu->addAction(addInspector); } mainMenuBar->addSeparator(); @@ -138,6 +144,7 @@ MainWindow::MainWindow(Context& context) : context(context) { addSceneView(); addHierarchy(); + addInspector(); } void MainWindow::addSceneView() { @@ -157,3 +164,9 @@ void MainWindow::addHierarchy() { manager->addToolWindow(hierarchy, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); } +void MainWindow::addInspector() { + Inspector* inspector = new Inspector(context); + + manager->addToolWindow(inspector, ToolWindowManager::AreaReference(ToolWindowManager::EmptySpace)); +} +