/** * wWidgets - Lightweight UI Toolkit. * Copyright (C) 2009-2011 Evgeny Andreeshchev * Alexander Kotliar * * This code is distributed under the MIT License: * http://www.opensource.org/licenses/MIT */ #include "stdafx.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 #include #include #include #include #include #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(a.red() * mk + b.red() * k), aznumeric_cast(a.green() * mk + b.green() * k), aznumeric_cast(a.blue() * mk + b.blue() * k), aznumeric_cast(a.alpha() * mk + b.alpha() * k)); } // --------------------------------------------------------------------------- template 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, 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(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::clone(ConstStringList* constStrings) const { PropertyRow::setConstStrings(constStrings); Serialization::BinOArchive oa; SharedPtr self(const_cast(this)); oa(self, "row", "Row"); Serialization::BinIArchive ia; ia.open(oa); SharedPtr clonedRow; ia(clonedRow, "row", "Row"); PropertyRow::setConstStrings(0); if (clonedRow) clonedRow->setHideChildren(hideChildren_); return clonedRow; } void PropertyRow::drawStaticText(QPainter& p, 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 >&>(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, 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(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(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(childrenLeft + tree->treeStyle().firstLevelIndent * tree->_defaultRowHeight()); } else childrenLeft = aznumeric_cast(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 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(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(this)->find(name, nameAlt, typeName); } bool PropertyRow::processesKey(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(parent()); std::unique_ptr menuHandler = std::unique_ptr(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(parent()); if (!container) { PropertyRow* nonPulled = nonPulledParent(); if (nonPulled->parent() && nonPulled->parent()->isContainer()) container = static_cast(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(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.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(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, bool selected, 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(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 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); PropertyRowContainer* container = static_cast(parentRow); 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(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, 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, 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, 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, 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(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(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& GlobalPropertyRowClassFactory() { return Serialization::ClassFactory::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& GetPropertyRowClassFactory() { return Serialization::ClassFactory::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