node_editor_widget.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /* node_editor_widget.h - convert widget to node editor
  2. * Copyright (C) 2017-2018 caryoscelus
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #ifndef STUDIO_GENERIC_NODE_EDITOR_WIDGET_H_825E9422_9018_587A_82D1_1ECB634E35EF
  18. #define STUDIO_GENERIC_NODE_EDITOR_WIDGET_H_825E9422_9018_587A_82D1_1ECB634E35EF
  19. #include <core/std/traits.h>
  20. #include <core/util/type_info.h>
  21. #include <core/util/nullptr.h>
  22. #include <core/action_stack.h>
  23. #include "node_editor.h"
  24. namespace rainynite::studio {
  25. namespace detail {
  26. template <class C>
  27. using editing_finished_t = decltype(std::declval<C&>().editingFinished());
  28. template <class C>
  29. constexpr bool has_editing_finished_signal = std::experimental::is_detected_v<editing_finished_t, C>;
  30. }
  31. /**
  32. * Abstract template for "converting" editor widget classes into node editors.
  33. *
  34. * It is currently not used directly, but provides common base for
  35. * UntypedNodeEditorWidget and NodeEditorWidget.
  36. */
  37. template <class S, typename T>
  38. class AbstractNodeEditorWidget : public NodeEditor {
  39. public:
  40. /// Curiously-recurring pattern class argument
  41. using Self = S;
  42. /// Type to store value node in (not pointer!)
  43. using ValueNodeType = T;
  44. /**
  45. * This function should be called from subclass ctr
  46. *
  47. * It cannot be called from ctr here because this is not yet Self*
  48. */
  49. void init() {
  50. if constexpr (detail::has_editing_finished_signal<Self>) {
  51. QObject::connect(
  52. self(),
  53. &Self::editingFinished,
  54. self(),
  55. &Self::write_action
  56. );
  57. }
  58. }
  59. void node_update() override {
  60. if (value_node = dynamic_cast<ValueNodeType*>(this->get_node().get())) {
  61. self()->do_update_value(get_core_context());
  62. self()->setReadOnly(self()->is_readonly(value_node));
  63. } else {
  64. self()->setReadOnly(false);
  65. }
  66. }
  67. void time_changed(core::Time time_) override {
  68. ContextListener::time_changed(time_);
  69. if (value_node = dynamic_cast<ValueNodeType*>(this->get_node().get()))
  70. if (update_enabled)
  71. self()->do_update_value(get_core_context());
  72. }
  73. private:
  74. Self* self() {
  75. static_assert(is_base_of_v<AbstractNodeEditorWidget, Self>);
  76. return static_cast<Self*>(this);
  77. }
  78. protected:
  79. ValueNodeType* value_node = nullptr;
  80. };
  81. /**
  82. * This template turns string-editing `Widget` into node-editing widget
  83. */
  84. template <class W>
  85. class UntypedNodeEditorWidget :
  86. public AbstractNodeEditorWidget<
  87. UntypedNodeEditorWidget<W>,
  88. core::AbstractValue
  89. >,
  90. public W
  91. {
  92. public:
  93. using Widget = W;
  94. using ValueNodeType = core::AbstractValue;
  95. UntypedNodeEditorWidget(QWidget* parent = nullptr) :
  96. Widget(parent)
  97. {
  98. this->init();
  99. }
  100. /// This function is called when we should write user changes back to node
  101. void write_action() {
  102. if (this->value_node == nullptr)
  103. return;
  104. if (is_readonly(this->value_node))
  105. return; // why should this even happen?..
  106. auto s = Widget::value();
  107. any value;
  108. try {
  109. value = core::parse_primitive_type(this->value_node->get_type(), s);
  110. } catch (core::serialize::DeserializationError const&) {
  111. // TODO
  112. return;
  113. } catch (std::exception const&) {
  114. // TODO
  115. return;
  116. }
  117. auto context = no_null(this->get_context());
  118. context->action_stack()->template emplace<core::actions::ChangeValueAt>(
  119. this->get_node(),
  120. value,
  121. context->get_context()
  122. );
  123. context->action_stack()->close();
  124. }
  125. /// This function forwards update_value call
  126. void do_update_value(shared_ptr<core::Context> ctx) {
  127. this->update_value(this->value_node->get_any(std::move(ctx)));
  128. }
  129. bool is_readonly(ValueNodeType* node) const {
  130. return !node->can_set_any_at();
  131. }
  132. };
  133. /**
  134. * This template turns `Widget` into node-editing widget with specific type.
  135. */
  136. template <class W, typename T>
  137. class NodeEditorWidget :
  138. public AbstractNodeEditorWidget<
  139. NodeEditorWidget<W,T>,
  140. core::BaseValue<T>
  141. >,
  142. public W
  143. {
  144. public:
  145. using Widget = W;
  146. using ValueType = T;
  147. using ValueNodeType = core::BaseValue<T>;
  148. NodeEditorWidget(QWidget* parent = nullptr) :
  149. Widget(parent)
  150. {
  151. this->init();
  152. }
  153. void write_action() {
  154. if (this->value_node == nullptr)
  155. return;
  156. if (auto vnode = dynamic_cast<core::Value<ValueType>*>(this->value_node)) {
  157. if (vnode->mod() == Widget::value())
  158. return;
  159. }
  160. auto context = no_null(this->get_context());
  161. context->action_stack()->template emplace<core::actions::ChangeValueAt>(
  162. this->get_node(),
  163. Widget::value(),
  164. context->get_context()
  165. );
  166. context->action_stack()->close();
  167. }
  168. /// This function forwards update_value call
  169. void do_update_value(shared_ptr<core::Context> ctx) {
  170. this->update_value(this->value_node->value(std::move(ctx)));
  171. }
  172. bool is_readonly(ValueNodeType* node) const {
  173. return dynamic_cast<core::Value<ValueType>*>(node) == nullptr;
  174. }
  175. };
  176. } // namespace rainynite::studio
  177. #endif