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*>)