1 /* 2 * OpenClonk, http://www.openclonk.org 3 * 4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ 5 * Copyright (c) 2013, The OpenClonk Team and contributors 6 * 7 * Distributed under the terms of the ISC license; see accompanying file 8 * "COPYING" for details. 9 * 10 * "Clonk" is a registered trademark of Matthes Bender, used with permission. 11 * See accompanying file "TRADEMARK" for details. 12 * 13 * To redistribute this file separately, substitute the full license texts 14 * for the above references. 15 */ 16 17 /* Proplist table view */ 18 19 #ifndef INC_C4ConsoleQtPropListViewer 20 #define INC_C4ConsoleQtPropListViewer 21 #ifdef WITH_QT_EDITOR 22 23 #include "C4Include.h" // needed for automoc 24 #include "editor/C4ConsoleGUI.h" // for glew.h 25 #include "editor/C4ConsoleQt.h" 26 #include "editor/C4ConsoleQtShapes.h" 27 #include "editor/C4PropertyPath.h" 28 #include "script/C4Value.h" 29 30 class C4ConsoleQtPropListModel; 31 struct C4ConsoleQtPropListModelProperty; 32 33 class C4PropertyDelegate : public QObject 34 { 35 Q_OBJECT 36 37 protected: 38 const class C4PropertyDelegateFactory *factory; 39 C4Value creation_props; 40 C4RefCntPointer<C4String> set_function, async_get_function, name; 41 C4PropertyPath::PathType set_function_type; 42 C4RefCntPointer<C4String> update_callback; 43 44 public: 45 C4PropertyDelegate(const class C4PropertyDelegateFactory *factory, C4PropList *props); 46 ~C4PropertyDelegate() override = default; 47 SetEditorData(QWidget * editor,const C4Value & val,const C4PropertyPath & property_path)48 virtual void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const {}; SetModelData(QObject * editor,const C4PropertyPath & property_path,class C4ConsoleQtShape * prop_shape)49 virtual void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const {}; 50 virtual QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const = 0; 51 virtual void UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const; 52 virtual bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const; 53 virtual bool GetPropertyValueBase(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const; 54 virtual QString GetDisplayString(const C4Value &val, class C4Object *obj, bool short_names) const; 55 virtual QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const; 56 virtual QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const; GetSetFunction()57 const char *GetSetFunction() const { return set_function.Get() ? set_function->GetCStr() : nullptr; } // get name of setter function for this property GetShapeDelegate(C4Value & val,C4PropertyPath * shape_path)58 virtual const class C4PropertyDelegateShape *GetShapeDelegate(C4Value &val, C4PropertyPath *shape_path) const { return nullptr; } GetDirectShapeDelegate()59 virtual const class C4PropertyDelegateShape *GetDirectShapeDelegate() const { return nullptr; } GetUpdateCallback()60 const char *GetUpdateCallback() const { return update_callback ? update_callback->GetCStr() : nullptr; } HasCustomPaint()61 virtual bool HasCustomPaint() const { return false; } Paint(QPainter * painter,const QStyleOptionViewItem & option,const C4Value & val)62 virtual bool Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const { return false; } 63 virtual C4PropertyPath GetPathForProperty(struct C4ConsoleQtPropListModelProperty *editor_prop) const; 64 C4PropertyPath GetPathForProperty(const C4PropertyPath &parent_path, const char *default_subpath) const; GetNameStr()65 C4String *GetNameStr() const { return name.Get(); } GetCreationProps()66 const C4Value &GetCreationProps() const { return creation_props; } 67 virtual bool IsPasteValid(const C4Value &val) const = 0; 68 69 signals: 70 void EditorValueChangedSignal(QWidget *editor) const; 71 void EditingDoneSignal(QWidget *editor) const; 72 }; 73 74 class C4PropertyDelegateInt : public C4PropertyDelegate 75 { 76 private: 77 int32_t min, max, step; 78 public: 79 C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, C4PropList *props); 80 81 void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; 82 void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; 83 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; 84 bool IsPasteValid(const C4Value &val) const override; 85 }; 86 87 class C4PropertyDelegateStringEditor : public QWidget 88 { 89 Q_OBJECT 90 private: 91 QLineEdit *edit; 92 QPushButton *localization_button; 93 bool text_edited, commit_pending; 94 C4Value value; 95 char lang_code[3]; 96 C4Value base_proplist; 97 std::unique_ptr<class C4ConsoleQtLocalizeStringDlg> localization_dialogue; 98 99 void OpenLocalizationDialogue(); 100 void CloseLocalizationDialogue(); 101 void StoreEditedText(); 102 public: 103 C4PropertyDelegateStringEditor(QWidget *parent, bool has_localization_button); 104 void SetValue(const C4Value &val); 105 C4Value GetValue(); IsCommitPending()106 bool IsCommitPending() const { return commit_pending; } SetCommitPending(bool to_val)107 void SetCommitPending(bool to_val) { commit_pending = to_val; } 108 signals: 109 void EditingDoneSignal() const; 110 }; 111 112 class C4PropertyDelegateString : public C4PropertyDelegate 113 { 114 private: 115 bool translatable; 116 public: 117 typedef C4PropertyDelegateStringEditor Editor; 118 119 C4PropertyDelegateString(const class C4PropertyDelegateFactory *factory, C4PropList *props); 120 121 void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; 122 void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; 123 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; 124 QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; 125 bool IsPasteValid(const C4Value &val) const override; 126 }; 127 128 // Editor: Text displaying value plus a button that opens an extended editor 129 class C4PropertyDelegateLabelAndButtonWidget : public QWidget 130 { 131 Q_OBJECT 132 133 public: 134 QHBoxLayout *layout; 135 QLabel *label; 136 QPushButton *button; 137 C4Value last_value; 138 C4PropertyPath property_path; 139 bool button_pending; 140 141 C4PropertyDelegateLabelAndButtonWidget(QWidget *parent); 142 }; 143 144 class C4PropertyDelegateDescendPath : public C4PropertyDelegate 145 { 146 protected: 147 C4Value info_proplist; 148 bool edit_on_selection; 149 C4RefCntPointer<C4String> descend_path; 150 public: 151 typedef C4PropertyDelegateLabelAndButtonWidget Editor; 152 153 C4PropertyDelegateDescendPath(const class C4PropertyDelegateFactory *factory, C4PropList *props); 154 155 void SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const override; 156 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; 157 }; 158 159 class C4PropertyDelegateArray : public C4PropertyDelegateDescendPath 160 { 161 private: 162 int32_t max_array_display; 163 mutable C4PropertyDelegate *element_delegate; // lazy eval 164 165 void ResolveElementDelegate() const; 166 public: 167 C4PropertyDelegateArray(const class C4PropertyDelegateFactory *factory, C4PropList *props); 168 169 QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; 170 bool IsPasteValid(const C4Value &val) const override; 171 }; 172 173 class C4PropertyDelegatePropList : public C4PropertyDelegateDescendPath 174 { 175 private: 176 C4RefCntPointer<C4String> display_string; 177 public: 178 C4PropertyDelegatePropList(const class C4PropertyDelegateFactory *factory, C4PropList *props); 179 180 QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; 181 bool IsPasteValid(const C4Value &val) const override; 182 }; 183 184 class C4PropertyDelegateEffectEditor : public QWidget 185 { 186 Q_OBJECT 187 188 public: 189 QHBoxLayout *layout; 190 QPushButton *remove_button, *edit_button; 191 C4PropertyPath property_path; 192 193 C4PropertyDelegateEffectEditor(QWidget *parent); 194 }; 195 196 class C4PropertyDelegateEffect : public C4PropertyDelegate 197 { 198 public: 199 typedef C4PropertyDelegateEffectEditor Editor; 200 201 C4PropertyDelegateEffect(const class C4PropertyDelegateFactory *factory, C4PropList *props); 202 203 void SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const override; 204 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; 205 QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; IsPasteValid(const C4Value & val)206 bool IsPasteValid(const C4Value &val) const override { return false; } 207 bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const override; 208 C4PropertyPath GetPathForProperty(C4ConsoleQtPropListModelProperty *editor_prop) const override; 209 }; 210 211 class C4PropertyDelegateColor : public C4PropertyDelegate 212 { 213 private: 214 uint32_t alpha_mask; 215 216 public: 217 typedef C4PropertyDelegateLabelAndButtonWidget Editor; 218 219 C4PropertyDelegateColor(const class C4PropertyDelegateFactory *factory, C4PropList *props); 220 221 void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; 222 void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; 223 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; 224 QString GetDisplayString(const C4Value &v, C4Object *obj, bool short_names) const override; 225 QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const override; 226 QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const override; 227 bool IsPasteValid(const C4Value &val) const override; 228 229 private: 230 void OpenColorDialogue(Editor *editor) const; 231 }; 232 233 // Display delegate for deep combo box. Handles the help tooltip showing. 234 class C4StyledItemDelegateWithButton : public QStyledItemDelegate 235 { 236 Q_OBJECT 237 238 public: 239 enum ButtonType 240 { 241 BT_Help, 242 BT_PlaySound, 243 } button_type; 244 C4StyledItemDelegateWithButton(ButtonType bt)245 C4StyledItemDelegateWithButton(ButtonType bt) : button_type(bt) { } 246 247 public: 248 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; 249 protected: 250 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; 251 }; 252 253 // A combo box that can select from a model with nested elements 254 // On click, descend into child elements 255 class C4DeepQComboBox : public QComboBox 256 { 257 Q_OBJECT 258 259 bool editable, manual_text_edited; 260 QString last_edited_text; 261 bool is_next_close_blocked; 262 int last_popup_height; 263 std::unique_ptr<C4StyledItemDelegateWithButton> item_delegate; 264 QSize default_icon_size; 265 266 public: 267 enum 268 { 269 OptionIndexRole = Qt::UserRole + 1, 270 ObjectHighlightRole = Qt::UserRole + 2, 271 ValueStringRole = Qt::UserRole + 3, 272 PriorityNameSortRole = Qt::UserRole + 4, 273 }; 274 275 C4DeepQComboBox(QWidget *parent, C4StyledItemDelegateWithButton::ButtonType button_type, bool editable); 276 277 void showPopup() override; 278 void hidePopup() override; 279 280 void setCurrentModelIndex(QModelIndex new_index); 281 int32_t GetCurrentSelectionIndex(); BlockNextCloseEvent()282 void BlockNextCloseEvent() { is_next_close_blocked = true; }; // after item selection on a "play" button, the combo dropdown should stay open 283 284 public slots: doShowPopup()285 void doShowPopup() { showPopup(); } 286 287 signals: 288 void NewItemSelected(int32_t new_item); 289 void TextChanged(const QString &new_text); 290 291 protected: 292 // event filter for view: Catch mouse clicks to descend into children 293 bool eventFilter(QObject *obj, QEvent *event) override; 294 }; 295 296 // Widget holder class 297 class C4PropertyDelegateEnumEditor : public QWidget 298 { 299 Q_OBJECT 300 301 public: 302 enum { INDEX_Custom_Value = -1 }; 303 C4Value last_val; 304 C4Value last_parameter_val; // Resolved parameter of last_val - assigned for shape parameters only 305 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 306 C4PropertyPath last_get_path; 307 C4DeepQComboBox *option_box; 308 QHBoxLayout *layout; 309 QWidget *parameter_widget; 310 bool updating, option_changed, dropdown_pending; 311 const C4PropertyDelegate *paint_parameter_delegate; // Delegate to draw over the parameter_widget if it's an empty transparent QWidget (for shape delegates) 312 C4PropertyDelegateEnumEditor(QWidget * parent)313 C4PropertyDelegateEnumEditor(QWidget *parent) 314 : QWidget(parent), last_selection_index(-1), option_box(nullptr), layout(nullptr), parameter_widget(nullptr), 315 updating(false), option_changed(false), dropdown_pending(false), paint_parameter_delegate(nullptr) { } 316 317 void paintEvent(QPaintEvent *) override; 318 }; 319 320 class C4PropertyDelegateEnum : public C4PropertyDelegate 321 { 322 Q_OBJECT 323 324 public: 325 typedef C4PropertyDelegateEnumEditor Editor; // qmake doesn't like nested classes 326 327 class Option 328 { 329 public: 330 C4RefCntPointer<C4String> name; // Display name in Editor enum dropdown box 331 C4RefCntPointer<C4String> short_name; // Shortened name displayed as sub-delegate 332 C4RefCntPointer<C4String> help; // Tooltip text 333 C4RefCntPointer<C4String> group; // Grouping in enum dropdown box; nested groups separated by '/' 334 C4RefCntPointer<C4String> option_key; 335 C4RefCntPointer<C4String> value_key; 336 C4RefCntPointer<C4String> sound_name; // Assigned for options that have a play button 337 C4V_Type type{C4V_Any}; // Assume this option is set when value is of given type 338 C4Value props; // Stored pointer to proplist defining this option 339 C4Value value; // Value to set if this entry is selected 340 bool force_serialization{false}; // If serialization should be forced on value 341 C4Value value_function; // Function to be called to set value 342 mutable C4PropertyDelegate *adelegate{nullptr}; // Delegate to display if this entry is selected (pointer owned by C4PropertyDelegateFactory) 343 C4Value adelegate_val; // Value to resolve adelegate from 344 // How the currently selected option is identified from the value 345 enum StorageType { 346 StorageNone=0, // Invalid option 347 StorageByType=1, // Use type to identify this enum 348 StorageByValue=2, // This option sets a constant value 349 StorageByKey=3, // Assume value is a proplist; identify option by field option_key 350 } storage_type{StorageNone}; 351 int32_t priority{0}; // Custom sort order 352 353 Option() = default; 354 }; 355 356 protected: GetOptionComboBoxButtonType()357 virtual C4StyledItemDelegateWithButton::ButtonType GetOptionComboBoxButtonType() const { return C4StyledItemDelegateWithButton::BT_Help; } 358 359 private: 360 std::vector<Option> options; 361 Option default_option; 362 bool allow_editing; 363 bool sorted; 364 365 protected: 366 C4RefCntPointer<C4String> empty_name; // Override for name of empty option 367 368 protected: 369 void ClearOptions(); 370 void ReserveOptions(int32_t num); 371 QStandardItemModel *CreateOptionModel() const; 372 public: 373 C4PropertyDelegateEnum(const class C4PropertyDelegateFactory *factory, C4PropList *props, const C4ValueArray *poptions=nullptr); 374 375 void AddTypeOption(C4String *name, C4V_Type type, const C4Value &val, C4PropertyDelegate *adelegate=nullptr); 376 void AddConstOption(C4String *name, const C4Value &val, C4String *group=nullptr, C4String *sound_name=nullptr); 377 378 void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; 379 void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; 380 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; 381 QString GetDisplayString(const C4Value &val, class C4Object *obj, bool short_names) const override; 382 const class C4PropertyDelegateShape *GetShapeDelegate(C4Value &val, C4PropertyPath *shape_path) const override; // Forward to parameter of selected option HasCustomPaint()383 bool HasCustomPaint() const override { return true; } 384 bool Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const override; 385 bool IsPasteValid(const C4Value &val) const override; 386 387 private: 388 QModelIndex GetModelIndexByID(QStandardItemModel *model, QStandardItem *parent_item, int32_t id, const QModelIndex &parent) const; 389 int32_t GetOptionByValue(const C4Value &val) const; 390 void UpdateEditorParameter(C4PropertyDelegateEnum::Editor *editor, bool by_selection) const; 391 void EnsureOptionDelegateResolved(const Option &option) const; 392 void SetOptionValue(const C4PropertyPath &use_path, const C4PropertyDelegateEnum::Option &option, const C4Value &option_value) const; 393 void UpdateOptionIndex(Editor *editor, int idx, const QString *custom_text) const; 394 }; 395 396 // Select a definition 397 class C4PropertyDelegateDef : public C4PropertyDelegateEnum 398 { 399 private: 400 C4RefCntPointer<C4String> filter_property; 401 public: 402 C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, C4PropList *props); 403 bool IsPasteValid(const C4Value &val) const override; 404 405 private: 406 void AddDefinitions(class C4ConsoleQtDefinitionListModel *def_list_model, QModelIndex parent, C4String *group); 407 }; 408 409 // Select an object 410 class C4PropertyDelegateObject : public C4PropertyDelegateEnum 411 { 412 private: 413 C4RefCntPointer<C4String> filter; 414 size_t max_nearby_objects; // maximum number of objects shown in "nearby" list 415 416 C4RefCntPointer<C4String> GetObjectEntryString(C4Object *obj) const; 417 void UpdateObjectList(); 418 public: 419 C4PropertyDelegateObject(const C4PropertyDelegateFactory *factory, C4PropList *props); 420 421 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; 422 QString GetDisplayString(const C4Value &v, class C4Object *obj, bool short_names) const override; 423 bool IsPasteValid(const C4Value &val) const override; 424 }; 425 426 // Select a sound 427 class C4PropertyDelegateSound : public C4PropertyDelegateEnum 428 { 429 public: 430 C4PropertyDelegateSound(const C4PropertyDelegateFactory *factory, C4PropList *props); 431 QString GetDisplayString(const C4Value &v, class C4Object *obj, bool short_names) const override; 432 bool IsPasteValid(const C4Value &val) const override; 433 protected: GetOptionComboBoxButtonType()434 C4StyledItemDelegateWithButton::ButtonType GetOptionComboBoxButtonType() const override { return C4StyledItemDelegateWithButton::BT_PlaySound; } 435 }; 436 437 // true or false 438 class C4PropertyDelegateBool : public C4PropertyDelegateEnum 439 { 440 public: 441 C4PropertyDelegateBool(const class C4PropertyDelegateFactory *factory, C4PropList *props); 442 443 bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const override; 444 bool IsPasteValid(const C4Value &val) const override; 445 }; 446 447 // true or false depending on whether effect is present 448 class C4PropertyDelegateHasEffect : public C4PropertyDelegateBool 449 { 450 private: 451 C4RefCntPointer<C4String> effect; 452 public: 453 C4PropertyDelegateHasEffect(const class C4PropertyDelegateFactory *factory, C4PropList *props); 454 455 bool GetPropertyValue(const C4Value &container, C4String *key, int32_t index, C4Value *out_val) const override; 456 }; 457 458 // C4Value setting using an enum 459 class C4PropertyDelegateC4ValueEnum : public C4PropertyDelegateEnum 460 { 461 public: 462 C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, C4PropList *props); IsPasteValid(const C4Value & val)463 bool IsPasteValid(const C4Value &val) const override { return true; } 464 }; 465 466 class C4PropertyDelegateC4ValueInputEditor : public QWidget // TODO: Merge with C4PropertyDelegateLabelAndButtonWidget 467 { 468 Q_OBJECT 469 470 public: 471 QHBoxLayout *layout; 472 QLineEdit *edit; 473 QPushButton *extended_button; 474 bool commit_pending; 475 C4PropertyPath property_path; 476 477 C4PropertyDelegateC4ValueInputEditor(QWidget *parent); 478 }; 479 480 // C4Value setting using an input box 481 class C4PropertyDelegateC4ValueInput : public C4PropertyDelegate 482 { 483 public: 484 typedef C4PropertyDelegateC4ValueInputEditor Editor; 485 C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory * factory,C4PropList * props)486 C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory *factory, C4PropList *props) : C4PropertyDelegate(factory, props) { } 487 488 void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override; 489 void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; 490 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override; IsPasteValid(const C4Value & val)491 bool IsPasteValid(const C4Value &val) const override { return true; } 492 }; 493 494 // areas shown in viewport 495 class C4PropertyDelegateShape : public C4PropertyDelegate 496 { 497 uint32_t clr; 498 499 virtual void DoPaint(QPainter *painter, const QRect &inner_rect) const = 0; 500 public: 501 C4PropertyDelegateShape(const class C4PropertyDelegateFactory *factory, C4PropList *props); SetEditorData(QWidget * editor,const C4Value & val,const C4PropertyPath & property_path)502 void SetEditorData(QWidget *editor, const C4Value &val, const C4PropertyPath &property_path) const override { } // TODO maybe implement update? 503 void SetModelData(QObject *editor, const C4PropertyPath &property_path, class C4ConsoleQtShape *prop_shape) const override; CreateEditor(const class C4PropertyDelegateFactory * parent_delegate,QWidget * parent,const QStyleOptionViewItem & option,bool by_selection,bool is_child)504 QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection, bool is_child) const override { return nullptr; } GetShapeDelegate(C4Value & val,C4PropertyPath * shape_path)505 const C4PropertyDelegateShape *GetShapeDelegate(C4Value &val, C4PropertyPath *shape_path) const override { return this; } GetDirectShapeDelegate()506 const C4PropertyDelegateShape *GetDirectShapeDelegate() const override { return this; } HasCustomPaint()507 bool HasCustomPaint() const override { return true; } 508 bool Paint(QPainter *painter, const QStyleOptionViewItem &option, const C4Value &val) const override; GetDisplayString(const C4Value & v,class C4Object * obj,bool short_names)509 QString GetDisplayString(const C4Value &v, class C4Object *obj, bool short_names) const override { return QString(); } 510 511 virtual void ConnectSignals(C4ConsoleQtShape *shape, const C4PropertyPath &property_path) const; 512 }; 513 514 class C4PropertyDelegateRect : public C4PropertyDelegateShape 515 { 516 C4RefCntPointer<C4String> storage; 517 518 void DoPaint(QPainter *painter, const QRect &inner_rect) const override; 519 public: 520 C4PropertyDelegateRect(const class C4PropertyDelegateFactory *factory, C4PropList *props); 521 bool IsPasteValid(const C4Value &val) const override; 522 }; 523 524 class C4PropertyDelegateCircle : public C4PropertyDelegateShape 525 { 526 bool can_move_center; 527 528 void DoPaint(QPainter *painter, const QRect &inner_rect) const override; 529 public: 530 C4PropertyDelegateCircle(const class C4PropertyDelegateFactory *factory, C4PropList *props); 531 bool IsPasteValid(const C4Value &val) const override; 532 }; 533 534 class C4PropertyDelegatePoint : public C4PropertyDelegateShape 535 { 536 bool horizontal_fix{ false }; 537 bool vertical_fix{ false }; 538 539 void DoPaint(QPainter *painter, const QRect &inner_rect) const override; 540 public: 541 C4PropertyDelegatePoint(const class C4PropertyDelegateFactory *factory, C4PropList *props); 542 bool IsPasteValid(const C4Value &val) const override; 543 }; 544 545 class C4PropertyDelegateGraph : public C4PropertyDelegateShape 546 { 547 bool horizontal_fix = false; 548 bool vertical_fix = false; 549 bool structure_fix = false; 550 551 void DoPaint(QPainter *painter, const QRect &inner_rect) const override; 552 protected: 553 bool IsVertexPasteValid(const C4Value &val) const; 554 bool IsEdgePasteValid(const C4Value &val) const; 555 public: 556 C4PropertyDelegateGraph(const class C4PropertyDelegateFactory *factory, C4PropList *props); 557 bool IsPasteValid(const C4Value &val) const override; 558 559 void ConnectSignals(C4ConsoleQtShape *shape, const C4PropertyPath &property_path) const override; 560 }; 561 562 class C4PropertyDelegatePolyline : public C4PropertyDelegateGraph 563 { 564 void DoPaint(QPainter *painter, const QRect &inner_rect) const override; 565 public: 566 C4PropertyDelegatePolyline(const class C4PropertyDelegateFactory *factory, C4PropList *props); 567 bool IsPasteValid(const C4Value &val) const override; 568 }; 569 570 class C4PropertyDelegatePolygon : public C4PropertyDelegateGraph 571 { 572 void DoPaint(QPainter *painter, const QRect &inner_rect) const override; 573 public: 574 C4PropertyDelegatePolygon(const class C4PropertyDelegateFactory *factory, C4PropList *props); 575 bool IsPasteValid(const C4Value &val) const override; 576 }; 577 578 class C4PropertyDelegateFactory : public QStyledItemDelegate 579 { 580 Q_OBJECT 581 582 mutable std::map<C4PropList *, std::unique_ptr<C4PropertyDelegate> > delegates; 583 mutable C4PropertyDelegateEffect effect_delegate; 584 mutable QWidget *current_editor{nullptr}; 585 mutable C4PropertyDelegate *current_editor_delegate; 586 mutable C4Value last_edited_value; 587 class C4ConsoleQtPropListModel *property_model{nullptr}; 588 class C4ConsoleQtDefinitionListModel *def_list_model; 589 590 C4PropertyDelegate *CreateDelegateByPropList(C4PropList *props) const; 591 C4PropertyDelegate *GetDelegateByIndex(const QModelIndex &index) const; 592 public: 593 C4PropertyDelegateFactory(); 594 ~C4PropertyDelegateFactory() override = default; 595 596 C4PropertyDelegate *GetDelegateByValue(const C4Value &val) const; GetEffectDelegate()597 C4PropertyDelegateEffect *GetEffectDelegate() const { return &effect_delegate; } 598 599 void ClearDelegates(); 600 void SetPropertyData(const C4PropertyDelegate *d, QObject *editor, C4ConsoleQtPropListModelProperty *editor_prop) const; SetPropertyModel(class C4ConsoleQtPropListModel * new_property_model)601 void SetPropertyModel(class C4ConsoleQtPropListModel *new_property_model) { property_model = new_property_model; } SetDefinitionListModel(class C4ConsoleQtDefinitionListModel * new_def_list_model)602 void SetDefinitionListModel(class C4ConsoleQtDefinitionListModel *new_def_list_model) { def_list_model = new_def_list_model; } GetDefinitionListModel()603 class C4ConsoleQtDefinitionListModel *GetDefinitionListModel() const { return def_list_model; } GetPropertyModel()604 class C4ConsoleQtPropListModel *GetPropertyModel() const { return property_model; } 605 void OnPropListChanged(); 606 bool CheckCurrentEditor(C4PropertyDelegate *d, QWidget *editor) const; 607 608 private: 609 void EditorValueChanged(QWidget *editor); 610 void EditingDone(QWidget *editor); 611 void CopyToClipboard(const QModelIndex &index); 612 bool PasteFromClipboard(const QModelIndex &index, bool check_only); 613 614 protected: 615 // Model callbacks forwarded to actual delegates 616 void setEditorData(QWidget *editor, const QModelIndex &index) const override; 617 void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; 618 QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; 619 void destroyEditor(QWidget *editor, const QModelIndex &index) const override; 620 void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; 621 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; 622 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; 623 bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; 624 }; 625 626 // Delegate for the name column of the property window 627 // For now, just use the default + help button 628 class C4PropertyNameDelegate : public C4StyledItemDelegateWithButton 629 { 630 Q_OBJECT 631 632 class C4ConsoleQtPropListModel *property_model{nullptr}; 633 634 public: C4PropertyNameDelegate()635 C4PropertyNameDelegate() : C4StyledItemDelegateWithButton(C4StyledItemDelegateWithButton::BT_Help) { } 636 SetPropertyModel(class C4ConsoleQtPropListModel * new_property_model)637 void SetPropertyModel(class C4ConsoleQtPropListModel *new_property_model) { property_model = new_property_model; } 638 }; 639 640 // One property in the prop list model view 641 struct C4ConsoleQtPropListModelProperty 642 { 643 C4PropertyPath property_path; 644 C4Value parent_value; 645 C4RefCntPointer<C4String> display_name; 646 C4RefCntPointer<C4String> help_text; 647 C4RefCntPointer<C4String> key; 648 C4Value delegate_info; 649 C4PropertyDelegate *delegate{nullptr}; 650 bool about_to_edit{false}; 651 int32_t priority{0}; 652 653 // Parent group index 654 int32_t group_idx{-1}; 655 656 // Each property may be connected to one shape shown in the viewport for editing 657 C4ConsoleQtShapeHolder *shape; 658 const C4PropertyDelegate *shape_delegate{nullptr}; 659 C4PropertyPath shape_property_path; 660 661 C4ConsoleQtPropListModelProperty() = default; 662 }; 663 664 // Prop list view implemented as a model view 665 class C4ConsoleQtPropListModel : public QAbstractItemModel 666 { 667 Q_OBJECT 668 669 public: 670 typedef C4ConsoleQtPropListModelProperty Property; 671 struct PropertyGroup 672 { 673 QString name; 674 std::vector<Property> props; 675 }; 676 struct TargetStackEntry // elements of the path for setting child properties 677 { 678 C4PropertyPath path; 679 // TODO: Would be nice to store only path without values and info_proplist. However, info_proplist is hard to resolve when traversing up 680 // So just keep the value for now and hope that proplists do not change during selection 681 C4Value value, info_proplist; 682 TargetStackEntryTargetStackEntry683 TargetStackEntry(const C4PropertyPath &path, const C4Value &value, const C4Value &info_proplist) 684 : path(path), value(value), info_proplist(info_proplist) {} 685 }; 686 struct EditedPath // Information about how to find currently edited element (to restore after model update) 687 { 688 C4PropertyPath target_path; 689 int32_t major_index, minor_index; 690 }; 691 private: 692 C4Value target_value; // Target value for which properties are listed (either proplist or array) 693 C4Value base_proplist; // Parent-most value, i.e. object or effect selected and framed in editor 694 C4Value info_proplist; // Proplist from which available properties are derived. May differ from target_proplist in child proplists. 695 C4PropertyPath target_path; // script path to target proplist to set values 696 std::list<TargetStackEntry> target_path_stack; // stack of target paths descended into by setting child properties 697 std::vector<PropertyGroup> property_groups; 698 QFont header_font, important_property_font; 699 C4PropertyDelegateFactory *delegate_factory; 700 QItemSelectionModel *selection_model; 701 bool layout_valid; // set to false when property numbers change 702 std::map<std::string, C4ConsoleQtShapeHolder> shapes; // shapes currently shown in editor. Indexed by get path 703 public: 704 C4ConsoleQtPropListModel(C4PropertyDelegateFactory *delegate_factory); 705 ~C4ConsoleQtPropListModel() override; 706 SetSelectionModel(QItemSelectionModel * m)707 void SetSelectionModel(QItemSelectionModel *m) { selection_model = m; } GetSelectionModel()708 QItemSelectionModel *GetSelectionModel() const { return selection_model; } 709 710 bool AddPropertyGroup(C4PropList *add_proplist, int32_t group_index, QString name, C4PropList *target_proplist, const C4PropertyPath &group_target_path, C4Object *base_object, C4String *default_selection, int32_t *default_selection_index); 711 bool AddEffectGroup(int32_t group_index, C4Object *base_object); 712 void SetBasePropList(C4PropList *new_proplist); // Clear stack and select new proplist 713 void DescendPath(const C4Value &new_value, C4PropList *new_info_proplist, const C4PropertyPath &new_path); // Add proplist to stack 714 void AscendPath(); // go back one element in target path stack 715 void UpdateValue(bool select_default); 716 void DoOnUpdateCall(const C4PropertyPath &updated_path, const C4PropertyDelegate *delegate); 717 718 private: 719 int32_t UpdateValuePropList(C4PropList *target_proplist, int32_t *default_selection_group, int32_t *default_selection_index); 720 int32_t UpdateValueArray(C4ValueArray *target_array, int32_t *default_selection_group, int32_t *default_selection_index); 721 722 signals: 723 void ProplistChanged(int32_t major_sel, int32_t minor_sel) const; 724 725 private slots: 726 void UpdateSelection(int32_t major_sel, int32_t minor_sel) const; 727 728 public: GetTargetPropList()729 class C4PropList *GetTargetPropList() const { return target_value.getPropList(); } GetTargetArray()730 class C4ValueArray *GetTargetArray() const { return target_value.getArray(); } GetBasePropList()731 class C4PropList *GetBasePropList() const { return base_proplist.getPropList(); } GetTargetPathStackSize()732 int32_t GetTargetPathStackSize() const { return target_path_stack.size(); } GetTargetPathText()733 const char *GetTargetPathText() const { return target_path.GetGetPath(); } 734 QString GetTargetPathHelp() const; 735 const char *GetTargetPathName() const; IsArray()736 bool IsArray() const { return !!target_value.getArray(); } 737 void AddArrayElement(); 738 void RemoveArrayElement(); 739 bool IsTargetReadonly() const; 740 C4ConsoleQtPropListModel::Property *GetPropByIndex(const QModelIndex &index) const; 741 class C4ConsoleQtShape *GetShapeByPropertyPath(const char *property_path); 742 743 public: 744 int rowCount(const QModelIndex & parent = QModelIndex()) const override; 745 int columnCount(const QModelIndex & parent = QModelIndex()) const override; 746 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 747 QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; 748 QModelIndex index(int row, int column, const QModelIndex &parent) const override; 749 QModelIndex parent(const QModelIndex &index) const override; 750 Qt::ItemFlags flags(const QModelIndex &index) const override; 751 Qt::DropActions supportedDropActions() const override; 752 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; 753 QStringList mimeTypes() const override; 754 QMimeData *mimeData(const QModelIndexList &indexes) const override; 755 }; 756 757 #endif // WITH_QT_EDITOR 758 #endif // INC_C4ConsoleQtPropListViewer 759