Loading...
Searching...
No Matches
OngoingCommandDispatcher.hpp
1#pragma once
2#include <score/command/Dispatchers/ICommandDispatcher.hpp>
3#include <score/command/Dispatchers/SendStrategy.hpp>
4#include <score/plugins/StringFactoryKey.hpp>
5#include <score/tools/Debug.hpp>
6#include <score/tools/SafeCast.hpp>
7
8#include <QObject>
9
10#include <memory>
11
28class SCORE_LIB_BASE_EXPORT OngoingCommandDispatcher final : public ICommandDispatcher
29{
30public:
33
34 // In Document.cpp
35 void watch(const QObject* obj);
36 void unwatch(const QObject* obj);
37
41 bool watching(const QObject* obj) const noexcept { return m_watched == obj; }
42
45 template <typename TheCommand, typename Watched, typename... Args>
46 void submit(Watched&& watched, Args&&... args)
47 {
48 using decayed = std::remove_cvref_t<Watched>;
49 const QObject* target{};
50 if constexpr(std::is_pointer_v<decayed>)
51 {
52 static_assert(std::is_base_of_v<QObject, std::remove_cvref_t<decltype(*watched)>>);
53 target = watched;
54 }
55 else
56 {
57 static_assert(std::is_base_of_v<QObject, decayed>);
58 target = &watched;
59 }
60
61 // An interaction that never got its end -- a drag whose mouse grab the
62 // scene took away without delivering the release, a widget hidden
63 // mid-edit -- leaves its command sitting here. update() only refreshes a
64 // command's parameters and not what it points at, so carrying that one
65 // over would send this interaction's edits to the *previous* object: the
66 // widget under the mouse would move while the value went somewhere else.
67 // The stale command has already been redone, so close it properly.
68 if(m_cmd && (m_watched != target || m_cmd->key() != TheCommand::static_key()))
69 {
70 // A different command type means the two interactions are unrelated, so
71 // whoever owned the first one never finished it. That used to throw;
72 // closing it is friendlier, but it is still worth saying out loud.
73 if(m_cmd->key() != TheCommand::static_key())
74 {
75 qDebug() << "OngoingCommandDispatcher: closing unfinished"
76 << m_cmd->key().toString().c_str() << "to start"
77 << TheCommand::static_key().toString().c_str();
78 }
79 commit();
80 }
81
82 watch(target);
83
84 if(!m_cmd)
85 {
86 stack().disableActions();
87 m_cmd = std::make_unique<TheCommand>(watched, std::forward<Args>(args)...);
88 m_cmd->redo(stack().context());
89 }
90 else
91 {
92 safe_cast<TheCommand*>(m_cmd.get())->update(watched, std::forward<Args>(args)...);
93 m_cmd->redo(stack().context());
94 }
95 }
96
99 void commit();
100
102 void rollback();
103 void rollback_without_undo();
104
105private:
106 std::unique_ptr<score::Command> m_cmd;
107 const QObject* m_watched{};
108};
The ICommandDispatcher class.
Definition ICommandDispatcher.hpp:21
The OngoingCommandDispatcher class.
Definition OngoingCommandDispatcher.hpp:29
void submit(Watched &&watched, Args &&... args)
Definition OngoingCommandDispatcher.hpp:46
bool watching(const QObject *obj) const noexcept
Definition OngoingCommandDispatcher.hpp:41
A small abstraction layer over the score::CommandStack.
Definition CommandStackFacade.hpp:20