Warning, file /graphics/glaxnimate/src/core/model/document_node.hpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include <unordered_set> 0010 0011 #include <QList> 0012 #include <QUuid> 0013 #include <QIcon> 0014 0015 #include "model/animation/animatable.hpp" 0016 #include "model/object.hpp" 0017 0018 namespace glaxnimate::model { 0019 0020 class Document; 0021 class ReferencePropertyBase; 0022 0023 /** 0024 * \brief Base class for elements of the document tree, that need to show in the tree view etc. 0025 */ 0026 class DocumentNode : public Object 0027 { 0028 Q_OBJECT 0029 0030 public: 0031 /** 0032 * @brief Unique identifier for the node 0033 */ 0034 GLAXNIMATE_PROPERTY_RO(QUuid, uuid, {}) 0035 /** 0036 * @brief Name of the node, used to display it in the UI 0037 */ 0038 GLAXNIMATE_PROPERTY(QString, name, "", &DocumentNode::on_name_changed) 0039 0040 protected: 0041 template<class Base> 0042 class ChildRange 0043 { 0044 public: 0045 0046 using get_func_t = Base* (Base::*) (int) const; 0047 using count_func_t = int (Base::*) () const; 0048 0049 class ChildIterator 0050 { 0051 public: 0052 using value_type = Base*; 0053 using reference = value_type*; 0054 using pointer = value_type*; 0055 using difference_type = int; 0056 using iterator_category = std::forward_iterator_tag; 0057 0058 ChildIterator& operator++() noexcept { ++index; return *this; } 0059 Base* operator->() const { return (parent->*get_func)(index); } 0060 Base* operator*() const { return (parent->*get_func)(index); } 0061 bool operator==(const ChildIterator& oth) const noexcept 0062 { 0063 return parent == oth.parent && index == oth.index; 0064 } 0065 bool operator!=(const ChildIterator& oth) const noexcept 0066 { 0067 return !(*this == oth ); 0068 } 0069 0070 private: 0071 ChildIterator(const Base* parent, int index, get_func_t get_func) noexcept 0072 : parent(parent), index(index), get_func(get_func) {} 0073 0074 const Base* parent; 0075 int index; 0076 get_func_t get_func; 0077 friend ChildRange; 0078 }; 0079 0080 ChildIterator begin() const noexcept { return ChildIterator{parent, 0, get_func}; } 0081 ChildIterator end() const noexcept { return ChildIterator{parent, size(), get_func}; } 0082 int size() const { return (parent->*count_func)(); } 0083 0084 private: 0085 ChildRange(const Base* parent, get_func_t get_func, count_func_t count_func) noexcept 0086 : parent(parent), get_func(get_func), count_func(count_func) {} 0087 const Base* parent; 0088 get_func_t get_func; 0089 count_func_t count_func; 0090 friend Base; 0091 }; 0092 0093 public: 0094 using User = ReferencePropertyBase; 0095 0096 explicit DocumentNode(model::Document* document); 0097 ~DocumentNode(); 0098 0099 virtual QIcon tree_icon() const = 0; 0100 0101 virtual DocumentNode* docnode_parent() const; 0102 virtual int docnode_child_count() const = 0; 0103 virtual DocumentNode* docnode_child(int index) const = 0; 0104 virtual int docnode_child_index(DocumentNode* dn) const = 0; 0105 0106 virtual QIcon instance_icon() const = 0; 0107 0108 ChildRange<DocumentNode> docnode_children() const noexcept 0109 { 0110 return ChildRange<DocumentNode>{this, &DocumentNode::docnode_child, &DocumentNode::docnode_child_count}; 0111 } 0112 0113 template<class T=DocumentNode> 0114 T* docnode_find_by_uuid(const QUuid& uuid) 0115 { 0116 if ( this->uuid.get() == uuid && qobject_cast<T*>(this) ) 0117 return this; 0118 for ( DocumentNode* child : docnode_children() ) 0119 if ( auto found = child->docnode_find_by_uuid<T>(uuid) ) 0120 return found; 0121 return nullptr; 0122 } 0123 0124 template<class T=DocumentNode> 0125 T* docnode_find_by_name(const QString& name) 0126 { 0127 if ( this->name.get() == name && qobject_cast<T*>(this) ) 0128 return this; 0129 for ( DocumentNode* child : docnode_children() ) 0130 if ( auto found = child->docnode_find_by_name<T>(name) ) 0131 return found; 0132 return nullptr; 0133 } 0134 0135 template<class T=DocumentNode> 0136 std::vector<T*> docnode_find_by_type_name(const QString& type_name) 0137 { 0138 std::vector<T*> matches; 0139 docnode_find_impl<T>(type_name, matches); 0140 return matches; 0141 } 0142 0143 template<class T> 0144 std::vector<T*> docnode_find_by_type() 0145 { 0146 std::vector<T*> matches; 0147 docnode_find_impl<T>({}, matches); 0148 return matches; 0149 } 0150 0151 bool docnode_is_instance(const QString& type_name) const; 0152 0153 Q_INVOKABLE glaxnimate::model::DocumentNode* find_by_name(const QString& name) { return docnode_find_by_name(name); } 0154 Q_INVOKABLE glaxnimate::model::DocumentNode* find_by_uuid(const QUuid& uuid) { return docnode_find_by_uuid(uuid); } 0155 Q_INVOKABLE QVariantList find_by_type_name(const QString& type_name) 0156 { 0157 auto ob = docnode_find_by_type_name(type_name); 0158 QVariantList ret; 0159 ret.reserve(ob.size()); 0160 for ( auto o : ob ) 0161 ret.push_back(QVariant::fromValue(o)); 0162 return ret; 0163 } 0164 0165 /** 0166 * \brief Updates the name of this node and all of its children 0167 * using the "best name" document functions 0168 */ 0169 void recursive_rename(); 0170 0171 /** 0172 * \brief Recursively updates uuid 0173 */ 0174 void refresh_uuid(); 0175 0176 QString object_name() const override; 0177 0178 0179 /** 0180 * \brief List of properties referencing this node 0181 */ 0182 const std::unordered_set<User*>& users() const; 0183 0184 /** 0185 * \brief Mark \p user as referencing this object 0186 */ 0187 void add_user(User* user); 0188 0189 /** 0190 * \brief Remove a user 0191 */ 0192 void remove_user(User* user); 0193 0194 /** 0195 * \brief Signals all users that the item has been reinstated 0196 */ 0197 void attach(); 0198 0199 /** 0200 * \brief Signals all users that the item has been removed 0201 */ 0202 void detach(); 0203 0204 /** 0205 * \brief Whether this node is a descendant of \p other 0206 */ 0207 bool is_descendant_of(const model::DocumentNode* other) const; 0208 0209 protected: 0210 virtual void on_parent_changed(model::DocumentNode* old_parent, model::DocumentNode* new_parent) 0211 { 0212 Q_UNUSED(old_parent); 0213 Q_UNUSED(new_parent); 0214 } 0215 0216 virtual void on_graphics_changed() {} 0217 0218 private: 0219 template<class T=DocumentNode> 0220 void docnode_find_impl(const QString& type_name, std::vector<T*>& matches) 0221 { 0222 if ( type_name.isEmpty() || docnode_is_instance(type_name) ) 0223 { 0224 if ( auto obj = qobject_cast<T*>(this) ) 0225 matches.push_back(obj); 0226 } 0227 0228 for ( DocumentNode* child : docnode_children() ) 0229 child->docnode_find_impl<T>(type_name, matches); 0230 } 0231 0232 void removed_from_list(); 0233 void added_to_list(DocumentNode* new_parent); 0234 0235 void on_name_changed(const QString& name, const QString& old_name); 0236 0237 Q_SIGNALS: 0238 void docnode_child_add_begin(int row); 0239 void docnode_child_add_end(DocumentNode* node, int row); 0240 0241 void docnode_child_remove_begin(int row); 0242 void docnode_child_remove_end(DocumentNode* node, int row); 0243 0244 void docnode_child_move_begin(int from, int to); 0245 void docnode_child_move_end(DocumentNode* node, int from, int to); 0246 0247 void name_changed(const QString&); 0248 0249 Q_SIGNALS: 0250 void users_changed(); 0251 0252 protected: 0253 class Private; 0254 DocumentNode(model::Document* document, std::unique_ptr<Private> d); 0255 std::unique_ptr<Private> d; 0256 friend ObjectListPropertyBase; 0257 }; 0258 0259 class Modifier; 0260 0261 /** 0262 * \brief A document node that has a physical size and shape in the document 0263 */ 0264 class VisualNode : public DocumentNode 0265 { 0266 Q_OBJECT 0267 0268 /** 0269 * @brief Color of the node the tree UI to highlight grouped items 0270 * 0271 * Generally parent/child relationshitps define groups but layers can 0272 * be grouped with each other even if they are children of a composition 0273 */ 0274 GLAXNIMATE_PROPERTY(QColor, group_color, QColor(0, 0, 0, 0), &VisualNode::on_group_color_changed) 0275 /** 0276 * \brief Visible setting for this node 0277 */ 0278 GLAXNIMATE_PROPERTY(bool, visible, true, &VisualNode::on_visible_changed, {}, PropertyTraits::Visual|PropertyTraits::Hidden) 0279 /** 0280 * \brief Locked setting for this node 0281 */ 0282 GLAXNIMATE_PROPERTY(bool, locked, false, &VisualNode::docnode_locked_changed) 0283 0284 Q_PROPERTY(bool visible_recursive READ docnode_visible_recursive) 0285 Q_PROPERTY(bool locked_recursive READ docnode_locked_recursive) 0286 Q_PROPERTY(bool selectable READ docnode_selectable) 0287 0288 public: 0289 enum PaintMode 0290 { 0291 Canvas, ///< Paint everything 0292 Render ///< Recursive, but hide objects marked with render == false 0293 }; 0294 0295 explicit VisualNode(model::Document* document); 0296 0297 QColor docnode_group_color() const; 0298 0299 virtual VisualNode* docnode_group_parent() const; 0300 virtual int docnode_group_child_count() const; 0301 virtual VisualNode* docnode_group_child(int index) const; 0302 VisualNode* docnode_fuzzy_parent() const; 0303 VisualNode* docnode_visual_child(int index) const; 0304 VisualNode* docnode_visual_parent() const; 0305 0306 ChildRange<VisualNode> docnode_visual_children() const noexcept 0307 { 0308 return ChildRange<VisualNode>{this, &VisualNode::docnode_visual_child, &VisualNode::docnode_child_count}; 0309 } 0310 0311 ChildRange<VisualNode> docnode_group_children() const noexcept 0312 { 0313 return ChildRange<VisualNode>{this, &VisualNode::docnode_group_child, &VisualNode::docnode_group_child_count}; 0314 } 0315 0316 /** 0317 * \brief Bounding rect in local coordinates (current frame) 0318 */ 0319 virtual QRectF local_bounding_rect(FrameTime t) const = 0; 0320 0321 /** 0322 * \brief \b true iff this node and all of its ancestors are visible and unlocked 0323 */ 0324 bool docnode_selectable() const; 0325 /** 0326 * \brief \b true iff this node and all of its ancestors are visible 0327 */ 0328 bool docnode_visible_recursive() const; 0329 /** 0330 * \brief \b true iff this node or any of its ancestors is locked 0331 */ 0332 bool docnode_locked_recursive() const; 0333 0334 /** 0335 * \brief Transform matrix mapping points from document coordinates to local coordinates 0336 */ 0337 QTransform transform_matrix(FrameTime t) const; 0338 QTransform group_transform_matrix(FrameTime t) const; 0339 /** 0340 * \brief Transform matrix mapping points from parent coordinates to local coordinates 0341 */ 0342 virtual QTransform local_transform_matrix(FrameTime) const { return QTransform(); } 0343 0344 0345 virtual void paint(QPainter* painter, FrameTime time, PaintMode mode, model::Modifier* modifier = nullptr) const; 0346 0347 QIcon instance_icon() const override; 0348 0349 Q_SIGNALS: 0350 void docnode_visible_changed(bool); 0351 void docnode_locked_changed(bool); 0352 void docnode_visible_recursive_changed(bool); 0353 void docnode_group_color_changed(const QColor&); 0354 0355 /** 0356 * \note Do not emit directly, call propagate_bounding_rect_changed instead 0357 */ 0358 void bounding_rect_changed(); 0359 void transform_matrix_changed(const QTransform& t); 0360 void group_transform_matrix_changed(const QTransform& t); 0361 void local_transform_matrix_changed(const QTransform& t); 0362 0363 private: 0364 void propagate_visible(bool visible); 0365 void on_group_color_changed(const QColor& color); 0366 0367 protected: 0368 void docnode_on_update_group(bool force = false); 0369 bool docnode_valid_color() const; 0370 void propagate_transform_matrix_changed(const QTransform& t_global, const QTransform& t_group); 0371 void propagate_bounding_rect_changed(); 0372 virtual void on_paint(QPainter*, FrameTime, PaintMode, model::Modifier*) const {} 0373 0374 private: 0375 void on_visible_changed(bool visible); 0376 0377 class Private; 0378 Private* dd() const; 0379 }; 0380 0381 } // namespace glaxnimate::model 0382 0383 Q_DECLARE_METATYPE(std::vector<glaxnimate::model::DocumentNode*>)