Compare commits

...

4 commits

15 changed files with 250 additions and 6 deletions

View file

@ -0,0 +1,22 @@
Silk icon set 1.3
_________________________________________
Mark James
http://www.famfamfam.com/lab/icons/silk/
_________________________________________
This work is licensed under a
Creative Commons Attribution 2.5 License.
[ http://creativecommons.org/licenses/by/2.5/ ]
This means you may use it for any purpose,
and make any changes you like.
All I ask is that you include a link back
to this page in your credits.
Are you using this icon set? Send me an email
(including a link or picture if available) to
mjames@gmail.com
Any other questions about this icon set please
contact mjames@gmail.com

BIN
assets/icons/instance.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

BIN
assets/icons/part.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

BIN
assets/icons/script.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

BIN
assets/icons/workspace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

View file

@ -25,6 +25,8 @@ set(PROJECT_SOURCES
mainwindow.ui
mainglwidget.h
mainglwidget.cpp
explorermodel.h
explorermodel.cpp
${TS_FILES}
)

140
editor/explorermodel.cpp Normal file
View file

@ -0,0 +1,140 @@
#include "explorermodel.h"
#include "objects/base/instance.h"
#include "qcontainerfwd.h"
#include "qicon.h"
#include "qimage.h"
#include "qnamespace.h"
#include "qobject.h"
#include "qwidget.h"
#include "common.h"
#include <algorithm>
#include <optional>
#include "objects/base/instance.h"
// https://doc.qt.io/qt-6/qtwidgets-itemviews-simpletreemodel-example.html#testing-the-model
std::map<std::string, QImage> instanceIconCache;
ExplorerModel::ExplorerModel(InstanceRef dataRoot, QWidget *parent)
: QAbstractItemModel(parent)
, rootItem(dataRoot) {
// TODO: Don't use lambdas and handlers like that
hierarchyPreUpdateHandler = [&](InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent) {
if (oldParent.has_value()) {
auto children = oldParent.value()->GetChildren();
size_t idx = std::find(children.begin(), children.end(), object) - children.end();
beginRemoveRows(toIndex(oldParent.value()), idx, idx);
}
if (newParent.has_value()) {
size_t size = newParent.value()->GetChildren().size();
beginInsertRows(toIndex(newParent.value()), size, size);
} else {
// TODO:
}
};
hierarchyPostUpdateHandler = [&](InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent) {
if (newParent.has_value()) endInsertRows();
if (oldParent.has_value()) endRemoveRows();
};
}
ExplorerModel::~ExplorerModel() = default;
QModelIndex ExplorerModel::index(int row, int column, const QModelIndex &parent) const {
if (!hasIndex(row, column, parent))
return {};
Instance* parentItem = parent.isValid()
? static_cast<Instance*>(parent.internalPointer())
: rootItem.get();
if (parentItem->GetChildren().size() >= row)
return createIndex(row, column, parentItem->GetChildren()[row].get());
return {};
}
QModelIndex ExplorerModel::toIndex(InstanceRef item) {
if (item == rootItem)
return {};
InstanceRef parentItem = item->GetParent().value();
// Check above ensures this item is not root, so value() must be valid
for (int i = 0; i < parentItem->GetChildren().size(); i++)
if (parentItem->GetChildren()[i] == item)
return createIndex(i, 0, item.get());
return QModelIndex{};
}
QModelIndex ExplorerModel::parent(const QModelIndex &index) const {
if (!index.isValid())
return {};
Instance* childItem = static_cast<Instance*>(index.internalPointer());
// NORISK: The parent must exist if the child was obtained from it during this frame
InstanceRef parentItem = childItem->GetParent().value();
if (parentItem == rootItem)
return {};
// Check above ensures this item is not root, so value() must be valid
InstanceRef parentParent = parentItem->GetParent().value();
for (int i = 0; i < parentParent->GetChildren().size(); i++)
if (parentParent->GetChildren()[i] == parentItem)
return createIndex(i, 0, parentItem.get());
return QModelIndex{};
}
int ExplorerModel::rowCount(const QModelIndex &parent) const {
if (parent.column() > 0)
return 0;
Instance* parentItem = parent.isValid()
? static_cast<Instance*>(parent.internalPointer())
: rootItem.get();
return parentItem->GetChildren().size();
}
int ExplorerModel::columnCount(const QModelIndex &parent) const {
return 1;
}
QVariant ExplorerModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return {};
Instance *item = static_cast<Instance*>(index.internalPointer());
switch (role) {
case Qt::DisplayRole:
return QString::fromStdString(item->name);
case Qt::DecorationRole:
return iconOf(item->GetClass());
}
return {};
}
QVariant ExplorerModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
return QString("Idk lol \u00AF\u005C\u005F\u0028\u30C4\u0029\u005F\u002F\u00AF");
}
Qt::ItemFlags ExplorerModel::flags(const QModelIndex &index) const
{
return index.isValid()
? QAbstractItemModel::flags(index) : Qt::ItemFlags(Qt::NoItemFlags);
}
QImage ExplorerModel::iconOf(InstanceType* type) const {
if (instanceIconCache.count(type->className)) return instanceIconCache[type->className];
InstanceType* currentClass = type;
while (currentClass->explorerIcon.empty()) currentClass = currentClass->super;
QImage icon("assets/icons/" + QString::fromStdString(currentClass->explorerIcon));
instanceIconCache[type->className] = icon;
return icon;
}

36
editor/explorermodel.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef EXPLORERMODEL_H
#define EXPLORERMODEL_H
#include "objects/base/instance.h"
#include "objects/part.h"
#include "qabstractitemmodel.h"
#include "qevent.h"
#include <QOpenGLWidget>
#include <QWidget>
#include <memory>
class ExplorerModel : public QAbstractItemModel {
Q_OBJECT
public:
Q_DISABLE_COPY_MOVE(ExplorerModel)
explicit ExplorerModel(InstanceRef dataRoot, QWidget *parent = nullptr);
~ExplorerModel() override;
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = {}) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = {}) const override;
int columnCount(const QModelIndex &parent = {}) const override;
private:
InstanceRef rootItem;
QModelIndex toIndex(InstanceRef item);
QImage iconOf(InstanceType* type) const;
};
#endif // EXPLORERMODEL_H

View file

@ -8,10 +8,14 @@
#include <QTimerEvent>
#include <QMouseEvent>
#include <QWidget>
#include <QTreeView>
#include <QAbstractItemView>
#include <memory>
#include "common.h"
#include "physics/simulation.h"
#include "objects/part.h"
#include "explorermodel.h"
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
@ -23,6 +27,9 @@ MainWindow::MainWindow(QWidget *parent)
timer.start(33, this);
setMouseTracking(true);
ui->explorerView->setModel(new ExplorerModel(std::dynamic_pointer_cast<Instance>(workspace)));
ui->explorerView->setRootIsDecorated(false);
simulationInit();
// Baseplate

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<width>1027</width>
<height>600</height>
</rect>
</property>
@ -40,12 +40,27 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<width>1027</width>
<height>29</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QDockWidget" name="explorerWidget">
<property name="windowTitle">
<string>Explorer</string>
</property>
<attribute name="dockWidgetArea">
<number>2</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="explorerView"/>
</item>
</layout>
</widget>
</widget>
</widget>
<customwidgets>
<customwidget>

View file

@ -5,4 +5,6 @@
Camera camera(glm::vec3(0.0, 0.0, 3.0));
//std::vector<Part> parts;
std::shared_ptr<Workspace> workspace = Workspace::New();
std::shared_ptr<Workspace> workspace = Workspace::New();
std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;

View file

@ -1,8 +1,17 @@
#pragma once
#include "objects/workspace.h"
#include "camera.h"
#include <functional>
#include <memory>
class Instance;
// typedef std::function<void(std::shared_ptr<Instance> element, std::optional<std::shared_ptr<Instance>> newParent)> HierarchyUpdateHandler;
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPreUpdateHandler;
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler;
// TEMPORARY COMMON DATA FOR DIFFERENT INTERNAL COMPONENTS
extern Camera camera;
extern std::shared_ptr<Workspace> workspace;
extern std::shared_ptr<Workspace> workspace;
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;

View file

@ -1,4 +1,5 @@
#include "instance.h"
#include "../../common.h"
#include <algorithm>
#include <memory>
#include <optional>
@ -8,6 +9,7 @@ static InstanceType TYPE_ {
.super = NULL,
.className = "Instance",
.constructor = NULL, // Instance is abstract and therefore not creatable
.explorerIcon = "instance",
};
InstanceType* Instance::TYPE = &TYPE_;
@ -25,6 +27,8 @@ Instance::~Instance () {
}
void Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
auto lastParent = GetParent();
if (hierarchyPreUpdateHandler.has_value()) hierarchyPreUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
// If we currently have a parent, remove ourselves from it before adding ourselves to the new one
if (this->parent.has_value() && !this->parent.value().expired()) {
auto oldParent = this->parent.value().lock();
@ -36,6 +40,8 @@ void Instance::SetParent(std::optional<std::shared_ptr<Instance>> newParent) {
}
this->parent = newParent;
// TODO: Add code for sending signals for parent updates
// TODO: Yeahhh maybe this isn't the best way of doing this?
if (hierarchyPostUpdateHandler.has_value()) hierarchyPostUpdateHandler.value()(this->shared_from_this(), lastParent, newParent);
}
std::optional<std::shared_ptr<Instance>> Instance::GetParent() {

View file

@ -9,10 +9,14 @@ struct InstanceType {
InstanceType* super; // May be null
std::string className;
InstanceConstructor constructor;
std::string explorerIcon = "";
};
// Base class for all instances in the data model
class Instance : std::enable_shared_from_this<Instance> {
// Note: enable_shared_from_this HAS to be public or else its field will not be populated
// Maybe this could be replaced with a friendship? But that seems unnecessary.
// https://stackoverflow.com/q/56415222/16255372
class Instance : public std::enable_shared_from_this<Instance> {
private:
std::optional<std::weak_ptr<Instance>> parent;
std::vector<std::shared_ptr<Instance>> children;
@ -30,7 +34,7 @@ public:
inline const std::vector<std::shared_ptr<Instance>> GetChildren() { return children; }
// Utility functions
inline void AddChild(std::shared_ptr<Instance> object) { children.push_back(object); }
inline void AddChild(std::shared_ptr<Instance> object) { object->SetParent(this->shared_from_this()); }
};
typedef std::shared_ptr<Instance> InstanceRef;

View file

@ -5,6 +5,7 @@ static InstanceType TYPE_ {
.super = Instance::TYPE,
.className = "Part",
.constructor = &Part::CreateGeneric,
.explorerIcon = "part",
};
InstanceType* Part::TYPE = &TYPE_;