123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /* node_editor_widget.h - convert widget to node editor
- * Copyright (C) 2017-2018 caryoscelus
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #ifndef STUDIO_GENERIC_NODE_EDITOR_WIDGET_H_825E9422_9018_587A_82D1_1ECB634E35EF
- #define STUDIO_GENERIC_NODE_EDITOR_WIDGET_H_825E9422_9018_587A_82D1_1ECB634E35EF
- #include <core/std/traits.h>
- #include <core/util/type_info.h>
- #include <core/util/nullptr.h>
- #include <core/action_stack.h>
- #include "node_editor.h"
- namespace rainynite::studio {
- namespace detail {
- template <class C>
- using editing_finished_t = decltype(std::declval<C&>().editingFinished());
- template <class C>
- constexpr bool has_editing_finished_signal = std::experimental::is_detected_v<editing_finished_t, C>;
- }
- /**
- * Abstract template for "converting" editor widget classes into node editors.
- *
- * It is currently not used directly, but provides common base for
- * UntypedNodeEditorWidget and NodeEditorWidget.
- */
- template <class S, typename T>
- class AbstractNodeEditorWidget : public NodeEditor {
- public:
- /// Curiously-recurring pattern class argument
- using Self = S;
- /// Type to store value node in (not pointer!)
- using ValueNodeType = T;
- /**
- * This function should be called from subclass ctr
- *
- * It cannot be called from ctr here because this is not yet Self*
- */
- void init() {
- if constexpr (detail::has_editing_finished_signal<Self>) {
- QObject::connect(
- self(),
- &Self::editingFinished,
- self(),
- &Self::write_action
- );
- }
- }
- void node_update() override {
- if (value_node = dynamic_cast<ValueNodeType*>(this->get_node().get())) {
- self()->do_update_value(get_core_context());
- self()->setReadOnly(self()->is_readonly(value_node));
- } else {
- self()->setReadOnly(false);
- }
- }
- void time_changed(core::Time time_) override {
- ContextListener::time_changed(time_);
- if (value_node = dynamic_cast<ValueNodeType*>(this->get_node().get()))
- if (update_enabled)
- self()->do_update_value(get_core_context());
- }
- private:
- Self* self() {
- static_assert(is_base_of_v<AbstractNodeEditorWidget, Self>);
- return static_cast<Self*>(this);
- }
- protected:
- ValueNodeType* value_node = nullptr;
- };
- /**
- * This template turns string-editing `Widget` into node-editing widget
- */
- template <class W>
- class UntypedNodeEditorWidget :
- public AbstractNodeEditorWidget<
- UntypedNodeEditorWidget<W>,
- core::AbstractValue
- >,
- public W
- {
- public:
- using Widget = W;
- using ValueNodeType = core::AbstractValue;
- UntypedNodeEditorWidget(QWidget* parent = nullptr) :
- Widget(parent)
- {
- this->init();
- }
- /// This function is called when we should write user changes back to node
- void write_action() {
- if (this->value_node == nullptr)
- return;
- if (is_readonly(this->value_node))
- return; // why should this even happen?..
- auto s = Widget::value();
- any value;
- try {
- value = core::parse_primitive_type(this->value_node->get_type(), s);
- } catch (core::serialize::DeserializationError const&) {
- // TODO
- return;
- } catch (std::exception const&) {
- // TODO
- return;
- }
- auto context = no_null(this->get_context());
- context->action_stack()->template emplace<core::actions::ChangeValueAt>(
- this->get_node(),
- value,
- context->get_context()
- );
- context->action_stack()->close();
- }
- /// This function forwards update_value call
- void do_update_value(shared_ptr<core::Context> ctx) {
- this->update_value(this->value_node->get_any(std::move(ctx)));
- }
- bool is_readonly(ValueNodeType* node) const {
- return !node->can_set_any_at();
- }
- };
- /**
- * This template turns `Widget` into node-editing widget with specific type.
- */
- template <class W, typename T>
- class NodeEditorWidget :
- public AbstractNodeEditorWidget<
- NodeEditorWidget<W,T>,
- core::BaseValue<T>
- >,
- public W
- {
- public:
- using Widget = W;
- using ValueType = T;
- using ValueNodeType = core::BaseValue<T>;
- NodeEditorWidget(QWidget* parent = nullptr) :
- Widget(parent)
- {
- this->init();
- }
- void write_action() {
- if (this->value_node == nullptr)
- return;
- if (auto vnode = dynamic_cast<core::Value<ValueType>*>(this->value_node)) {
- if (vnode->mod() == Widget::value())
- return;
- }
- auto context = no_null(this->get_context());
- context->action_stack()->template emplace<core::actions::ChangeValueAt>(
- this->get_node(),
- Widget::value(),
- context->get_context()
- );
- context->action_stack()->close();
- }
- /// This function forwards update_value call
- void do_update_value(shared_ptr<core::Context> ctx) {
- this->update_value(this->value_node->value(std::move(ctx)));
- }
- bool is_readonly(ValueNodeType* node) const {
- return dynamic_cast<core::Value<ValueType>*>(node) == nullptr;
- }
- };
- } // namespace rainynite::studio
- #endif
|