/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2013, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ /* Proplist table view */ #ifndef INC_C4ConsoleQtPropListViewer #define INC_C4ConsoleQtPropListViewer #ifdef WITH_QT_EDITOR #include "C4Include.h" // needed for automoc #include "editor/C4ConsoleGUI.h" // for glew.h #include "editor/C4ConsoleQt.h" #include "editor/C4ConsoleQtShapes.h" #include "editor/C4PropertyPath.h" #include "script/C4Value.h" class C4ConsoleQtPropListModel; struct C4ConsoleQtPropListModelProperty; class C4PropertyDelegate : public QObject { Q_OBJECT protected: const class C4PropertyDelegateFactory *factory; C4Value creation_props; C4RefCntPointer set_function, async_get_function, name; C4PropertyPath::PathType set_function_type; C4RefCntPointer update_callback; public: C4PropertyDelegate(const class C4PropertyDelegateFactory *factory, C4PropList *props); ~C4PropertyDelegate() override = default; virtual void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const {}; virtual void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const {}; virtual QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const = 0; virtual void UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const; virtual bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const; virtual bool GetPropertyValueBase(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const; virtual QString GetDisplayString(const C4Value &val, class C4Object *obj, bool short_names) const; virtual QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const; virtual QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const; const char *GetSetFunction() const { return set_function.Get() ? set_function->GetCStr() : nullptr; } // get name of setter function for this property virtual const class C4PropertyDelegateShape *GetShapeDelegate(C4Value &val, C4PropertyPath *shape_path) const { return nullptr; } virtual const class C4PropertyDelegateShape *GetDirectShapeDelegate() const { return nullptr; } const char *GetUpdateCallback() const { return update_callback ? update_callback->GetCStr() : nullptr; } virtual bool HasCustomPaint() const { return false; } virtual bool Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const { return false; } virtual C4PropertyPath GetPathForProperty(struct C4ConsoleQtPropListModelProperty *editor_prop) const; C4PropertyPath GetPathForProperty(const C4PropertyPath &parent_path, const char *default_subpath) const; C4String *GetNameStr() const { return name.Get(); } const C4Value &GetCreationProps() const { return creation_props; } virtual bool IsPasteValid(const C4Value &val) const = 0; signals: void EditorValueChangedSignal(QWidget *editor) const; void EditingDoneSignal(QWidget *editor) const; }; class C4PropertyDelegateInt : public C4PropertyDelegate { private: int32_t min, max, step; public: C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, C4PropList *props); void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; bool IsPasteValid(const C4Value &val) const override; }; class C4PropertyDelegateStringEditor : public QWidget { Q_OBJECT private: QLineEdit *edit; QPushButton *localization_button; bool text_edited, commit_pending; C4Value value; char lang_code[3]; C4Value base_proplist; std::unique_ptr localization_dialogue; void OpenLocalizationDialogue(); void CloseLocalizationDialogue(); void StoreEditedText(); public: C4PropertyDelegateStringEditor(QWidget *parent, bool has_localization_button); void SetValue(const C4Value &val); C4Value GetValue(); bool IsCommitPending() const { return commit_pending; } void SetCommitPending(bool to_val) { commit_pending = to_val; } signals: void EditingDoneSignal() const; }; class C4PropertyDelegateString : public C4PropertyDelegate { private: bool translatable; public: typedef C4PropertyDelegateStringEditor Editor; C4PropertyDelegateString(const class C4PropertyDelegateFactory *factory, C4PropList *props); void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; bool IsPasteValid(const C4Value &val) const override; }; // Editor: Text displaying value plus a button that opens an extended editor class C4PropertyDelegateLabelAndButtonWidget : public QWidget { Q_OBJECT public: QHBoxLayout *layout; QLabel *label; QPushButton *button; C4Value last_value; C4PropertyPath property_path; bool button_pending; C4PropertyDelegateLabelAndButtonWidget(QWidget *parent); }; class C4PropertyDelegateDescendPath : public C4PropertyDelegate { protected: C4Value info_proplist; bool edit_on_selection; C4RefCntPointer descend_path; public: typedef C4PropertyDelegateLabelAndButtonWidget Editor; C4PropertyDelegateDescendPath(const class C4PropertyDelegateFactory *factory, C4PropList *props); void SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const override; QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; }; class C4PropertyDelegateArray : public C4PropertyDelegateDescendPath { private: int32_t max_array_display; mutable C4PropertyDelegate *element_delegate; // lazy eval void ResolveElementDelegate() const; public: C4PropertyDelegateArray(const class C4PropertyDelegateFactory *factory, C4PropList *props); QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; bool IsPasteValid(const C4Value &val) const override; }; class C4PropertyDelegatePropList : public C4PropertyDelegateDescendPath { private: C4RefCntPointer display_string; public: C4PropertyDelegatePropList(const class C4PropertyDelegateFactory *factory, C4PropList *props); QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; bool IsPasteValid(const C4Value &val) const override; }; class C4PropertyDelegateEffectEditor : public QWidget { Q_OBJECT public: QHBoxLayout *layout; QPushButton *remove_button, *edit_button; C4PropertyPath property_path; C4PropertyDelegateEffectEditor(QWidget *parent); }; class C4PropertyDelegateEffect : public C4PropertyDelegate { public: typedef C4PropertyDelegateEffectEditor Editor; C4PropertyDelegateEffect(const class C4PropertyDelegateFactory *factory, C4PropList *props); void SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const override; QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; bool IsPasteValid(const C4Value &val) const override { return false; } bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const override; C4PropertyPath GetPathForProperty(C4ConsoleQtPropListModelProperty *editor_prop) const override; }; class C4PropertyDelegateColor : public C4PropertyDelegate { private: uint32_t alpha_mask; public: typedef C4PropertyDelegateLabelAndButtonWidget Editor; C4PropertyDelegateColor(const class C4PropertyDelegateFactory *factory, C4PropList *props); void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const override; QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const override; bool IsPasteValid(const C4Value &val) const override; private: void OpenColorDialogue(Editor *editor) const; }; // Display delegate for deep combo box. Handles the help tooltip showing. class C4StyledItemDelegateWithButton : public QStyledItemDelegate { Q_OBJECT public: enum ButtonType { BT_Help, BT_PlaySound, } button_type; C4StyledItemDelegateWithButton(ButtonType bt) : button_type(bt) { } public: bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; protected: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; // A combo box that can select from a model with nested elements // On click, descend into child elements class C4DeepQComboBox : public QComboBox { Q_OBJECT bool editable, manual_text_edited; QString last_edited_text; bool is_next_close_blocked; int last_popup_height; std::unique_ptr item_delegate; QSize default_icon_size; public: enum { OptionIndexRole = Qt::UserRole + 1, ObjectHighlightRole = Qt::UserRole + 2, ValueStringRole = Qt::UserRole + 3, PriorityNameSortRole = Qt::UserRole + 4, }; C4DeepQComboBox(QWidget *parent, C4StyledItemDelegateWithButton::ButtonType button_type, bool editable); void showPopup() override; void hidePopup() override; void setCurrentModelIndex(QModelIndex new_index); int32_t GetCurrentSelectionIndex(); void BlockNextCloseEvent() { is_next_close_blocked = true; }; // after item selection on a "play" button, the combo dropdown should stay open public slots: void doShowPopup() { showPopup(); } signals: void NewItemSelected(int32_t new_item); void TextChanged(const QString &new_text); protected: // event filter for view: Catch mouse clicks to descend into children bool eventFilter(QObject *obj, QEvent *event) override; }; // Widget holder class class C4PropertyDelegateEnumEditor : public QWidget { Q_OBJECT public: enum { INDEX_Custom_Value = -1 }; C4Value last_val; C4Value last_parameter_val; // Resolved parameter of last_val - assigned for shape parameters only int32_t last_selection_index; // Index of selection in model or INDEX_Custom_Value for custom value that does not resolve to an existing entry in editable enum C4PropertyPath last_get_path; C4DeepQComboBox *option_box; QHBoxLayout *layout; QWidget *parameter_widget; bool updating, option_changed, dropdown_pending; const C4PropertyDelegate *paint_parameter_delegate; // Delegate to draw over the parameter_widget if it's an empty transparent QWidget (for shape delegates) C4PropertyDelegateEnumEditor(QWidget *parent) : QWidget(parent), last_selection_index(-1), option_box(nullptr), layout(nullptr), parameter_widget(nullptr), updating(false), option_changed(false), dropdown_pending(false), paint_parameter_delegate(nullptr) { } void paintEvent(QPaintEvent *) override; }; class C4PropertyDelegateEnum : public C4PropertyDelegate { Q_OBJECT public: typedef C4PropertyDelegateEnumEditor Editor; // qmake doesn't like nested classes class Option { public: C4RefCntPointer name; // Display name in Editor enum dropdown box C4RefCntPointer short_name; // Shortened name displayed as sub-delegate C4RefCntPointer help; // Tooltip text C4RefCntPointer group; // Grouping in enum dropdown box; nested groups separated by '/' C4RefCntPointer option_key; C4RefCntPointer value_key; C4RefCntPointer sound_name; // Assigned for options that have a play button C4V_Type type{C4V_Any}; // Assume this option is set when value is of given type C4Value props; // Stored pointer to proplist defining this option C4Value value; // Value to set if this entry is selected bool force_serialization{false}; // If serialization should be forced on value C4Value value_function; // Function to be called to set value mutable C4PropertyDelegate *adelegate{nullptr}; // Delegate to display if this entry is selected (pointer owned by C4PropertyDelegateFactory) C4Value adelegate_val; // Value to resolve adelegate from // How the currently selected option is identified from the value enum StorageType { StorageNone=0, // Invalid option StorageByType=1, // Use type to identify this enum StorageByValue=2, // This option sets a constant value StorageByKey=3, // Assume value is a proplist; identify option by field option_key } storage_type{StorageNone}; int32_t priority{0}; // Custom sort order Option() = default; }; protected: virtual C4StyledItemDelegateWithButton::ButtonType GetOptionComboBoxButtonType() const { return C4StyledItemDelegateWithButton::BT_Help; } private: std::vector