summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Janes <mark.a.janes@intel.com>2017-10-20 19:53:48 -0700
committerMark Janes <mark.a.janes@intel.com>2017-11-27 11:22:47 -0800
commit3c5e651b782df6429f7b128f1d8157351af3ca80 (patch)
tree8eedd7e780779ede9dbcc37872dbb661f70069c0
parent710f41356207a94ec178444039a870ff908e9417 (diff)
State: Display a tree of collapsible state directories
This implementation of a custom tree view is similar to Qt's native implementation: a modified ListView that hides items based on whether a parent directory is collapsed.
-rw-r--r--retrace/daemon/glframe_retrace_interface.hpp8
-rw-r--r--retrace/daemon/glframe_retrace_render.cpp5
-rw-r--r--retrace/daemon/ui/glframe_state_model.cpp141
-rw-r--r--retrace/daemon/ui/glframe_state_model.hpp38
-rw-r--r--retrace/daemon/ui/qml/StateControl.qml37
5 files changed, 198 insertions, 31 deletions
diff --git a/retrace/daemon/glframe_retrace_interface.hpp b/retrace/daemon/glframe_retrace_interface.hpp
index 61fba458..5b1fba45 100644
--- a/retrace/daemon/glframe_retrace_interface.hpp
+++ b/retrace/daemon/glframe_retrace_interface.hpp
@@ -270,14 +270,14 @@ struct StateKey {
const std::string _name)
: group(_group), path(_path), name(_name) {}
bool operator<(const StateKey &o) const {
- if (name < o.name)
- return true;
- if (name > o.name)
- return false;
if (path < o.path)
return true;
if (path > o.path)
return false;
+ if (name < o.name)
+ return true;
+ if (name > o.name)
+ return false;
return group < o.group;
}
};
diff --git a/retrace/daemon/glframe_retrace_render.cpp b/retrace/daemon/glframe_retrace_render.cpp
index dcb73284..a0ebfff1 100644
--- a/retrace/daemon/glframe_retrace_render.cpp
+++ b/retrace/daemon/glframe_retrace_render.cpp
@@ -581,7 +581,7 @@ RetraceRender::onState(SelectionId selId,
GLenum e = GL::GetError();
if (e == GL_NO_ERROR) {
callback->onState(selId, experimentCount, renderId,
- StateKey("", "", "CULL_FACE"),
+ StateKey("Rendering", "Cull State", "CULL_FACE"),
cull_enabled ? "true" : "false");
}
}
@@ -594,7 +594,8 @@ RetraceRender::onState(SelectionId selId,
const std::string cull_str = value_to_string(cull);
if (cull_str.size() > 0) {
callback->onState(selId, experimentCount, renderId,
- StateKey("", "", "CULL_FACE_MODE"), cull_str);
+ StateKey("Rendering", "Cull State",
+ "CULL_FACE_MODE"), cull_str);
}
}
}
diff --git a/retrace/daemon/ui/glframe_state_model.cpp b/retrace/daemon/ui/glframe_state_model.cpp
index da0063e7..741643f1 100644
--- a/retrace/daemon/ui/glframe_state_model.cpp
+++ b/retrace/daemon/ui/glframe_state_model.cpp
@@ -28,6 +28,7 @@
#include "glframe_state_model.hpp"
#include <GL/gl.h>
+#include <algorithm>
#include <string>
#include <vector>
@@ -41,12 +42,27 @@ using glretrace::state_name_to_enum;
static const int kUninitializedValue = -2;
static const int kMixedValue = -1;
-QStateValue::QStateValue(const std::string &_name,
+QStateValue::QStateValue(QObject *parent) {
+ if (parent)
+ moveToThread(parent->thread());
+}
+
+QStateValue::QStateValue(QObject *parent,
+ const std::string &_group,
+ const std::string &_path,
+ const std::string &_name,
const std::vector<std::string> &_choices)
- : m_name(_name.c_str()),
- m_value(kUninitializedValue) {
+ : m_group(_group.c_str()),
+ m_path(_path.c_str()),
+ m_name(_name.c_str()),
+ m_value(kUninitializedValue),
+ m_visible(true) {
+ moveToThread(parent->thread());
for (auto c : _choices)
m_choices.append(QVariant(c.c_str()));
+ m_indent = static_cast<int>(std::count(_path.begin(), _path.end(), '/'));
+ if (_name.length() == 0)
+ m_name = _path.substr(_path.find_last_of("/") + 1).c_str();
}
void
@@ -113,10 +129,12 @@ QStateModel::clear() {
emit stateChanged();
{
ScopedLock s(m_protect);
- for (auto i : m_state_by_name)
- delete i.second;
m_state_by_name.clear();
m_renders.clear();
+ m_known_paths.clear();
+ for (auto i : m_for_deletion)
+ delete i;
+ m_for_deletion.clear();
}
}
@@ -126,13 +144,7 @@ void QStateModel::onState(SelectionId selectionCount,
StateKey item,
const std::string &value) {
if (selectionCount == SelectionId(SelectionId::INVALID_SELECTION)) {
- {
- ScopedLock s(m_protect);
- m_states.clear();
- for (auto i : m_state_by_name)
- m_states.push_back(i.second);
- }
- emit stateChanged();
+ refresh();
return;
}
@@ -151,19 +163,44 @@ void QStateModel::onState(SelectionId selectionCount,
}
if (m_renders.empty() || renderId != m_renders.back())
m_renders.push_back(renderId);
+ std::string path_comp = item.path;
+ while (path_comp.length() > 0) {
+ auto known = m_known_paths.find(path_comp);
+ if (known == m_known_paths.end()) {
+ // create an empty item to serve as the directory
+ QStateValue *i = new QStateValue(this,
+ item.group,
+ path_comp,
+ "",
+ std::vector<std::string>());
+ StateKey k(item.group, path_comp, "");
+ m_state_by_name[k] = i;
+ m_known_paths[item.path] = true;
+ } else {
+ break;
+ }
+ path_comp = path_comp.substr(0, path_comp.find_last_of("/"));
+ }
+
auto &name = item.name;
- auto state_value = m_state_by_name.find(name);
+ auto state_value = m_state_by_name.find(item);
if (state_value == m_state_by_name.end()) {
- QStateValue *i = new QStateValue(name,
+ QStateValue *i = new QStateValue(this,
+ item.group,
+ item.path,
+ name,
name_to_choices(name));
- m_state_by_name[name] = i;
- state_value = m_state_by_name.find(name);
+ m_state_by_name[item] = i;
+ state_value = m_state_by_name.find(item);
+ m_for_deletion.push_back(i);
}
state_value->second->insert(value);
}
void
-QStateModel::setState(const QString &name,
+QStateModel::setState(const QString &group,
+ const QString &path,
+ const QString &name,
const QString &value) {
RenderSelection sel;
sel.id = m_sel_count;
@@ -178,8 +215,76 @@ QStateModel::setState(const QString &name,
++r;
}
- StateKey key("", "", name.toStdString());
+ StateKey key(group.toStdString(), path.toStdString(), name.toStdString());
m_retrace->setState(sel, key, value.toStdString());
emit stateExperiment();
}
+void
+QStateModel::collapse(const QString &path) {
+ const std::string path_str = path.toStdString();
+ m_filter_paths[path_str] = true;
+ for (auto i : m_state_by_name) {
+ if (!i.second->visible().toBool())
+ continue;
+ if (strncmp(path_str.c_str(), i.first.path.c_str(),
+ path_str.length()) == 0) {
+ // do not filter the collapsed directories themselves
+ if ((i.first.path == path_str) && (i.first.name.length() == 0))
+ continue;
+ i.second->setVisible(false);
+ }
+ }
+}
+
+void
+QStateModel::expand(const QString &path) {
+ const std::string path_str = path.toStdString();
+ auto i = m_filter_paths.find(path_str);
+ assert(i != m_filter_paths.end());
+ m_filter_paths.erase(i);
+ for (auto i : m_state_by_name) {
+ if (i.second->visible().toBool())
+ continue;
+ if (strncmp(path_str.c_str(), i.first.path.c_str(),
+ path_str.length()) == 0) {
+ // possibly expanded
+ bool visible = true;
+ for (auto f : m_filter_paths) {
+ if (strncmp(f.first.c_str(), i.first.path.c_str(),
+ f.first.length()) == 0) {
+ // do not filter the collapsed directories themselves
+ if ((i.first.path == f.first) && (i.first.name.length() == 0))
+ visible = true;
+ else
+ visible = false;
+ }
+ }
+ i.second->setVisible(visible);
+ }
+ }
+}
+
+void
+QStateModel::refresh() {
+ {
+ ScopedLock s(m_protect);
+ m_states.clear();
+ for (auto i : m_state_by_name) {
+ bool visible = true;
+ for (auto f : m_filter_paths) {
+ if (strncmp(f.first.c_str(), i.first.path.c_str(),
+ f.first.length()) == 0) {
+ // do not filter the collapsed directories themselves
+ if ((i.first.path == f.first) && (i.first.name.length() == 0))
+ visible = true;
+ else
+ visible = false;
+ }
+ }
+ i.second->setVisible(visible);
+ m_states.push_back(i.second);
+ }
+ }
+ emit stateChanged();
+}
diff --git a/retrace/daemon/ui/glframe_state_model.hpp b/retrace/daemon/ui/glframe_state_model.hpp
index 6c764382..a08fd440 100644
--- a/retrace/daemon/ui/glframe_state_model.hpp
+++ b/retrace/daemon/ui/glframe_state_model.hpp
@@ -47,23 +47,41 @@ namespace glretrace {
class QStateValue : public QObject, NoCopy, NoAssign, NoMove {
Q_OBJECT
+ Q_PROPERTY(QString group READ group CONSTANT)
+ Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QVariant value READ value CONSTANT)
+ Q_PROPERTY(QVariant indent READ indent CONSTANT)
+ Q_PROPERTY(QVariant visible
+ READ visible
+ WRITE setVisible
+ NOTIFY visibleChanged)
Q_PROPERTY(QList<QVariant> choices READ choices CONSTANT)
public:
- QStateValue() {}
- QStateValue(const std::string &_name,
+ explicit QStateValue(QObject *parent = 0);
+ QStateValue(QObject *parent,
+ const std::string &_group,
+ const std::string &_path,
+ const std::string &_name,
const std::vector<std::string> &_choices);
void insert(const std::string &value);
+ QString group() const { return m_group; }
+ QString path() const { return m_path; }
QString name() const { return m_name; }
QVariant value() const { return m_value; }
+ QVariant indent() const { return m_indent; }
+ QVariant visible() const { return m_visible; }
+ void setVisible(QVariant v) { m_visible = v; emit visibleChanged(); }
QList<QVariant> choices() const { return m_choices; }
+ signals:
+ void visibleChanged();
+
private:
- QString m_name;
- QVariant m_value;
+ QString m_group, m_path, m_name;
+ QVariant m_value, m_indent, m_visible;
QList<QVariant> m_choices;
};
@@ -83,19 +101,27 @@ class QStateModel : public QObject,
StateKey item,
const std::string &value);
void clear();
- Q_INVOKABLE void setState(const QString &name,
+ Q_INVOKABLE void setState(const QString &group,
+ const QString &path,
+ const QString &name,
const QString &value);
+ Q_INVOKABLE void collapse(const QString &path);
+ Q_INVOKABLE void expand(const QString &path);
signals:
void stateExperiment();
void stateChanged();
private:
+ void refresh();
+
IFrameRetrace *m_retrace;
SelectionId m_sel_count;
ExperimentId m_experiment_count;
// typedef QList<QStateValue*> StateList;
- std::map<std::string, QStateValue*> m_state_by_name;
+ std::map<StateKey, QStateValue*> m_state_by_name;
+ std::map<std::string, bool> m_filter_paths;
+ std::map<std::string, bool> m_known_paths;
QList<QStateValue*> m_states;
std::vector<QStateValue*> m_for_deletion;
std::vector<RenderId> m_renders;
diff --git a/retrace/daemon/ui/qml/StateControl.qml b/retrace/daemon/ui/qml/StateControl.qml
index 2eda8a85..c0673b29 100644
--- a/retrace/daemon/ui/qml/StateControl.qml
+++ b/retrace/daemon/ui/qml/StateControl.qml
@@ -14,17 +14,52 @@ Item {
anchors.fill: parent
delegate: Component {
Row {
+ visible: modelData.visible
+ height: modelData.visible ? combo.height : 0
+ Rectangle {
+ id: indent
+ width: nameText.height * modelData.indent
+ height: 1
+ opacity: 0.0
+ }
+ Rectangle {
+ id: collapse
+ anchors.bottom: nameText.bottom
+ width: nameText.height
+ height: nameText.height
+ visible: modelData.choices.length == 0
+ property var collapsed: false
+ color: collapse.collapsed ? "red" : "green"
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (collapse.collapsed) {
+ collapse.color = "green";
+ stateModel.expand(modelData.path);
+ collapse.collapsed = false
+ } else {
+ collapse.color = "red";
+ stateModel.collapse(modelData.path);
+ collapse.collapsed = true
+ }
+ }
+ }
+ }
Text {
id: nameText
anchors.verticalCenter: parent.verticalCenter
text: modelData.name + " : "
}
ComboBoxFitContents {
+ id: combo
anchors.verticalCenter: parent.verticalCenter
model: modelData.choices
currentIndex: modelData.value
+ visible: (modelData.choices.length > 0)
onActivated: {
- stateModel.setState(modelData.name,
+ stateModel.setState(modelData.group,
+ modelData.path,
+ modelData.name,
modelData.choices[currentIndex]);
}
}