You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1907 lines
62 KiB
C++
1907 lines
62 KiB
C++
/**
|
|
* wWidgets - Lightweight UI Toolkit.
|
|
* Copyright (C) 2009-2011 Evgeny Andreeshchev <eugene.andreeshchev@gmail.com>
|
|
* Alexander Kotliar <alexander.kotliar@gmail.com>
|
|
*
|
|
* This code is distributed under the MIT License:
|
|
* http://www.opensource.org/licenses/MIT
|
|
*/
|
|
|
|
// Modifications copyright Amazon.com, Inc. or its affiliates.
|
|
|
|
#include "EditorCommon_precompiled.h"
|
|
#include "QPropertyTree.h"
|
|
#include "QPropertyTreeStyle.h"
|
|
#include "PropertyTreeModel.h"
|
|
#include "PropertyRowContainer.h"
|
|
#include "PropertyDrawContext.h"
|
|
#include "Unicode.h"
|
|
#include "Serialization.h"
|
|
#include "Serialization/BinArchive.h"
|
|
#include "Serialization/Callback.h"
|
|
#include "Serialization/Decorators/IconXPM.h"
|
|
|
|
#include <QMenu>
|
|
#include <QKeyEvent>
|
|
#include <QPainter>
|
|
#include <QObject>
|
|
#include <QStyleOption>
|
|
#include <QFontMetrics>
|
|
#include "MathUtils.h"
|
|
#include "warning.xpm"
|
|
#include "error.xpm"
|
|
|
|
#if 0
|
|
# define DEBUG_TRACE(fmt, ...) printf(fmt "\n", __VA_ARGS__)
|
|
# define DEBUG_TRACE_ROW(fmt, ...) for(PropertyRow* zzzz = this; zzzz; zzzz = zzzz->parent()) printf(" "); printf(fmt "\n", __VA_ARGS__)
|
|
#else
|
|
# define DEBUG_TRACE(...)
|
|
# define DEBUG_TRACE_ROW(...)
|
|
#endif
|
|
|
|
enum { TEXT_VALUE_SPACING = 3 };
|
|
|
|
QColor interpolateColor(const QColor& a, const QColor& b, float k)
|
|
{
|
|
float mk = 1.0f - k;
|
|
return QColor(aznumeric_cast<int>(a.red() * mk + b.red() * k),
|
|
aznumeric_cast<int>(a.green() * mk + b.green() * k),
|
|
aznumeric_cast<int>(a.blue() * mk + b.blue() * k),
|
|
aznumeric_cast<int>(a.alpha() * mk + b.alpha() * k));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
template<class T>
|
|
static void visitPulledRows(PropertyRow* row, T& drawFunc)
|
|
{
|
|
int count = (int)row->count();
|
|
for (int i = 0; i < count; ++i) {
|
|
PropertyRow* child = row->childByIndex(i);
|
|
if (child->pulledUp() || child->pulledBefore()) {
|
|
drawFunc(child);
|
|
visitPulledRows(child, drawFunc);
|
|
}
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
ConstStringList* PropertyRow::constStrings_ = 0;
|
|
|
|
PropertyRow::PropertyRow()
|
|
{
|
|
parent_ = 0;
|
|
callback_ = 0;
|
|
|
|
expanded_ = false;
|
|
selected_ = false;
|
|
visible_ = true;
|
|
labelUndecorated_ = 0;
|
|
belongsToFilteredRow_ = false;
|
|
matchFilter_ = true;
|
|
|
|
pos_ = QPoint(0, 0);
|
|
size_ = QPoint(-1, -1);
|
|
plusSize_ = 0;
|
|
textPos_ = 0;
|
|
textSizeInitial_ = 0;
|
|
textHash_ = 0;
|
|
textSize_ = 0;
|
|
widgetPos_ = 0;
|
|
widgetSize_ = 0;
|
|
userWidgetSize_ = -1;
|
|
heightIncludingChildren_ = 0;
|
|
|
|
name_ = "";
|
|
typeName_ = "";
|
|
|
|
pulledUp_ = false;
|
|
pulledBefore_ = false;
|
|
packedAfterPreviousRow_ = false;
|
|
hasPulled_ = false;
|
|
userReadOnly_ = false;
|
|
userReadOnlyRecurse_ = false;
|
|
userFullRow_ = false;
|
|
userPackCheckboxes_ = false;
|
|
userWidgetToContent_ = false;
|
|
multiValue_ = false;
|
|
fontWeight_ = FontWeight::Undefined;
|
|
userNonCopyable_ = false;
|
|
|
|
label_ = "";
|
|
labelChanged_ = true;
|
|
layoutChanged_ = true;
|
|
hideChildren_ = false;
|
|
validatorHasErrors_ = false;
|
|
validatorHasWarnings_ = false;
|
|
|
|
tooltip_ = "";
|
|
}
|
|
|
|
PropertyRow::~PropertyRow()
|
|
{
|
|
size_t count = children_.size();
|
|
for (size_t i = 0; i < count; ++i)
|
|
if (children_[i]->parent() == this)
|
|
children_[i]->setParent(0);
|
|
if (callback_)
|
|
callback_->Release();
|
|
callback_ = 0;
|
|
}
|
|
|
|
void PropertyRow::setNames(const char* name, const char* label, const char* typeName)
|
|
{
|
|
name_ = name;
|
|
label_ = label ? label : "";
|
|
typeName_ = typeName;
|
|
}
|
|
|
|
PropertyRow* PropertyRow::childByIndex(int index)
|
|
{
|
|
if(index >= 0 && index < int(children_.size()))
|
|
return children_[index];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
const PropertyRow* PropertyRow::childByIndex(int index) const
|
|
{
|
|
if(index >= 0 && index < int(children_.size()))
|
|
return children_[index];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void PropertyRow::_setExpanded(bool expanded)
|
|
{
|
|
expanded_ = expanded;
|
|
int numChildren = (int)children_.size();
|
|
|
|
for (int i = 0; i < numChildren; ++i) {
|
|
PropertyRow* child = children_[i];
|
|
if(child->pulledUp())
|
|
child->_setExpanded(expanded);
|
|
}
|
|
|
|
layoutChanged_ = true;
|
|
setLayoutChangedToChildren();
|
|
|
|
}
|
|
|
|
struct SetExpandedOp {
|
|
bool expanded_;
|
|
SetExpandedOp(bool expanded) : expanded_(expanded) {}
|
|
ScanResult operator()(PropertyRow* row, QPropertyTree* tree, [[maybe_unused]] int index)
|
|
{
|
|
if(row->canBeToggled(tree))
|
|
row->_setExpanded(expanded_);
|
|
return SCAN_CHILDREN_SIBLINGS;
|
|
}
|
|
};
|
|
|
|
void PropertyRow::setExpandedRecursive(QPropertyTree* tree, bool expanded)
|
|
{
|
|
if(canBeToggled(tree))
|
|
_setExpanded(expanded);
|
|
|
|
SetExpandedOp op(expanded);
|
|
scanChildren(op, tree);
|
|
}
|
|
|
|
int PropertyRow::childIndex(const PropertyRow* row) const
|
|
{
|
|
YASLI_ASSERT(row);
|
|
Rows::const_iterator it = std::find(children_.begin(), children_.end(), row);
|
|
YASLI_ESCAPE(it != children_.end(), return -1);
|
|
return aznumeric_cast<int>(std::distance(children_.begin(), it));
|
|
}
|
|
|
|
bool PropertyRow::isChildOf(const PropertyRow* row) const
|
|
{
|
|
const PropertyRow* p = parent();
|
|
while(p){
|
|
if(p == row)
|
|
return true;
|
|
p = p->parent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PropertyRow::add(PropertyRow* row)
|
|
{
|
|
children_.push_back(row);
|
|
row->setParent(this);
|
|
}
|
|
|
|
void PropertyRow::addAfter(PropertyRow* row, PropertyRow* after)
|
|
{
|
|
iterator it = std::find(children_.begin(), children_.end(), after);
|
|
if(it != children_.end()){
|
|
++it;
|
|
children_.insert(it, row);
|
|
}
|
|
else{
|
|
children_.push_back(row);
|
|
}
|
|
|
|
row->setParent(this);
|
|
}
|
|
|
|
void PropertyRow::assignRowState(const PropertyRow& row, bool recurse)
|
|
{
|
|
expanded_ = row.expanded_;
|
|
selected_ = row.selected_;
|
|
if(recurse){
|
|
int numChildren = (int)children_.size();
|
|
for (int i = 0; i < numChildren; ++i) {
|
|
PropertyRow* child = children_[i].get();
|
|
YASLI_ESCAPE(child, continue);
|
|
int unusedIndex;
|
|
const PropertyRow* rhsChild = row.findFromIndex(&unusedIndex, child->name(), child->typeName(), i);
|
|
if(rhsChild)
|
|
child->assignRowState(*rhsChild, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyRow::assignRowProperties(PropertyRow* row)
|
|
{
|
|
YASLI_ESCAPE(row, return);
|
|
parent_ = row->parent_;
|
|
|
|
userReadOnly_ = row->userReadOnly_;
|
|
userReadOnlyRecurse_ = row->userReadOnlyRecurse_;
|
|
userFixedWidget_ = row->userFixedWidget_;
|
|
pulledUp_ = row->pulledUp_;
|
|
pulledBefore_ = row->pulledBefore_;
|
|
size_ = row->size_;
|
|
pos_ = row->pos_;
|
|
plusSize_ = row->plusSize_;
|
|
textPos_ = row->textPos_;
|
|
textSizeInitial_ = row->textSizeInitial_;
|
|
textHash_ = row->textHash_;
|
|
textSize_ = row->textSize_;
|
|
widgetPos_ = row->widgetPos_;
|
|
widgetSize_ = row->widgetSize_;
|
|
userWidgetSize_ = row->userWidgetSize_;
|
|
userWidgetToContent_ = row->userWidgetToContent_;
|
|
callback_ = row->callback_;
|
|
row->callback_ = 0;
|
|
|
|
assignRowState(*row, false);
|
|
}
|
|
|
|
void PropertyRow::replaceAndPreserveState(PropertyRow* oldRow, PropertyRow* newRow, PropertyTreeModel* model)
|
|
{
|
|
Rows::iterator it = std::find(children_.begin(), children_.end(), oldRow);
|
|
YASLI_ASSERT(it != children_.end());
|
|
if(it != children_.end()){
|
|
newRow->assignRowProperties(*it);
|
|
newRow->labelChanged_ = true;
|
|
*it = newRow;
|
|
if (model)
|
|
model->callRowCallback(newRow);
|
|
}
|
|
}
|
|
|
|
void PropertyRow::erase(PropertyRow* row)
|
|
{
|
|
PropertyRow* childToRemove = PropertyRow::findChildFromDescendant(row);
|
|
if(childToRemove)
|
|
{
|
|
childToRemove->setParent(nullptr);
|
|
children_.erase(std::find(children_.begin(), children_.end(), childToRemove));
|
|
}
|
|
}
|
|
|
|
void PropertyRow::swapChildren(PropertyRow* row, PropertyTreeModel* model)
|
|
{
|
|
children_.swap(row->children_);
|
|
iterator it;
|
|
for( it = children_.begin(); it != children_.end(); ++it)
|
|
(**it).setParent(this);
|
|
for( it = row->children_.begin(); it != row->children_.end(); ++it)
|
|
(**it).setParent(row);
|
|
if (model){
|
|
for(it = children_.begin(); it != children_.end(); ++it){
|
|
PropertyRow* child = *it;
|
|
if (PropertyRow* srcChild = row->find(child->name(), child->label(), child->typeName())) {
|
|
child->setCallback(srcChild->callback_);
|
|
srcChild->setCallback(0);
|
|
model->callRowCallback(child);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyRow::addBefore(PropertyRow* row, PropertyRow* before)
|
|
{
|
|
if(before == 0)
|
|
children_.insert(children_.begin(), row);
|
|
else{
|
|
iterator it = std::find(children_.begin(), children_.end(), before);
|
|
if(it != children_.end())
|
|
children_.insert(it, row);
|
|
else
|
|
children_.push_back(row);
|
|
}
|
|
row->setParent(this);
|
|
}
|
|
|
|
wstring PropertyRow::valueAsWString() const
|
|
{
|
|
return toWideChar(valueAsString().c_str());
|
|
}
|
|
|
|
string PropertyRow::valueAsString() const
|
|
{
|
|
return string();
|
|
}
|
|
|
|
SharedPtr<PropertyRow> PropertyRow::clone(ConstStringList* constStrings) const
|
|
{
|
|
PropertyRow::setConstStrings(constStrings);
|
|
Serialization::BinOArchive oa;
|
|
SharedPtr<PropertyRow> self(const_cast<PropertyRow*>(this));
|
|
oa(self, "row", "Row");
|
|
|
|
Serialization::BinIArchive ia;
|
|
ia.open(oa);
|
|
SharedPtr<PropertyRow> clonedRow;
|
|
ia(clonedRow, "row", "Row");
|
|
PropertyRow::setConstStrings(0);
|
|
if (clonedRow)
|
|
clonedRow->setHideChildren(hideChildren_);
|
|
return clonedRow;
|
|
}
|
|
|
|
void PropertyRow::drawStaticText([[maybe_unused]] QPainter& p, [[maybe_unused]] const QRect& widgetRect)
|
|
{
|
|
}
|
|
|
|
void PropertyRow::Serialize(IArchive& ar)
|
|
{
|
|
serializeValue(ar);
|
|
|
|
ar(ConstStringWrapper(constStrings_, name_), "name", "name");
|
|
ar(ConstStringWrapper(constStrings_, label_), "label", "label");
|
|
ar(ConstStringWrapper(constStrings_, typeName_), "type", "type");
|
|
ar(reinterpret_cast<std::vector<SharedPtr<PropertyRow> >&>(children_), "children", "!^children");
|
|
if(ar.IsInput()){
|
|
labelChanged_ = true;
|
|
layoutChanged_ = true;
|
|
PropertyRow::iterator it;
|
|
for(it = begin(); it != end(); ){
|
|
PropertyRow* row = *it;
|
|
if(row){
|
|
row->setParent(this);
|
|
++it;
|
|
}
|
|
else{
|
|
YASLI_ASSERT_STR(false, "Missing property row");
|
|
it = erase(it);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PropertyRow::onActivate(const PropertyActivationEvent& e)
|
|
{
|
|
if (e.reason != e.REASON_RELEASE)
|
|
return e.tree->spawnWidget(this, e.force);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void PropertyRow::setLabelChanged()
|
|
{
|
|
for(PropertyRow* row = this; row != 0; row = row->parent())
|
|
row->labelChanged_ = true;
|
|
}
|
|
|
|
void PropertyRow::setLayoutChanged()
|
|
{
|
|
layoutChanged_ = true;
|
|
}
|
|
|
|
void PropertyRow::setLabelChangedToChildren()
|
|
{
|
|
size_t numChildren = children_.size();
|
|
for (size_t i = 0; i < numChildren; ++i) {
|
|
children_[i]->labelChanged_ = true;
|
|
children_[i]->setLabelChangedToChildren();
|
|
}
|
|
}
|
|
|
|
void PropertyRow::setLayoutChangedToChildren()
|
|
{
|
|
size_t numChildren = children_.size();
|
|
for (size_t i = 0; i < numChildren; ++i) {
|
|
children_[i]->layoutChanged_ = true;
|
|
children_[i]->setLayoutChangedToChildren();
|
|
}
|
|
}
|
|
|
|
void PropertyRow::setLabel(const char* label)
|
|
{
|
|
if (!label)
|
|
label = "";
|
|
if (label_ != label) {
|
|
label_ = label;
|
|
setLabelChanged();
|
|
}
|
|
}
|
|
|
|
void PropertyRow::propagateFlagsTopToBottom()
|
|
{
|
|
// these flags are reset in parseControlCodes
|
|
if (!userReadOnly_ && !userWidgetToContent_)
|
|
return;
|
|
size_t numChildren = children_.size();
|
|
for (size_t i = 0; i < numChildren; ++i) {
|
|
PropertyRow* r = children_[i];
|
|
if (userReadOnly_)
|
|
r->userReadOnly_ = true;
|
|
if (userWidgetToContent_) {
|
|
r->userWidgetToContent_ = true;
|
|
r->userFixedWidget_ = true;
|
|
}
|
|
r->propagateFlagsTopToBottom();
|
|
}
|
|
}
|
|
|
|
void PropertyRow::setTooltip(const char* tooltip)
|
|
{
|
|
tooltip_ = tooltip;
|
|
}
|
|
|
|
bool PropertyRow::setValidatorEntry(int index, int count)
|
|
{
|
|
if (index != validatorIndex_ || count != validatorCount_) {
|
|
validatorIndex_ = min(index, 0xffff);
|
|
validatorCount_ = min(count, 0xff);
|
|
validatorsHeight_ = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PropertyRow::resetValidatorIcons()
|
|
{
|
|
validatorHasWarnings_ = false;
|
|
validatorHasErrors_ = false;
|
|
}
|
|
|
|
void PropertyRow::addValidatorIcons(bool hasWarnings, bool hasErrors)
|
|
{
|
|
if (hasWarnings )
|
|
validatorHasWarnings_ = true;
|
|
if (hasErrors)
|
|
validatorHasErrors_ = true;
|
|
}
|
|
|
|
void PropertyRow::updateLabel(const QPropertyTree* tree, [[maybe_unused]] int index, bool parentHidesNonInlineChildren)
|
|
{
|
|
if (!labelChanged_) {
|
|
if (pulledUp_)
|
|
parent()->hasPulled_ = true;
|
|
return;
|
|
}
|
|
|
|
hasPulled_ = false;
|
|
|
|
int numChildren = (int)children_.size();
|
|
for (int i = 0; i < numChildren; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
row->updateLabel(tree, i, hideChildren_);
|
|
}
|
|
|
|
parseControlCodes(tree, label_, true);
|
|
bool hiddenByParentFlag = parentHidesNonInlineChildren && !pulledUp_;
|
|
visible_ = (*labelUndecorated_ != '\0' || userFullRow_ || pulledUp_ || isRoot()) && !hiddenByParentFlag;
|
|
|
|
propagateFlagsTopToBottom();
|
|
|
|
if(pulledContainer())
|
|
pulledContainer()->_setExpanded(expanded());
|
|
|
|
layoutChanged_ = true;
|
|
labelChanged_ = false;
|
|
}
|
|
|
|
struct ResetSerializerOp{
|
|
ScanResult operator()(PropertyRow* row)
|
|
{
|
|
row->setSerializer(SStruct());
|
|
return SCAN_CHILDREN_SIBLINGS;
|
|
}
|
|
};
|
|
|
|
void PropertyRow::parseControlCodes(const QPropertyTree* tree, const char* ptr, bool changeLabel)
|
|
{
|
|
if (changeLabel) {
|
|
userFullRow_ = false;
|
|
pulledUp_ = false;
|
|
pulledBefore_ = false;
|
|
userFixedWidget_ = false;
|
|
userPackCheckboxes_ = false;
|
|
userWidgetSize_ = -1;
|
|
userWidgetToContent_ = false;
|
|
fontWeight_ = FontWeight::Undefined;
|
|
userNonCopyable_ = false;
|
|
}
|
|
|
|
while(true){
|
|
if(*ptr == '^'){
|
|
if(parent() && !parent()->isRoot()){
|
|
if(pulledUp_)
|
|
pulledBefore_ = true;
|
|
pulledUp_ = true;
|
|
parent()->hasPulled_ = true;
|
|
|
|
if(pulledUp() && isContainer())
|
|
parent()->setPulledContainer(this);
|
|
}
|
|
}
|
|
else if(*ptr == '='){
|
|
userWidgetToContent_ = true;
|
|
}
|
|
else if(*ptr == '+'){
|
|
bool isFirstUpdate = labelUndecorated_ == 0;
|
|
if (isFirstUpdate)
|
|
_setExpanded(true);
|
|
}
|
|
else if(*ptr == '-'){
|
|
bool isFirstUpdate = labelUndecorated_ == 0;
|
|
if (isFirstUpdate)
|
|
_setExpanded(false);
|
|
}
|
|
else if(*ptr == '<')
|
|
userFullRow_ = true;
|
|
else if(*ptr == '>'){
|
|
userFixedWidget_ = true;
|
|
const char* p = ++ptr;
|
|
while(*p >= '0' && *p <= '9')
|
|
++p;
|
|
if(*p == '>'){
|
|
userWidgetSize_ = atoi(ptr);
|
|
ptr = ++p;
|
|
}
|
|
continue;
|
|
}
|
|
else if(*ptr == '~'){
|
|
ResetSerializerOp op;
|
|
scanChildren(op);
|
|
}
|
|
else if(*ptr == '!'){
|
|
if(userReadOnly_)
|
|
userReadOnlyRecurse_ = true;
|
|
userReadOnly_ = true;
|
|
}
|
|
else if(*ptr == '|'){
|
|
userPackCheckboxes_ = true;
|
|
}
|
|
else if(*ptr == '['){
|
|
++ptr;
|
|
PropertyRow::iterator it;
|
|
for(it = children_.begin(); it != children_.end(); ++it)
|
|
(*it)->parseControlCodes(tree, ptr, false);
|
|
|
|
int counter = 1;
|
|
while(*ptr){
|
|
if(*ptr == ']' && !--counter)
|
|
break;
|
|
else if(*ptr == '[')
|
|
++counter;
|
|
++ptr;
|
|
}
|
|
}
|
|
else if(*ptr == '@'){
|
|
switch (ptr[1])
|
|
{
|
|
case 'b': case 'B':
|
|
fontWeight_ = FontWeight::Bold;
|
|
++ptr;
|
|
break;
|
|
|
|
case 'r': case 'R':
|
|
fontWeight_ = FontWeight::Regular;
|
|
++ptr;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if(*ptr == ':'){
|
|
userNonCopyable_ = true;
|
|
}
|
|
else
|
|
break;
|
|
++ptr;
|
|
}
|
|
|
|
if (isContainer()) {
|
|
// automatically inline children for short arrays
|
|
PropertyRowContainer* container = static_cast<PropertyRowContainer*>(this);
|
|
int numChildren = (int)container->count();
|
|
if (container->isFixedSize() && numChildren > 0 && numChildren <= 4) {
|
|
if (container->childByIndex(0)->inlineInShortArrays()) {
|
|
for(int i = 0; i < numChildren; ++i) {
|
|
PropertyRow* child = container->childByIndex(i);
|
|
child->pulledUp_ = true;
|
|
if (child->label_)
|
|
child->labelUndecorated_ = child->label_ + strlen(child->label_);
|
|
}
|
|
hasPulled_ = true;
|
|
container->setInlined(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (changeLabel)
|
|
labelUndecorated_ = ptr;
|
|
|
|
labelChanged();
|
|
}
|
|
|
|
const char* PropertyRow::typeNameForFilter([[maybe_unused]] QPropertyTree* tree) const
|
|
{
|
|
return typeName();
|
|
}
|
|
|
|
void PropertyRow::updateTextSizeInitial(const QPropertyTree* tree, int index, bool fontChanged)
|
|
{
|
|
char containerLabel[1024] = "";
|
|
const char* text = rowText(containerLabel, sizeof(containerLabel), tree, index);
|
|
if(text[0] == '\0' || widgetPlacement() == WIDGET_INSTEAD_OF_TEXT) {
|
|
textSizeInitial_ = 0;
|
|
textHash_ = 0;
|
|
}
|
|
else{
|
|
unsigned hash = calculateHash(text);
|
|
const QFont* font = rowFont(tree);
|
|
hash = calculateHash(font, hash);
|
|
if(hash != textHash_ || fontChanged){
|
|
QFontMetrics fm(*font);
|
|
textSizeInitial_ = fm.horizontalAdvance(text);
|
|
textHash_ = hash;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PropertyRow::calculateMinimalSize(const QPropertyTree* tree, int posX, int availableWidth, bool force, int* _extraSizeRemainder, int* _extraSize, int index)
|
|
{
|
|
PropertyRow* nonPulled = nonPulledParent();
|
|
if (!layoutChanged_ && !force && !nonPulled->layoutChanged_) {
|
|
DEBUG_TRACE_ROW("... skipping size for %s", label());
|
|
return;
|
|
}
|
|
plusSize_ = 0;
|
|
if(isRoot())
|
|
expanded_ = true;
|
|
else{
|
|
if(nonPulled->isRoot() || (tree->treeStyle().compact && nonPulled->parent()->isRoot()))
|
|
_setExpanded(true);
|
|
else if(!pulledUp())
|
|
plusSize_ = int(tree->treeStyle().firstLevelIndent * tree->_defaultRowHeight());
|
|
|
|
if(parent()->pulledUp())
|
|
pulledBefore_ = false;
|
|
|
|
if(!visible(tree) && !(isContainer() && pulledUp())){
|
|
size_ = QPoint(0, 0);
|
|
DEBUG_TRACE_ROW("row '%s' got zero size", label());
|
|
layoutChanged_ = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
int minWidgetSize = widgetSizeMin(tree);
|
|
widgetSize_ = minWidgetSize;
|
|
if (_extraSizeRemainder && *_extraSizeRemainder) {
|
|
widgetSize_ += *_extraSizeRemainder;
|
|
*_extraSizeRemainder = 0;
|
|
}
|
|
|
|
updateTextSizeInitial(tree, index, force);
|
|
|
|
int height = isRoot() ? 0 : tree->_defaultRowHeight() + floorHeight();
|
|
size_.setY(height);
|
|
|
|
pos_.setX(posX);
|
|
posX += plusSize_;
|
|
|
|
int extraSizeStorage = 0;
|
|
int& extraSize = !pulledUp() || !_extraSize ? extraSizeStorage : *_extraSize;
|
|
|
|
int validatorIconsWidth = 0;
|
|
if (validatorHasErrors_)
|
|
validatorIconsWidth += tree->_defaultRowHeight();
|
|
if (validatorHasWarnings_)
|
|
validatorIconsWidth += tree->_defaultRowHeight();
|
|
|
|
int freePulledChildren = 0;
|
|
if(!pulledUp()){
|
|
int minTextSize = 0;
|
|
int totalMinimalWidth = 0;
|
|
calcPulledRows(&minTextSize, &freePulledChildren, &totalMinimalWidth, tree, index);
|
|
DEBUG_TRACE_ROW("%s minTextSize: %i, totalMinimalWidth: %i", label(), minTextSize, totalMinimalWidth);
|
|
size_.setX(totalMinimalWidth);
|
|
extraSize = (tree->rightBorder() - tree->leftBorder()) - totalMinimalWidth - posX - validatorIconsWidth;
|
|
DEBUG_TRACE_ROW("%s extraSize 0: %i", label(), extraSize);
|
|
|
|
float textScale = 1.0f;
|
|
bool hideOwnText = false;
|
|
if(extraSize < 0){
|
|
// hide container item text first
|
|
if (parent() && parent()->isContainer()){
|
|
extraSize += textSizeInitial_;
|
|
minTextSize -= textSizeInitial_;
|
|
hideOwnText = true;
|
|
}
|
|
|
|
textScale = minTextSize ? clamp(1.0f - float(-extraSize) / minTextSize, 0.0f, 1.0f) : 0;
|
|
}
|
|
setTextSize(tree, index, textScale);
|
|
|
|
if (hideOwnText) {
|
|
textSize_ = 0;
|
|
DEBUG_TRACE_ROW("%s hideOwnText", label());
|
|
}
|
|
}
|
|
|
|
DEBUG_TRACE_ROW("%s extraSize 1: %i", label(), extraSize);
|
|
|
|
WidgetPlacement widgetPlace = widgetPlacement();
|
|
|
|
int numChildren = (int)children_.size();
|
|
|
|
if(widgetPlace == WIDGET_ICON){
|
|
if (tree->treeStyle().alignLabelsToRight && !pulledUp_ && !pulledBefore_ && !hasPulled_ && numChildren == 0)
|
|
widgetPos_ = widgetSize_ ? tree->leftBorder() + xround((tree->rightBorder() - tree->leftBorder())* (1.f - tree->treeStyle().valueColumnWidth)) : -1000;
|
|
else
|
|
widgetPos_ = widgetSize_ ? posX : -1000;
|
|
posX += widgetSize_;
|
|
if (tree->treeStyle().alignLabelsToRight)
|
|
textPos_ = widgetPos_ + widgetSize_ + TEXT_VALUE_SPACING;
|
|
else
|
|
textPos_ = posX;
|
|
posX += textSize_;
|
|
}
|
|
|
|
bool hasPulledBefore = false;
|
|
if (hasPulled_) {
|
|
for (int i = 0; i < numChildren; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
if(row->visible(tree) && row->pulledBefore()){
|
|
row->calculateMinimalSize(tree, posX, availableWidth, force, 0, &extraSize, i);
|
|
posX += row->size_.x();
|
|
hasPulledBefore = true;
|
|
}
|
|
}
|
|
if (hasPulledBefore)
|
|
posX += TEXT_VALUE_SPACING;
|
|
}
|
|
|
|
if(widgetPlace != WIDGET_ICON){
|
|
textPos_ = posX;
|
|
posX += textSize_;
|
|
}
|
|
|
|
if(widgetPlace == WIDGET_AFTER_NAME){
|
|
if (textSize_)
|
|
posX += TEXT_VALUE_SPACING;
|
|
widgetPos_ = posX;
|
|
posX += widgetSize_;
|
|
}
|
|
|
|
if (widgetPlace == WIDGET_INSTEAD_OF_TEXT)
|
|
widgetPos_ = posX;
|
|
|
|
if(widgetPlace == WIDGET_VALUE || widgetPlace == WIDGET_AFTER_PULLED || (freePulledChildren > 0)){
|
|
if (textSize_)
|
|
posX += TEXT_VALUE_SPACING;
|
|
|
|
if(!pulledUp() && extraSize > 0){
|
|
// align widget value to value column
|
|
if(!isFullRow(tree))
|
|
{
|
|
int oldX = posX;
|
|
|
|
bool rightAlignment = tree->treeStyle().alignLabelsToRight && !hasPulledBefore;
|
|
int maxX = rightAlignment ? textSize_ + TEXT_VALUE_SPACING: posX;
|
|
int newX = max(tree->leftBorder() + xround((tree->rightBorder() - tree->leftBorder())* (1.f - tree->treeStyle().valueColumnWidth)), maxX);
|
|
|
|
if (rightAlignment) {
|
|
textPos_ = newX - textSize_ - TEXT_VALUE_SPACING;
|
|
widgetPos_ = textPos_ - widgetSize_ - TEXT_VALUE_SPACING;
|
|
}
|
|
|
|
int xDelta = newX - oldX;
|
|
if (xDelta <= extraSize)
|
|
{
|
|
extraSize -= xDelta;
|
|
posX = newX;
|
|
}
|
|
else
|
|
{
|
|
posX += extraSize;
|
|
extraSize = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int extraSizeRemainder = 0;
|
|
if (freePulledChildren > 0) {
|
|
extraSizeRemainder = extraSize % freePulledChildren;
|
|
extraSize = extraSize / freePulledChildren;
|
|
}
|
|
|
|
if (widgetPlace == WIDGET_VALUE || widgetPlace == WIDGET_INSTEAD_OF_TEXT) {
|
|
if(minWidgetSize && !isWidgetFixed() && extraSize > 0) {
|
|
DEBUG_TRACE_ROW("%s widget extraSize: %i+%d", label(), extraSize, extraSizeRemainder);
|
|
widgetSize_ += extraSize + extraSizeRemainder;
|
|
extraSizeRemainder = 0;
|
|
}
|
|
|
|
if (widgetPlace != WIDGET_INSTEAD_OF_TEXT)
|
|
widgetPos_ = posX;
|
|
DEBUG_TRACE_ROW("textSize: %i widgetPos: %i", int(textSize_), int(widgetPos_));
|
|
posX += widgetSize_;
|
|
}
|
|
|
|
size_.setX(textSize_ + (textSize_ ? TEXT_VALUE_SPACING : 0) + widgetSize_ + validatorIconsWidth);
|
|
|
|
int childrenLeft = nonPulled->pos_.x();
|
|
if (parent() != 0){
|
|
if (parent()->parent() == 0) {
|
|
if (!tree->treeStyle().doNotIndentSecondLevel)
|
|
childrenLeft = aznumeric_cast<int>(childrenLeft + tree->treeStyle().firstLevelIndent * tree->_defaultRowHeight());
|
|
}
|
|
else
|
|
childrenLeft = aznumeric_cast<int>(childrenLeft + tree->treeStyle().levelIndent * tree->_defaultRowHeight());
|
|
}
|
|
|
|
int checkBoxChildren = 0;
|
|
for (int i = 0; i < numChildren; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
if(!row->visible(tree)) {
|
|
DEBUG_TRACE_ROW("skipping invisible child: %s", row->label());
|
|
continue;
|
|
}
|
|
if(row->pulledUp()){
|
|
if(!row->pulledBefore()){
|
|
row->calculateMinimalSize(tree, posX, availableWidth, force, &extraSizeRemainder, &extraSize, i);
|
|
posX += row->size_.x();
|
|
posX += TEXT_VALUE_SPACING;
|
|
}
|
|
size_.setX(size_.x() + TEXT_VALUE_SPACING + row->size_.x());
|
|
size_.setY(max(size_.y(), row->size_.y()));
|
|
}
|
|
else if(expanded()){
|
|
|
|
row->calculateMinimalSize(tree, childrenLeft, availableWidth, force, 0, &extraSize, i);
|
|
if (row->widgetPlacement() == WIDGET_ICON && row->count() == 0)
|
|
++checkBoxChildren;
|
|
}
|
|
}
|
|
|
|
// align checkboxes into two columns
|
|
if (tree->packCheckboxes() || userPackCheckboxes_) {
|
|
if (expanded() && checkBoxChildren > 0 && hasVisibleChildren(tree)) {
|
|
int widthTotal = tree->rightBorder() - 16 - childrenLeft - plusSize_;
|
|
int widthNextToLastCheckbox = 0;
|
|
int left = childrenLeft + plusSize_;
|
|
PropertyRow* previousCheckbox = nullptr;
|
|
|
|
std::vector<PropertyRow*> checkboxesToRealign;
|
|
bool hasChanges = false;
|
|
|
|
for (int i = 0; i < numChildren; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
if(!row->visible(tree))
|
|
continue;
|
|
if(row->widgetPlacement() != WIDGET_ICON || row->count() > 0) {
|
|
previousCheckbox = 0;
|
|
continue;
|
|
}
|
|
if(!row->pulledUp()){
|
|
int checkboxWidth = row->textSize_ + tree->_defaultRowHeight()/* + TEXT_VALUE_SPACING*/;
|
|
|
|
if (previousCheckbox && widthNextToLastCheckbox >= widthTotal / 2 && checkboxWidth < widthTotal / 2) {
|
|
row->packedAfterPreviousRow_ = true;
|
|
widthNextToLastCheckbox = 0;
|
|
|
|
row->pos_.setX(left + widthTotal / 2);
|
|
row->widgetPos_ = row->pos_.x();
|
|
row->textPos_ = row->pos_.x() + row->widgetSize_;
|
|
row->size_.setX(widthTotal / 2);
|
|
|
|
previousCheckbox->size_.setX(widthTotal / 2);
|
|
previousCheckbox->pos_.setX(left);
|
|
previousCheckbox->widgetPos_ = left;
|
|
previousCheckbox->textPos_ = left + previousCheckbox->widgetSize_;
|
|
row->size_.setX(widthTotal / 2);
|
|
previousCheckbox = 0;
|
|
hasChanges = true;
|
|
}
|
|
else {
|
|
row->packedAfterPreviousRow_ = false;
|
|
widthNextToLastCheckbox = widthTotal - checkboxWidth;
|
|
previousCheckbox = row;
|
|
}
|
|
|
|
if (previousCheckbox && tree->treeStyle().alignLabelsToRight)
|
|
checkboxesToRealign.push_back(previousCheckbox);
|
|
}
|
|
}
|
|
|
|
if (hasChanges) {
|
|
for (int i = 0; i < checkboxesToRealign.size(); ++i) {
|
|
PropertyRow* row = checkboxesToRealign[i];
|
|
row->size_.setX(widthTotal / 2);
|
|
row->pos_.setX(left);
|
|
row->widgetPos_ = left;
|
|
row->textPos_ = left + row->widgetSize_;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (widgetPlace == WIDGET_AFTER_PULLED)
|
|
{
|
|
posX += TEXT_VALUE_SPACING;
|
|
widgetPos_ = posX;
|
|
}
|
|
|
|
if(!pulledUp())
|
|
size_.setX(tree->rightBorder() - pos_.x());
|
|
DEBUG_TRACE_ROW("calculateMinimalSize: '%s' %i %i (%s)", label(), size_.x(), size_.y(), isRoot() ? "root" : "non-root");
|
|
layoutChanged_ = false;
|
|
|
|
validatorsHeight_ = 0;
|
|
if (!pulledUp() && !pulledBefore() && (validatorCount_ != 0 || hasPulled_)) {
|
|
QFontMetrics fm(tree->font());
|
|
int padding = aznumeric_cast<int>(0.1f * tree->_defaultRowHeight());
|
|
auto calculateValidatorHeight = [&](PropertyRow* row) {
|
|
const ValidatorEntry* entries = tree->_validatorBlock()->GetEntry(row->validatorIndex_, row->validatorCount_);
|
|
if (entries) {
|
|
for (int i = 0; i < row->validatorCount_; ++i) {
|
|
int startPos = pos_.x() + plusSize_;
|
|
QRect r = fm.boundingRect(0, 0, availableWidth - startPos - tree->_defaultRowHeight() - padding * 2, 0,
|
|
Qt::TextWordWrap|Qt::AlignTop, QString::fromUtf8(entries[i].message.c_str()));
|
|
validatorsHeight_ += max(tree->_defaultRowHeight(), r.height() + padding * 2) + padding * 3;
|
|
}
|
|
}
|
|
};
|
|
calculateValidatorHeight(this);
|
|
visitPulledRows(this, calculateValidatorHeight);
|
|
}
|
|
|
|
size_.setY(size_.y() + validatorsHeight_);
|
|
}
|
|
|
|
void PropertyRow::adjustVerticalPosition(const QPropertyTree* tree, int& totalHeight)
|
|
{
|
|
int defaultRowHeight = tree->_defaultRowHeight();
|
|
pos_.setY(totalHeight);
|
|
int rowHeight = size_.y() + int(defaultRowHeight * (tree->treeStyle().rowSpacing - 1.0f) + 0.5f);
|
|
|
|
if (packedAfterPreviousRow_)
|
|
pos_.setY(totalHeight - rowHeight);
|
|
else
|
|
pos_.setY(totalHeight);
|
|
|
|
if(!pulledUp()) {
|
|
if (!packedAfterPreviousRow_)
|
|
totalHeight += rowHeight;
|
|
}
|
|
else{
|
|
pos_.setY(parent()->pos_.y());
|
|
expanded_ = parent()->expanded();
|
|
}
|
|
PropertyRow* nonPulled = nonPulledParent();
|
|
|
|
DEBUG_TRACE_ROW("adjustRect: %s %i %i %i %i %s", label(), pos_.x(), pos_.y(), size_.x(), size_.y(), pulledUp() ? "pulled" : "");
|
|
|
|
if (expanded_ || hasPulled_) {
|
|
for(PropertyRows::iterator it = children_.begin(); it != children_.end(); ++it){
|
|
PropertyRow* row = *it;
|
|
if(row->visible(tree) && (nonPulled->expanded() || row->pulledUp()))
|
|
row->adjustVerticalPosition(tree, totalHeight);
|
|
}
|
|
}
|
|
int delta = totalHeight - pos_.y();
|
|
if (delta > USHRT_MAX)
|
|
delta = USHRT_MAX;
|
|
heightIncludingChildren_ = delta;
|
|
}
|
|
|
|
void PropertyRow::setTextSize(const QPropertyTree* tree, int index, float mult)
|
|
{
|
|
updateTextSizeInitial(tree, index, false);
|
|
|
|
textSize_ = int(textSizeInitial_ * mult);
|
|
|
|
size_t numChildren = children_.size();
|
|
for (size_t i = 0; i < numChildren; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
if(row->pulledUp())
|
|
row->setTextSize(tree, 0, mult);
|
|
}
|
|
}
|
|
|
|
void PropertyRow::calcPulledRows(int* minTextSize, int* freePulledChildren, int* minimalWidth, const QPropertyTree *tree, int index)
|
|
{
|
|
updateTextSizeInitial(tree, index, false);
|
|
|
|
*minTextSize += textSizeInitial_;
|
|
WidgetPlacement widgetPlace = widgetPlacement();
|
|
if((widgetPlace == WIDGET_VALUE || widgetPlace == WIDGET_INSTEAD_OF_TEXT || widgetPlace == WIDGET_AFTER_PULLED) && !isWidgetFixed())
|
|
*freePulledChildren += 1;
|
|
*minimalWidth += textSizeInitial_ + widgetSizeMin(tree); // spacing
|
|
bool hasWidget = widgetPlace == WIDGET_VALUE ||
|
|
widgetPlace == WIDGET_INSTEAD_OF_TEXT ||
|
|
widgetPlace == WIDGET_AFTER_PULLED;
|
|
if (textSizeInitial_ && (hasWidget || hasPulled_))
|
|
*minimalWidth += TEXT_VALUE_SPACING;
|
|
if (hasWidget && hasPulled_)
|
|
*minimalWidth += TEXT_VALUE_SPACING;
|
|
|
|
size_t numChildren = children_.size();
|
|
int pulledCount = 0;
|
|
for (size_t i = 0; i < numChildren; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
if(row->pulledUp())
|
|
{
|
|
++pulledCount;
|
|
row->calcPulledRows(minTextSize, freePulledChildren, minimalWidth, tree, index);
|
|
}
|
|
}
|
|
if (hasPulled_)
|
|
*minimalWidth += (pulledCount - 1) * TEXT_VALUE_SPACING;
|
|
}
|
|
|
|
PropertyRow* PropertyRow::findSelected()
|
|
{
|
|
if(selected())
|
|
return this;
|
|
iterator it;
|
|
for(it = children_.begin(); it != children_.end(); ++it){
|
|
PropertyRow* result = (*it)->findSelected();
|
|
if(result)
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
PropertyRow* PropertyRow::find(const char* name, const char* nameAlt, const char* typeName)
|
|
{
|
|
iterator it;
|
|
for(it = children_.begin(); it != children_.end(); ++it){
|
|
PropertyRow* row = *it;
|
|
if(((row->name() == name) || strcmp(row->name(), name) == 0) &&
|
|
((nameAlt == 0) || (row->label() != 0 && strcmp(row->label(), nameAlt) == 0)) &&
|
|
((typeName == 0) || (row->typeName() != 0 && strcmp(row->typeName(), typeName) == 0)))
|
|
return row;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
PropertyRow* PropertyRow::findFromIndex(int* outIndex, const char* name, const char* typeName, int startIndex) const
|
|
{
|
|
int numChildren = (int)children_.size();
|
|
startIndex = min(startIndex, numChildren);
|
|
|
|
for (int i = startIndex; i < numChildren; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
if(((row->name() == name) || strcmp(row->name(), name) == 0) &&
|
|
((row->typeName() == typeName || strcmp(row->typeName(), typeName) == 0))) {
|
|
*outIndex = i;
|
|
return row;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < startIndex; ++i) {
|
|
PropertyRow* row = children_[i];
|
|
if(((row->name() == name) || strcmp(row->name(), name) == 0) &&
|
|
((row->typeName() == typeName || strcmp(row->typeName(), typeName) == 0))) {
|
|
*outIndex = i;
|
|
return row;
|
|
}
|
|
}
|
|
|
|
*outIndex = -1;
|
|
return 0;
|
|
}
|
|
|
|
const PropertyRow* PropertyRow::find(const char* name, const char* nameAlt, const char* typeName) const
|
|
{
|
|
return const_cast<PropertyRow* const>(this)->find(name, nameAlt, typeName);
|
|
}
|
|
|
|
bool PropertyRow::processesKey([[maybe_unused]] QPropertyTree* tree, const QKeyEvent* ev)
|
|
{
|
|
if (ev->key() == Qt::Key_Delete && ev->modifiers() == Qt::NoModifier)
|
|
{
|
|
return true;
|
|
}
|
|
else if (ev->key() == Qt::Key_Insert && ev->modifiers() == Qt::SHIFT)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PropertyRow::onKeyDown(QPropertyTree* tree, const QKeyEvent* ev)
|
|
{
|
|
if(parent() && parent()->isContainer() && !parent()->userReadOnly()){
|
|
PropertyRowContainer* container = static_cast<PropertyRowContainer*>(parent());
|
|
std::unique_ptr<ContainerMenuHandler> menuHandler =
|
|
std::unique_ptr<ContainerMenuHandler>(createMenuHandler(tree, container));
|
|
menuHandler->element = this;
|
|
if(ev->key() == Qt::Key_Delete && ev->modifiers() == Qt::NoModifier) {
|
|
menuHandler->onMenuChildRemove();
|
|
return true;
|
|
}
|
|
else if(ev->key() == Qt::Key_Insert && ev->modifiers() == Qt::SHIFT){
|
|
menuHandler->onMenuChildInsertBefore();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ContainerMenuHandler* PropertyRow::createMenuHandler(QPropertyTree* tree, PropertyRowContainer* container)
|
|
{
|
|
return new ContainerMenuHandler(tree, container);
|
|
}
|
|
|
|
bool PropertyRow::onContextMenu(QMenu &menu, QPropertyTree* tree)
|
|
{
|
|
PropertyRowContainer* container = 0;
|
|
if (parent() && parent()->isContainer())
|
|
container = static_cast<PropertyRowContainer*>(parent());
|
|
if (!container) {
|
|
PropertyRow* nonPulled = nonPulledParent();
|
|
if (nonPulled->parent() && nonPulled->parent()->isContainer())
|
|
container = static_cast<PropertyRowContainer*>(nonPulled->parent());
|
|
}
|
|
if(container){
|
|
PropertyRow* containerElement = this;
|
|
ContainerMenuHandler* handler = createMenuHandler(tree, container);
|
|
handler->element = containerElement;
|
|
tree->addMenuHandler(handler);
|
|
if(!container->isFixedSize()){
|
|
if(!menu.isEmpty())
|
|
{
|
|
menu.addSeparator();
|
|
}
|
|
|
|
menu.addAction("Insert Before", handler, SLOT(onMenuChildInsertBefore()), QKeySequence("Shift+Insert"))->setEnabled(!container->userReadOnly());
|
|
menu.addAction("Remove", handler, SLOT(onMenuChildRemove()), QKeySequence("Delete"))->setEnabled(!container->userReadOnly());
|
|
}
|
|
}
|
|
|
|
if(hasVisibleChildren(tree)){
|
|
if(!menu.isEmpty())
|
|
{
|
|
menu.addSeparator();
|
|
}
|
|
|
|
menu.addAction("Expand", tree, SLOT(expandAll()));
|
|
menu.addAction("Collapse", tree, SLOT(collapseAll()));
|
|
}
|
|
|
|
return !menu.isEmpty();
|
|
}
|
|
|
|
int PropertyRow::level() const
|
|
{
|
|
int result = 0;
|
|
const PropertyRow* row = this;
|
|
while(row){
|
|
row = row->parent();
|
|
++result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
PropertyRow* PropertyRow::nonPulledParent()
|
|
{
|
|
PropertyRow* row = this;
|
|
while(row->pulledUp())
|
|
row = row->parent();
|
|
return row;
|
|
}
|
|
|
|
bool PropertyRow::pulledSelected() const
|
|
{
|
|
if(selected())
|
|
return true;
|
|
const PropertyRow* row = this;
|
|
while(row->parent() && row->pulledUp()){
|
|
row = row->parent();
|
|
if(row->selected())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
const QFont* PropertyRow::rowFont(const QPropertyTree* tree) const
|
|
{
|
|
switch (fontWeight_)
|
|
{
|
|
case FontWeight::Regular:
|
|
return &tree->font();
|
|
|
|
case FontWeight::Bold:
|
|
return &tree->_boldFont();
|
|
|
|
default:
|
|
// Bold for structures/containers
|
|
return hasVisibleChildren(tree) || (isContainer() && !static_cast<const PropertyRowContainer*>(this)->isInlined()) ? &tree->_boldFont() : &tree->font();
|
|
}
|
|
}
|
|
|
|
QRect PropertyRow::rectIncludingChildren(const QPropertyTree* tree) const
|
|
{
|
|
QRect r = rect();
|
|
if (expanded())
|
|
for (size_t i = 0; i < children_.size(); ++i)
|
|
if (children_[i]->visible(tree))
|
|
r = r.united(children_[i]->rectIncludingChildren(tree));
|
|
return r;
|
|
}
|
|
|
|
static void drawVerticalGradient(QPainter& painter, const QRect& rect, const QColor& topColor, const QColor& bottomColor)
|
|
{
|
|
QLinearGradient gradient(rect.left(), rect.top(), rect.left(), rect.bottom());
|
|
gradient.setColorAt(0.0f, topColor);
|
|
gradient.setColorAt(1.0f, bottomColor);
|
|
painter.fillRect(rect, QBrush(gradient));
|
|
}
|
|
|
|
|
|
void PropertyRow::drawRow(QPainter& painter, const QPropertyTree* tree, int index, bool selectionPass)
|
|
{
|
|
QRect rowRect = rect();
|
|
QRect selectionRect = rowRect;
|
|
if (!isRoot()) {
|
|
bool selectionDrawn = !tree->hideSelection() || tree->hasFocusOrInplaceHasFocus();
|
|
if(!pulledUp())
|
|
selectionRect = rowRect.adjusted(plusSize_ - (tree->treeStyle().compact ? 2 : 3), -2, 1, 1);
|
|
else
|
|
selectionRect = rowRect.adjusted(-2, -2, 2, 1);
|
|
if (selectionPass) {
|
|
if (tree->treeStyle().groupShadows && this->level() == 2 && !children_.empty()) {
|
|
QRect childrenRect = this->rectIncludingChildren(tree);
|
|
int top = rowRect.bottom() + 2;
|
|
if (top < childrenRect.bottom())
|
|
{
|
|
childrenRect = QRect(-tree->leftBorder(), top, tree->width() - 16 + tree->leftBorder(), childrenRect.bottom() + 3 - top);
|
|
QColor windowColor = tree->palette().color(QPalette::Button);
|
|
QColor shadowColor = tree->palette().color(QPalette::Mid);
|
|
QColor backgroundColor(interpolateColor(windowColor, shadowColor, tree->treeStyle().groupShade));
|
|
painter.fillRect(childrenRect, QBrush(backgroundColor));
|
|
|
|
int levelShadowOpacity = tree->treeStyle().levelShadowOpacity;
|
|
int h = int(tree->_defaultRowHeight() * 0.75f);
|
|
drawVerticalGradient(painter, QRect(childrenRect.left()+1, childrenRect.top(), childrenRect.width()-2, h), QColor(0, 0, 0, levelShadowOpacity), QColor(0, 0, 0, 0));
|
|
drawVerticalGradient(painter, QRect(childrenRect.left(), childrenRect.top(), 1, h*2), QColor(0, 0, 0, levelShadowOpacity), QColor(0, 0, 0, 0));
|
|
drawVerticalGradient(painter, QRect(childrenRect.width()-2, childrenRect.top(), 1, h*2), QColor(0, 0, 0, levelShadowOpacity), QColor(0, 0, 0, 0));
|
|
|
|
h = (int(tree->_defaultRowHeight() * 0.25f));
|
|
drawVerticalGradient(painter, QRect(childrenRect.left() + 1, childrenRect.bottom() - h, childrenRect.width() - 2, h), QColor(0, 0, 0, 0), QColor(0, 0, 0, levelShadowOpacity));
|
|
drawVerticalGradient(painter, QRect(childrenRect.left(), childrenRect.bottom() - h*2, 1, h*2), QColor(0, 0, 0, 0), QColor(0, 0, 0, levelShadowOpacity));
|
|
drawVerticalGradient(painter, QRect(childrenRect.width()-2, childrenRect.bottom() - h*2, 1, h*2), QColor(0, 0, 0, 0), QColor(0, 0, 0, levelShadowOpacity));
|
|
}
|
|
}
|
|
if (tree->treeStyle().groupRectangle && this->level() < 3 && (canBeToggled(tree) || isContainer() || widgetPlacement() == WIDGET_NONE))
|
|
{
|
|
QColor windowColor = tree->palette().color(QPalette::Button);
|
|
QColor shadowColor = tree->palette().color(QPalette::Mid);
|
|
QColor backgroundColor(interpolateColor(windowColor, shadowColor, tree->treeStyle().groupShade));
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
painter.setBrush(QBrush(backgroundColor));
|
|
painter.setPen(Qt::NoPen);
|
|
painter.drawRoundedRect(rowRect.adjusted(0, tree->_defaultRowHeight() / 8, 0, -tree->_defaultRowHeight() / 8), 4, 4);
|
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
|
|
}
|
|
}
|
|
else{
|
|
PropertyDrawContext context;
|
|
context.tree = tree;
|
|
context.widgetRect = widgetRect(tree);
|
|
context.lineRect = floorRect(tree);
|
|
context.painter = &painter;
|
|
context.captured = tree->_isCapturedRow(this);
|
|
context.m_pressed = tree->_pressedRow() == this;
|
|
|
|
QColor textColor = tree->palette().buttonText().color();
|
|
|
|
char containerLabel[1024] = "";
|
|
wstring text = toWideChar(rowText(containerLabel, sizeof(containerLabel), tree, index));
|
|
|
|
if (tree->treeStyle().showHorizontalLines) {
|
|
if(textSize_ && !isStatic() && widgetPlacement() == WIDGET_VALUE &&
|
|
!pulledUp() && !isFullRow(tree) && !hasPulled() && floorHeight() == 0)
|
|
{
|
|
QRect rect(textPos_ - 1, rowRect.bottom() - 2, context.lineRect.width() - (textPos_ - 1), 1);
|
|
|
|
QLinearGradient gradient(rect.left(), rect.top(), rect.right(), rect.top());
|
|
gradient.setColorAt(0.0f, tree->palette().color(QPalette::Button));
|
|
gradient.setColorAt(0.6f, tree->palette().color(QPalette::Light));
|
|
gradient.setColorAt(0.95f, tree->palette().color(QPalette::Light));
|
|
gradient.setColorAt(1.0f, tree->palette().color(QPalette::Button));
|
|
QBrush brush(gradient);
|
|
painter.fillRect(rect, brush);
|
|
}
|
|
}
|
|
|
|
|
|
if(selectionDrawn && pulledSelected()){
|
|
textColor = tree->palette().highlight().color();
|
|
}
|
|
else{
|
|
overrideTextColor(textColor);
|
|
}
|
|
|
|
if(!tree->treeStyle().compact || !parent()->isRoot()){
|
|
if(hasVisibleChildren(tree)){
|
|
drawPlus(painter, tree, plusRect(tree), expanded(), selected(), expanded());
|
|
}
|
|
}
|
|
|
|
if(!isStatic() && context.widgetRect.isValid())
|
|
redraw(context);
|
|
|
|
if(textSize_ > 0){
|
|
const QFont* font = rowFont(tree);
|
|
tree->_drawRowLabel(painter, text.c_str(), font, textRect(tree), textColor);
|
|
}
|
|
|
|
if (validatorHasWarnings_) {
|
|
QImage* icon = tree->_iconCache()->getImageForIcon(Serialization::IconXPM(warning_xpm));
|
|
|
|
QRect r = validatorWarningIconRect(tree);
|
|
r.setWidth(tree->_defaultRowHeight());
|
|
painter.drawImage(r.center() - QPoint(icon->width() / 2, icon->height() / 2), *icon);
|
|
}
|
|
if (validatorHasErrors_) {
|
|
QImage* icon = tree->_iconCache()->getImageForIcon(Serialization::IconXPM(error_xpm));
|
|
QRect r = validatorErrorIconRect(tree);
|
|
r.setWidth(tree->_defaultRowHeight());
|
|
painter.drawImage(r.center() - QPoint(icon->width() / 2, icon->height() / 2), *icon);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!selectionPass && validatorsHeight_ > 0)
|
|
{
|
|
QRect totalRect = validatorRect(tree);
|
|
QFontMetrics fm(tree->font());
|
|
const int padding = aznumeric_cast<int>(tree->_defaultRowHeight() * 0.1f);
|
|
int offset = padding;
|
|
auto drawFunc = [&](PropertyRow* row) {
|
|
if (const ValidatorEntry* validatorEntries = tree->_validatorBlock()->GetEntry(row->validatorIndex_, row->validatorCount_)) {
|
|
for (int i = 0; i < row->validatorCount_; ++i) {
|
|
const ValidatorEntry* validatorEntry = validatorEntries + i;
|
|
bool isError = validatorEntry->type == VALIDATOR_ENTRY_ERROR;
|
|
|
|
QImage* icon = tree->_iconCache()->getImageForIcon(isError ? Serialization::IconXPM(error_xpm) : Serialization::IconXPM(warning_xpm));
|
|
QColor brushColor = isError ? QColor(255, 64, 64, 192) : QPalette().color(QPalette::ToolTipBase);
|
|
QColor penColor = isError ? QColor(64, 0, 0, 255) : QPalette().color(QPalette::ToolTipText);
|
|
|
|
QRect rect(totalRect.left(), totalRect.top() + offset,
|
|
totalRect.width(), totalRect.height() - offset);
|
|
QRect textRect = rect.adjusted(tree->_defaultRowHeight() + padding, padding, -padding, -padding);
|
|
const char* text = validatorEntry->message.c_str();
|
|
int textHeight = max(tree->_defaultRowHeight(),
|
|
fm.boundingRect(textRect, Qt::TextWordWrap, text, 0, 0).height() + padding * 2);
|
|
rect.setHeight(textHeight + padding * 2);
|
|
textRect.setHeight(textHeight);
|
|
|
|
QPen pen(penColor);
|
|
pen.setWidth(1);
|
|
painter.setPen(QPen(penColor));
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
painter.setBrush(brushColor);
|
|
painter.translate(-0.5f, -0.5f);
|
|
painter.drawRoundedRect(rect, 5, 5, Qt::AbsoluteSize);
|
|
painter.translate(0.5f, 0.5f);
|
|
painter.setPen(penColor);
|
|
painter.setBrush(QBrush());
|
|
QTextOption opt;
|
|
opt.setWrapMode(QTextOption::WordWrap);
|
|
opt.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
|
painter.drawText(textRect, text, opt);
|
|
textRect.setHeight(0xffff);
|
|
QRect iconRect(rect.left(), rect.top(), tree->_defaultRowHeight(), rect.height());
|
|
painter.drawImage(iconRect.center() - QPoint(icon->width() / 2, icon->height() / 2), *icon);
|
|
offset += rect.height() + padding;
|
|
}
|
|
}
|
|
};
|
|
drawFunc(this);
|
|
visitPulledRows(this, drawFunc);
|
|
}
|
|
}
|
|
|
|
void PropertyRow::drawPlus(QPainter& p, const QPropertyTree* tree, const QRect& rect, bool expanded, [[maybe_unused]] bool selected, [[maybe_unused]] bool grayed) const
|
|
{
|
|
QStyleOption option;
|
|
option.rect = rect;
|
|
option.state = QStyle::State_Enabled | QStyle::State_Children;
|
|
if (expanded)
|
|
option.state |= QStyle::State_Open;
|
|
p.setPen(QPen());
|
|
p.setBrush(QBrush());
|
|
|
|
// create a widget for context so that the stylesheet is applied:
|
|
QWidget tempWidgetForContext;
|
|
QStyle::PrimitiveElement elementToUse = QStyle::PE_IndicatorArrowRight;
|
|
if (expanded)
|
|
elementToUse = QStyle::PE_IndicatorArrowDown;
|
|
|
|
tree->style()->drawPrimitive(elementToUse, &option, &p, &tempWidgetForContext);
|
|
}
|
|
|
|
bool PropertyRow::visible(const QPropertyTree* tree) const
|
|
{
|
|
if (tree->_isDragged(this))
|
|
return false;
|
|
return ((visible_ || !tree->hideUntranslated()) && (matchFilter_ || belongsToFilteredRow_));
|
|
}
|
|
|
|
bool PropertyRow::canBeToggled(const QPropertyTree* tree) const
|
|
{
|
|
if(!visible(tree))
|
|
return false;
|
|
if((tree->treeStyle().compact && (parent() && parent()->isRoot())) || (isContainer() && pulledUp()) || !hasVisibleChildren(tree))
|
|
return false;
|
|
return !empty();
|
|
}
|
|
|
|
bool PropertyRow::canBeDragged() const
|
|
{
|
|
if(parent()){
|
|
if(parent()->isContainer())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PropertyRow::canBeDroppedOn(const PropertyRow* parentRow, const PropertyRow* beforeChild, const QPropertyTree* tree) const
|
|
{
|
|
YASLI_ASSERT(parentRow);
|
|
|
|
if(parentRow->pulledContainer())
|
|
parentRow = parentRow->pulledContainer();
|
|
|
|
if(parentRow->isContainer()){
|
|
const PropertyRowContainer* container = static_cast<const PropertyRowContainer*>(parentRow);
|
|
|
|
if((container->isFixedSize() || container->userReadOnly()) && parent() != parentRow)
|
|
return false;
|
|
|
|
if(beforeChild && beforeChild->parent() != parentRow)
|
|
return false;
|
|
|
|
const PropertyRow* defaultRow = container->defaultRow(tree->model());
|
|
if(defaultRow && strcmp(defaultRow->typeName(), typeName()) == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PropertyRow::dropInto(PropertyRow* parentRow, PropertyRow* cursorRow, QPropertyTree* tree, bool before)
|
|
{
|
|
SharedPtr<PropertyRow> ref(this);
|
|
|
|
PropertyTreeModel* model = tree->model();
|
|
PropertyTreeModel::UpdateLock lock = model->lockUpdate();
|
|
if(parentRow->pulledContainer())
|
|
parentRow = parentRow->pulledContainer();
|
|
if(parentRow->isContainer()){
|
|
tree->model()->rowAboutToBeChanged(tree->model()->root()); // FIXME: select optimal row
|
|
setSelected(false);
|
|
PropertyRow* oldParent = parent();
|
|
TreePath oldParentPath = tree->model()->pathFromRow(oldParent);
|
|
oldParent->erase(this);
|
|
if(before)
|
|
parentRow->addBefore(this, cursorRow);
|
|
else
|
|
parentRow->addAfter(this, cursorRow);
|
|
model->selectRow(this, true);
|
|
TreePath thisPath = tree->model()->pathFromRow(this);
|
|
TreePath parentRowPath = tree->model()->pathFromRow(parentRow);
|
|
oldParent = tree->model()->rowFromPath(oldParentPath);
|
|
if (oldParent)
|
|
model->rowChanged(oldParent); // after this call we can get invalid this
|
|
if(PropertyRow* newThis = tree->model()->rowFromPath(thisPath)) {
|
|
TreeSelection selection;
|
|
selection.push_back(thisPath);
|
|
model->setSelection(selection);
|
|
|
|
// we use path to obtain new row
|
|
tree->ensureVisible(newThis);
|
|
model->rowChanged(newThis); // after this call row pointers are invalidated
|
|
}
|
|
parentRow = tree->model()->rowFromPath(parentRowPath);
|
|
if (parentRow)
|
|
model->rowChanged(parentRow); // after this call row pointers are invalidated
|
|
}
|
|
}
|
|
|
|
void PropertyRow::intersect(const PropertyRow* row)
|
|
{
|
|
setMultiValue(multiValue() || row->multiValue() || valueAsString() != row->valueAsString());
|
|
|
|
|
|
int indexSource = 0;
|
|
for(int i = 0; i < int(children_.size()); ++i)
|
|
{
|
|
PropertyRow* testRow = children_[i];
|
|
PropertyRow* matchingRow = row->findFromIndex(&indexSource, testRow->name_, testRow->typeName_, indexSource);
|
|
++indexSource;
|
|
if (matchingRow == 0) {
|
|
children_.erase(children_.begin() + i);
|
|
--i;
|
|
}
|
|
else {
|
|
children_[i]->intersect(matchingRow);
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* PropertyRow::rowText(char *containerLabelBuffer, size_t bufsiz, const QPropertyTree* tree, int index) const
|
|
{
|
|
if(parent() && parent()->isContainer() && !pulledUp()){
|
|
if (tree->showContainerIndices()) {
|
|
if (tree->showContainerIndexLabels()) {
|
|
azsnprintf(containerLabelBuffer, bufsiz, " %i. %s",
|
|
index + 1 - tree->containerIndicesZeroBased(),
|
|
labelUndecorated() ? labelUndecorated() : "");
|
|
}
|
|
else {
|
|
azsnprintf(containerLabelBuffer, bufsiz, "%i.",
|
|
index + 1 - tree->containerIndicesZeroBased());
|
|
}
|
|
return containerLabelBuffer;
|
|
}
|
|
else
|
|
return "";
|
|
}
|
|
else
|
|
return labelUndecorated() ? labelUndecorated() : "";
|
|
}
|
|
|
|
bool PropertyRow::hasVisibleChildren(const QPropertyTree* tree, bool internalCall) const
|
|
{
|
|
if(empty() || (!internalCall && pulledUp()))
|
|
return false;
|
|
|
|
PropertyRow::const_iterator it;
|
|
for(it = children_.begin(); it != children_.end(); ++it){
|
|
const PropertyRow* child = *it;
|
|
if(child->pulledUp()){
|
|
if(child->hasVisibleChildren(tree, true))
|
|
return true;
|
|
}
|
|
else if(child->visible(tree))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const PropertyRow* PropertyRow::hit(const QPropertyTree* tree, QPoint point) const
|
|
{
|
|
return const_cast<PropertyRow*>(this)->hit(tree, point);
|
|
}
|
|
|
|
PropertyRow* PropertyRow::hit(const QPropertyTree* tree, QPoint point)
|
|
{
|
|
bool expanded = this->expanded();
|
|
if(isContainer() && pulledUp())
|
|
expanded = parent() ? parent()->expanded() : true;
|
|
bool onlyPulled = !expanded;
|
|
PropertyRow::const_iterator it;
|
|
for(it = children_.begin(); it != children_.end(); ++it){
|
|
PropertyRow* child = *it;
|
|
if (!child->visible(tree))
|
|
continue;
|
|
if(!onlyPulled || child->pulledUp())
|
|
if(PropertyRow* result = child->hit(tree, point))
|
|
return result;
|
|
}
|
|
if (QRect(pos_.x(), pos_.y(), size_.x(), size_.y()).contains(point))
|
|
return this;
|
|
return 0;
|
|
}
|
|
|
|
PropertyRow* PropertyRow::findByAddress(const void* addr)
|
|
{
|
|
if(searchHandle() == addr)
|
|
return this;
|
|
else{
|
|
Rows::iterator it;
|
|
for(it = children_.begin(); it != children_.end(); ++it){
|
|
PropertyRow* result = it->get()->findByAddress(addr);
|
|
if(result)
|
|
return result;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const void* PropertyRow::searchHandle() const
|
|
{
|
|
return serializer_.pointer();
|
|
}
|
|
|
|
|
|
PropertyRow* PropertyRow::findChildFromDescendant(PropertyRow* row) const
|
|
{
|
|
PropertyRow* child = row;
|
|
Rows::const_iterator it = std::find(children_.begin(), children_.end(), child);
|
|
while( it == children_.end() && child )
|
|
{
|
|
child = child->parent();
|
|
it = std::find(children_.begin(), children_.end(), child);
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
struct GetVerticalIndexOp{
|
|
int index_;
|
|
const PropertyRow* row_;
|
|
|
|
GetVerticalIndexOp(const PropertyRow* row) : row_(row), index_(0) {}
|
|
|
|
ScanResult operator()(PropertyRow* row, QPropertyTree* tree, [[maybe_unused]] int index)
|
|
{
|
|
if(row == row_)
|
|
return SCAN_FINISHED;
|
|
if(row->visible(tree) && row->isSelectable() && !row->pulledUp() && !row->packedAfterPreviousRow())
|
|
++index_;
|
|
return row->expanded() ? SCAN_CHILDREN_SIBLINGS : SCAN_SIBLINGS;
|
|
}
|
|
};
|
|
|
|
int PropertyRow::verticalIndex(QPropertyTree* tree, PropertyRow* row)
|
|
{
|
|
GetVerticalIndexOp op(row);
|
|
scanChildren(op, tree);
|
|
return op.index_;
|
|
}
|
|
|
|
|
|
struct RowByVerticalIndexOp{
|
|
int index_;
|
|
PropertyRow* row_;
|
|
|
|
RowByVerticalIndexOp(int index) : row_(0), index_(index) {}
|
|
|
|
ScanResult operator()(PropertyRow* row, QPropertyTree* tree, [[maybe_unused]] int index)
|
|
{
|
|
if(row->visible(tree) && !row->pulledUp() && row->isSelectable() && !row->packedAfterPreviousRow()){
|
|
row_ = row;
|
|
if(index_-- <= 0)
|
|
return SCAN_FINISHED;
|
|
}
|
|
return row->expanded() ? SCAN_CHILDREN_SIBLINGS : SCAN_SIBLINGS;
|
|
}
|
|
};
|
|
|
|
PropertyRow* PropertyRow::rowByVerticalIndex(QPropertyTree* tree, int index)
|
|
{
|
|
RowByVerticalIndexOp op(index);
|
|
scanChildren(op, tree);
|
|
return op.row_;
|
|
}
|
|
|
|
struct HorizontalIndexOp{
|
|
int index_;
|
|
PropertyRow* row_;
|
|
bool pulledBefore_;
|
|
|
|
HorizontalIndexOp(PropertyRow* row) : row_(row), index_(0), pulledBefore_(row->pulledBefore()) {}
|
|
|
|
ScanResult operator()(PropertyRow* row, QPropertyTree* tree, [[maybe_unused]] int index)
|
|
{
|
|
if(!row->pulledUp())
|
|
return SCAN_SIBLINGS;
|
|
if(row->visible(tree) && row->isSelectable() && row->pulledUp() && row->pulledBefore() == pulledBefore_){
|
|
index_ += pulledBefore_ ? -1 : 1;
|
|
if(row == row_)
|
|
return SCAN_FINISHED;
|
|
}
|
|
return SCAN_CHILDREN_SIBLINGS;
|
|
}
|
|
};
|
|
|
|
int PropertyRow::horizontalIndex(QPropertyTree* tree, PropertyRow* row)
|
|
{
|
|
if(row == this)
|
|
return 0;
|
|
HorizontalIndexOp op(row);
|
|
if(row->pulledBefore())
|
|
scanChildrenReverse(op, tree);
|
|
else
|
|
scanChildren(op, tree);
|
|
return op.index_;
|
|
}
|
|
|
|
struct RowByHorizontalIndexOp{
|
|
int index_;
|
|
PropertyRow* row_;
|
|
bool pulledBefore_;
|
|
|
|
RowByHorizontalIndexOp(int index) : row_(0), index_(index), pulledBefore_(index < 0) {}
|
|
|
|
ScanResult operator()(PropertyRow* row, QPropertyTree* tree, [[maybe_unused]] int index)
|
|
{
|
|
if(!row->pulledUp())
|
|
return SCAN_SIBLINGS;
|
|
if(row->visible(tree) && row->isSelectable() && row->pulledUp() && row->pulledBefore() == pulledBefore_){
|
|
row_ = row;
|
|
if(pulledBefore_ ? ++index_ >= 0 : --index_ <= 0)
|
|
return SCAN_FINISHED;
|
|
}
|
|
return SCAN_CHILDREN_SIBLINGS;
|
|
}
|
|
};
|
|
|
|
PropertyRow* PropertyRow::rowByHorizontalIndex(QPropertyTree* tree, int index)
|
|
{
|
|
if(!index)
|
|
return this;
|
|
RowByHorizontalIndexOp op(index);
|
|
if(index < 0)
|
|
scanChildrenReverse(op, tree);
|
|
else
|
|
scanChildren(op, tree);
|
|
return op.row_ ? op.row_ : this;
|
|
}
|
|
|
|
void PropertyRow::redraw([[maybe_unused]] const PropertyDrawContext& context)
|
|
{
|
|
|
|
}
|
|
|
|
bool PropertyRow::isFullRow(const QPropertyTree* tree) const
|
|
{
|
|
if (tree->treeStyle().fullRowMode)
|
|
return true;
|
|
if (parent() && parent()->isContainer())
|
|
return true;
|
|
return userFullRow();
|
|
}
|
|
|
|
QRect PropertyRow::textRect(const QPropertyTree* tree) const
|
|
{
|
|
return QRect(textPos_, pos_.y(), textSize_ < textSizeInitial_ ? textSize_ - 1 : textSize_, tree->_defaultRowHeight());
|
|
}
|
|
|
|
QRect PropertyRow::widgetRect(const QPropertyTree* tree) const
|
|
{
|
|
return QRect(widgetPos_, pos_.y(), widgetSize_, tree->_defaultRowHeight());
|
|
}
|
|
|
|
QRect PropertyRow::validatorRect([[maybe_unused]] const QPropertyTree* tree) const
|
|
{
|
|
return QRect(pos_.x() + plusSize_, pos_.y() + size_.y() - validatorsHeight_, size_.x() - plusSize_, validatorsHeight_);
|
|
}
|
|
|
|
QRect PropertyRow::validatorErrorIconRect(const QPropertyTree* tree) const
|
|
{
|
|
int rowHeight = tree->_defaultRowHeight();
|
|
int width = validatorHasErrors_ && !expanded_ ? rowHeight : 0;
|
|
int normalX = pos_.x() + size_.x() - width;
|
|
int minimalX = max(widgetPos_ + widgetSize_, textPos_ + textSize_);
|
|
return QRect(max(minimalX, normalX), pos_.y(), width, rowHeight);
|
|
}
|
|
|
|
QRect PropertyRow::validatorWarningIconRect(const QPropertyTree* tree) const
|
|
{
|
|
QRect r = validatorErrorIconRect(tree);
|
|
int width = validatorHasWarnings_ && !expanded_ ? r.height() : 0;
|
|
return QRect(r.left() - width, pos_.y(), width, r.height());
|
|
}
|
|
|
|
QRect PropertyRow::plusRect(const QPropertyTree* tree) const
|
|
{
|
|
return QRect(pos_.x(), pos_.y(), plusSize_, tree->_defaultRowHeight());
|
|
}
|
|
|
|
QRect PropertyRow::floorRect(const QPropertyTree* tree) const
|
|
{
|
|
return QRect(textPos_, pos_.y() + tree->_defaultRowHeight(), size_.x() - (textPos_ - pos_.x()) , size_.y() - tree->_defaultRowHeight());
|
|
}
|
|
|
|
void PropertyRow::setCallback(Serialization::ICallback* callback)
|
|
{
|
|
callback_ = callback;
|
|
}
|
|
|
|
SERIALIZATION_CLASS_NAME(PropertyRow, PropertyRow, "PropertyRow", "Structure");
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
EDITOR_COMMON_API PropertyRowFactory& GlobalPropertyRowFactory()
|
|
{
|
|
return PropertyRowFactory::the();
|
|
}
|
|
|
|
EDITOR_COMMON_API Serialization::ClassFactory<PropertyRow>& GlobalPropertyRowClassFactory()
|
|
{
|
|
return Serialization::ClassFactory<PropertyRow>::the();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
PropertyRowWidget::PropertyRowWidget(PropertyRow* row, QPropertyTree* tree)
|
|
: row_(row)
|
|
, model_(tree->model())
|
|
, tree_(tree)
|
|
{
|
|
}
|
|
|
|
PropertyRowWidget::~PropertyRowWidget()
|
|
{
|
|
if(actualWidget())
|
|
actualWidget()->setParent(0);
|
|
tree_->setFocus();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
Serialization::ClassFactory<PropertyRow>& GetPropertyRowClassFactory()
|
|
{
|
|
return Serialization::ClassFactory<PropertyRow>::the();
|
|
}
|
|
PropertyRowFactory& GetPropertyRowFactory()
|
|
{
|
|
return PropertyRowFactory::the();
|
|
}
|
|
|
|
int RowWidthCache::getOrUpdate(const QPropertyTree* tree, const PropertyRow* rowForValue, int extraSpace)
|
|
{
|
|
string value = rowForValue->valueAsString();
|
|
const QFont* font = rowForValue->rowFont(tree);
|
|
unsigned int newHash = calculateHash(value.c_str());
|
|
newHash = calculateHash(font, valueHash);
|
|
if (newHash != valueHash)
|
|
{
|
|
QFontMetrics fm(*font);
|
|
width = fm.horizontalAdvance(value.c_str()) + 6 + extraSpace;
|
|
if (width < 24)
|
|
width = 24;
|
|
valueHash = newHash;
|
|
}
|
|
return width;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
FORCE_SEGMENT(PropertyRowNumber)
|
|
FORCE_SEGMENT(PropertyRowStringList)
|
|
/*
|
|
FORCE_SEGMENT(PropertyRowDecorators)
|
|
FORCE_SEGMENT(PropertyRowBitVector)
|
|
FORCE_SEGMENT(PropertyRowFileSelector)
|
|
FORCE_SEGMENT(PropertyRowColor)
|
|
FORCE_SEGMENT(PropertyRowHotkey)
|
|
FORCE_SEGMENT(PropertyRowSlider)
|
|
FORCE_SEGMENT(PropertyRowIcon)
|
|
*/
|
|
#include <QPropertyTree/moc_PropertyRow.cpp>
|
|
|