diff options
author | Mark Janes <mark.a.janes@intel.com> | 2017-11-26 07:26:56 -0800 |
---|---|---|
committer | Mark Janes <mark.a.janes@intel.com> | 2017-11-27 14:41:46 -0800 |
commit | e62b5987eb8ace7c7a74d3e0badb0215a26f105e (patch) | |
tree | d0b8478c73a0e78434654680bc468d395e04d40e /retrace | |
parent | 63c528de8b0bdbc8322c07edf4e5c5f3ecd8fb0f (diff) |
State: Refactor state display
Initial implementation used ad-hoc hints to correctly display various
types of data. With this commit, a generic implementation
incorporates the "indices" into the data model, so all state can be
displayed as a vector of items. The state table is not re-created on
each selection, which maintains the state of the triangle widget for
collapsing directories.
Diffstat (limited to 'retrace')
-rw-r--r-- | retrace/daemon/glframe_state_enums.cpp | 12 | ||||
-rw-r--r-- | retrace/daemon/glframe_state_enums.hpp | 1 | ||||
-rw-r--r-- | retrace/daemon/glframe_state_override.cpp | 56 | ||||
-rw-r--r-- | retrace/daemon/ui/glframe_state_model.cpp | 186 | ||||
-rw-r--r-- | retrace/daemon/ui/glframe_state_model.hpp | 40 | ||||
-rw-r--r-- | retrace/daemon/ui/qml/StateControl.qml | 148 |
6 files changed, 168 insertions, 275 deletions
diff --git a/retrace/daemon/glframe_state_enums.cpp b/retrace/daemon/glframe_state_enums.cpp index f7295062..329291e0 100644 --- a/retrace/daemon/glframe_state_enums.cpp +++ b/retrace/daemon/glframe_state_enums.cpp @@ -207,3 +207,15 @@ glretrace::state_name_to_choices(const std::string &n) { } } +std::vector<std::string> +glretrace::state_name_to_indices(const std::string &n) { + switch (state_name_to_enum(n)) { + case GL_COLOR_WRITEMASK: + return {"Red Enabled", "Green Enabled", "Blue Enabled", "Alpha Enabled"}; + case GL_BLEND_COLOR: + case GL_COLOR_CLEAR_VALUE: + return {"Red", "Green", "Blue", "Alpha"}; + default: + return {}; + } +} diff --git a/retrace/daemon/glframe_state_enums.hpp b/retrace/daemon/glframe_state_enums.hpp index daeee8de..95302187 100644 --- a/retrace/daemon/glframe_state_enums.hpp +++ b/retrace/daemon/glframe_state_enums.hpp @@ -39,6 +39,7 @@ namespace glretrace { uint32_t state_name_to_enum(const std::string &value); std::string state_enum_to_name(GLint value); std::vector<std::string> state_name_to_choices(const std::string &n); +std::vector<std::string> state_name_to_indices(const std::string &n); } // namespace glretrace diff --git a/retrace/daemon/glframe_state_override.cpp b/retrace/daemon/glframe_state_override.cpp index 7459200a..a4a807d4 100644 --- a/retrace/daemon/glframe_state_override.cpp +++ b/retrace/daemon/glframe_state_override.cpp @@ -67,14 +67,9 @@ StateOverride::setState(const StateKey &item, // model. void StateOverride::adjust_item(StateKey *item) const { - switch (state_name_to_enum(item->name)) { - case GL_COLOR_WRITEMASK: { - item->path = ""; - break; - } - default: - break; - } + // initially required by GL_COLOR_WRITEMASK, which can now be + // implemented within the generic pattern. + return; } // As with adjust_item, offsets from the UI do not always match the @@ -83,22 +78,9 @@ StateOverride::adjust_item(StateKey *item) const { void StateOverride::adjust_offset(const StateKey &item, int *offset) const { - switch (state_name_to_enum(item.name)) { - case GL_COLOR_WRITEMASK: { - static const std::map<std::string, int> writemask_offsets { - { "FrameBuffer State/Red Enabled", 0 }, - { "FrameBuffer State/Green Enabled", 1 }, - { "FrameBuffer State/Blue Enabled", 2 }, - { "FrameBuffer State/Alpha Enabled", 3 }, - }; - const auto i = writemask_offsets.find(item.path); - assert(i != writemask_offsets.end()); - *offset = i->second; - return; - } - default: - return; - } + // initially required by GL_COLOR_WRITEMASK, which can now be + // implemented within the generic pattern. + return; } // The UI uses strings for all state values. The override model @@ -479,29 +461,13 @@ StateOverride::onState(SelectionId selId, callback->onState(selId, experimentCount, renderId, k, color); } { - StateKey k("FrameBuffer State/Red Enabled", - "GL_COLOR_WRITEMASK"); + StateKey k("FrameBuffer State", "GL_COLOR_WRITEMASK"); getState(k, &data); callback->onState(selId, experimentCount, renderId, k, - {data[0] ? "true" : "false"}); - { - StateKey k("FrameBuffer State/Green Enabled", - "GL_COLOR_WRITEMASK"); - callback->onState(selId, experimentCount, renderId, k, - {data[1] ? "true" : "false"}); - } - { - StateKey k("FrameBuffer State/Blue Enabled", - "GL_COLOR_WRITEMASK"); - callback->onState(selId, experimentCount, renderId, k, - {data[2] ? "true" : "false"}); - } - { - StateKey k("FrameBuffer State/Alpha Enabled", - "GL_COLOR_WRITEMASK"); - callback->onState(selId, experimentCount, renderId, k, - {data[3] ? "true" : "false"}); - } + {data[0] ? "true" : "false", + data[1] ? "true" : "false", + data[2] ? "true" : "false", + data[3] ? "true" : "false"}); } { StateKey k("Depth State", "GL_DEPTH_CLEAR_VALUE"); diff --git a/retrace/daemon/ui/glframe_state_model.cpp b/retrace/daemon/ui/glframe_state_model.cpp index cdc7d496..c028c908 100644 --- a/retrace/daemon/ui/glframe_state_model.cpp +++ b/retrace/daemon/ui/glframe_state_model.cpp @@ -51,18 +51,19 @@ QStateValue::QStateValue(QObject *parent) { QStateValue::QStateValue(QObject *parent, const std::string &_path, const std::string &_name, - const std::vector<std::string> &_choices) + const std::vector<std::string> &_choices, + const std::vector<std::string> &_indices) : m_path(_path.c_str()), m_name(_name.c_str()), - m_value(kUninitializedValue), - m_visible(true), - m_type(QStateValue::KglDirectory) { + m_value({}), + m_visible(true) { moveToThread(parent->thread()); - if (!_choices.empty()) - m_type = QStateValue::KglEnum; for (auto c : _choices) - m_choices.append(QVariant(c.c_str())); + m_choices.append(QString(c.c_str())); + for (auto i : _indices) + m_indices.append(QString(i.c_str())); + const int path_count = std::count(_path.begin(), _path.end(), '/'); const int indent = path_count + (_name.length() > 0 ? 1 : 0); m_indent = indent; @@ -71,75 +72,44 @@ QStateValue::QStateValue(QObject *parent, } void -QStateValue::insert(const std::string &value) { - int value_index = 0; - QVariant qvalue(value.c_str()); - if (m_type == QStateValue::KglEnum) { - for (auto c : m_choices) { - if (qvalue == c) - break; - ++value_index; - } - // value must be found - assert(value_index < m_choices.size()); - if (m_value == kUninitializedValue) { - // first render, display the value - m_value = value_index; - return; - } - - if (m_value != value_index) - // selected renders have different values - m_value = kMixedValue; - - return; +QStateValue::insert(const std::vector<std::string> &value) { + QStringList new_value; + for (auto i : value) { + new_value.append(m_choices.length() ? + value_to_choice(i) : + QString::fromStdString(i)); } - // else - m_type = QStateValue::KglFloat; - const QString new_val = QString::fromStdString(value); - if (m_value == kUninitializedValue) { - m_value = new_val; + if (m_value.length() == 0) { + m_value = new_value; + emit valueChanged(); return; } - if (m_value != new_val) - m_value = "###"; -} -void -QStateValue::insert(const std::string &red, - const std::string &blue, - const std::string &green, - const std::string &alpha) { - m_type = QStateValue::KglColor; - QStringList color; - color.append(QString::fromStdString(red)); - color.append(QString::fromStdString(blue)); - color.append(QString::fromStdString(green)); - color.append(QString::fromStdString(alpha)); - - if (m_value == kUninitializedValue) { - // selected renders have different values - m_value = color; + // else we are appending values from a multiple-render selection to + // an existing value. + if (m_value == new_value) return; - } - if (m_value != color) { - // selected renders have different values - QStringList tmp = m_value.value<QStringList>(); - for (int i = 0; i < 4; ++i) { - if (tmp[i] != color[i]) - tmp[i] = "###"; - } - m_value = tmp; + QStringList tmp = m_value; + for (int i = 0; i < tmp.size(); ++i) { + if (tmp[i] != new_value[i]) + tmp[i] = "###"; } + m_value = tmp; + emit valueChanged(); } QStateModel::QStateModel() {} QStateModel::QStateModel(IFrameRetrace *retrace) : m_retrace(retrace) {} -QStateModel::~QStateModel() {} +QStateModel::~QStateModel() { + for (auto i : m_states) + delete i; + m_states.clear(); + m_state_by_name.clear(); +} QQmlListProperty<QStateValue> QStateModel::state() { ScopedLock s(m_protect); @@ -148,27 +118,10 @@ QQmlListProperty<QStateValue> QStateModel::state() { void QStateModel::clear() { - // QObjects being displayed in the UI must be cleared from the UI - // before being deleted. This routine is invoked in the UI thread - // (as opposed to the retrace thread). The emit with the empty - // states calls down into ::state() to retrieve the new empty list - // within a single thread. After emit, it is safe to delete the - // objects. Conversely, if these objects were cleaned up after an - // emit in the retrace thread, the request for updated state would - // be enqueued. The result is an asynchronous crash. - { - ScopedLock s(m_protect); - m_states.clear(); - } - emit stateChanged(); { ScopedLock s(m_protect); - m_state_by_name.clear(); - m_renders.clear(); - m_known_paths.clear(); - for (auto i : m_for_deletion) - delete i; - m_for_deletion.clear(); + for (auto i : m_states) + i->clear(); } } @@ -178,13 +131,16 @@ void QStateModel::onState(SelectionId selectionCount, StateKey item, const std::vector<std::string> &value) { if (selectionCount == SelectionId(SelectionId::INVALID_SELECTION)) { - refresh(); return; } if ((selectionCount > m_sel_count) || - (experimentCount > m_experiment_count)) + (experimentCount > m_experiment_count)) { + // state values for a new selection/experiment. Discard existing + // values. clear(); + m_renders.clear(); + } ScopedLock s(m_protect); if ((selectionCount > m_sel_count) || @@ -197,6 +153,16 @@ void QStateModel::onState(SelectionId selectionCount, } if (m_renders.empty() || renderId != m_renders.back()) m_renders.push_back(renderId); + + auto state_value = m_state_by_name.find(item); + if (state_value != m_state_by_name.end()) { + // update existing value + state_value->second->insert(value); + return; + } + + // else this is a state value that has not been seen before. Create + // QStateValue objects for the directory tree and the value itself. std::string path_comp = item.path; while (path_comp.length() > 0) { auto known = m_known_paths.find(path_comp); @@ -205,7 +171,7 @@ void QStateModel::onState(SelectionId selectionCount, QStateValue *i = new QStateValue(this, path_comp, "", - {}); + {}, {}); StateKey k(path_comp, ""); m_state_by_name[k] = i; m_known_paths[path_comp] = true; @@ -216,26 +182,21 @@ void QStateModel::onState(SelectionId selectionCount, } auto &name = item.name; - auto state_value = m_state_by_name.find(item); - if (state_value == m_state_by_name.end()) { - QStateValue *i = new QStateValue(this, - item.path, - name, - state_name_to_choices(name)); - m_state_by_name[item] = i; - state_value = m_state_by_name.find(item); - m_for_deletion.push_back(i); - } - if (value.size() == 1) { - state_value->second->insert(value[0]); - return; - } - if (value.size() == 4) { - // color value - state_value->second->insert(value[0], value[1], value[2], value[3]); - return; - } - assert(false); + QStateValue *i = new QStateValue(this, + item.path, + name, + state_name_to_choices(name), + state_name_to_indices(name)); + m_state_by_name[item] = i; + state_value = m_state_by_name.find(item); + i->insert(value); + + // the list of QStateValue objects has changed, resort and redraw + // the ListView displaying them. + m_states.clear(); + for (auto i : m_state_by_name) + m_states.push_back(i.second); + emit stateChanged(); } void @@ -255,8 +216,7 @@ QStateModel::setState(const QString &path, sel.series.push_back(RenderSequence(*r, RenderId(r->index() + 1))); ++r; } - - StateKey key(path.toStdString(), name.toStdString()); + const StateKey key(path.toStdString(), name.toStdString()); m_retrace->setState(sel, key, offset, value.toStdString()); emit stateExperiment(); } @@ -349,3 +309,17 @@ QStateModel::search(const QString &_search) { m_search = _search; set_visible(); } + +QString +QStateValue::value_to_choice(const std::string &_value) { + assert(m_choices.length() > 0); + int c = 0; + const QString v(_value.c_str()); + for (const auto &i : m_choices) { + if (i == v) + return QString().setNum(c); + ++c; + } + assert(false); + return QString(); +} diff --git a/retrace/daemon/ui/glframe_state_model.hpp b/retrace/daemon/ui/glframe_state_model.hpp index deb3a7b9..56819b2b 100644 --- a/retrace/daemon/ui/glframe_state_model.hpp +++ b/retrace/daemon/ui/glframe_state_model.hpp @@ -49,50 +49,46 @@ class QStateValue : public QObject, NoCopy, NoAssign, NoMove { Q_PROPERTY(QString path READ path CONSTANT) Q_PROPERTY(QString name READ name CONSTANT) - Q_PROPERTY(QVariant value READ value CONSTANT) + Q_PROPERTY(QStringList value READ value NOTIFY valueChanged) Q_PROPERTY(QVariant indent READ indent CONSTANT) - Q_PROPERTY(QStateType valueType READ valueType CONSTANT) Q_PROPERTY(QVariant visible READ visible WRITE setVisible NOTIFY visibleChanged) - Q_PROPERTY(QList<QVariant> choices READ choices CONSTANT) + Q_PROPERTY(QStringList choices READ choices CONSTANT) + Q_PROPERTY(QStringList indices READ indices CONSTANT) public: - enum QStateType { - KglDirectory, - KglEnum, - KglFloat, - KglColor - }; - Q_ENUMS(QStateType); - explicit QStateValue(QObject *parent = 0); QStateValue(QObject *parent, const std::string &_path, const std::string &_name, - const std::vector<std::string> &_choices); - void insert(const std::string &value); - void insert(const std::string &red, const std::string &blue, - const std::string &green, const std::string &alpha); + const std::vector<std::string> &_choices, + const std::vector<std::string> &_indices); + void insert(const std::vector<std::string> &value); QString path() const { return m_path; } QString name() const { return m_name; } - QStateType valueType() const { return m_type; } - QVariant value() const { return m_value; } + QStringList 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; } + QStringList choices() const { return m_choices; } + QStringList indices() const { return m_indices; } + void clear() { m_value.clear(); emit valueChanged(); } signals: void visibleChanged(); + void valueChanged(); private: + QString value_to_choice(const std::string &_value); + QString m_path, m_name; - QVariant m_value, m_indent, m_visible; - QStateType m_type; - QList<QVariant> m_choices; + QStringList m_value; + QVariant m_indent, m_visible; + QStringList m_choices; + QStringList m_indices; }; class QStateModel : public QObject, @@ -130,12 +126,10 @@ class QStateModel : public QObject, IFrameRetrace *m_retrace; SelectionId m_sel_count; ExperimentId m_experiment_count; - // typedef QList<QStateValue*> StateList; 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; QString m_search; mutable std::mutex m_protect; diff --git a/retrace/daemon/ui/qml/StateControl.qml b/retrace/daemon/ui/qml/StateControl.qml index a1a94b02..101cbc22 100644 --- a/retrace/daemon/ui/qml/StateControl.qml +++ b/retrace/daemon/ui/qml/StateControl.qml @@ -54,9 +54,10 @@ Item { model: stateModel.state anchors.fill: parent delegate: Component { + id: currentDelegate Row { visible: modelData.visible - height: modelData.visible ? combo.height : 0 + height: modelData.visible ? choices.height : 0 spacing: 10 Rectangle { id: indent @@ -70,7 +71,7 @@ Item { width: nameText.height * 0.75 height: nameText.height * 0.75 source: "qrc:///qml/images/if_next_right_82215.png" - visible: (modelData.valueType == QStateValue.KglDirectory) + visible: (modelData.value.length == 0) property var collapsed: false transform: Rotation { origin.x: collapse.width / 2 @@ -96,113 +97,58 @@ Item { anchors.verticalCenter: parent.verticalCenter text: modelData.name + " : " } - TextInput { - anchors.margins: 3 - anchors.verticalCenter: parent.verticalCenter - visible: (modelData.valueType == QStateValue.KglFloat) - validator: DoubleValidator{} - text: (modelData.valueType == QStateValue.KglFloat) ? modelData.value : "" - Keys.onReturnPressed: { - if (!acceptableInput) { - text = modelData.value; - return; - } - stateModel.setState(modelData.path, - modelData.name, - 0, - text); - } - } Row { - anchors.verticalCenter: parent.verticalCenter + id: dataRow spacing: 10 - visible: (modelData.valueType == QStateValue.KglColor) - Text{ - text: "Red: " - } - TextInput { - anchors.margins: 3 - validator: DoubleValidator{} - text: (modelData.valueType == QStateValue.KglColor) ? modelData.value[0] : "" - Keys.onReturnPressed: { - if (!acceptableInput) { - text = modelData.value[0]; - return; - } - stateModel.setState(modelData.path, - modelData.name, - 0, - text); - } - } - Text{ - text: "Blue: " - } - TextInput { - anchors.margins: 3 - validator: DoubleValidator{} - text: (modelData.valueType == QStateValue.KglColor) ? modelData.value[1] : "" - Keys.onReturnPressed: { - if (!acceptableInput) { - text = modelData.value[1]; - return; + property var rowModel: currentDelegate.modelData + Repeater { + model: modelData.value.length; + property var indices: modelData.indices + property var value: modelData.value + property var path: modelData.path + property var name: modelData.name + property var choices: modelData.choices + Row { + property var offset: index + spacing: 10 + Text { + anchors.verticalCenter: parent.verticalCenter + visible: indices.length > 0 + text: visible ? indices[offset] : "" } - stateModel.setState(modelData.path, - modelData.name, - 1, - text); - } - } - Text{ - text: "Green: " - } - TextInput { - anchors.margins: 3 - validator: DoubleValidator{} - text: (modelData.valueType == QStateValue.KglColor) ? modelData.value[2] : "" - Keys.onReturnPressed: { - if (!acceptableInput) { - text = modelData.value[2]; - return; + TextInput { + anchors.margins: 3 + anchors.verticalCenter: parent.verticalCenter + visible: choices.length == 0 + validator: DoubleValidator{} + text: value[offset] + Keys.onReturnPressed: { + if (!acceptableInput) { + text = modelData.value[0]; + return; + } + stateModel.setState(path, + name, + offset, + text); + } } - stateModel.setState(modelData.path, - modelData.name, - 2, - text); - } - } - Text{ - text: "Alpha: " - } - TextInput { - validator: DoubleValidator{} - anchors.margins: 3 - text: (modelData.valueType == QStateValue.KglColor) ? modelData.value[3] : "" - Keys.onReturnPressed: { - if (!acceptableInput) { - text = modelData.value[3]; - return; + ComboBoxFitContents { + anchors.verticalCenter: parent.verticalCenter + model: choices + currentIndex: parseInt(value[offset]) + visible: choices.length > 0 + onActivated: { + stateModel.setState(path, + name, + offset, + choices[currentIndex]); + } } - stateModel.setState(modelData.path, - modelData.name, - 3, - text); + } } } - ComboBoxFitContents { - id: combo - anchors.verticalCenter: parent.verticalCenter - model: modelData.choices - currentIndex: (modelData.valueType == QStateValue.KglEnum ? modelData.value : 0) - visible: (modelData.valueType == QStateValue.KglEnum) - onActivated: { - stateModel.setState(modelData.path, - modelData.name, - 0, - modelData.choices[currentIndex]); - } - } } } } |