35 C4PropertyDelegate::C4PropertyDelegate(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
36 : QObject(), factory(factory), set_function_type(
C4PropertyPath::PPT_SetFunction)
61 void C4PropertyDelegate::UpdateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option)
const
63 editor->setGeometry(option.rect);
66 bool C4PropertyDelegate::GetPropertyValueBase(
const C4Value &container,
C4String *key, int32_t index,
C4Value *out_val)
const
80 bool C4PropertyDelegate::GetPropertyValue(
const C4Value &container,
C4String *key, int32_t index,
C4Value *out_val)
const
82 if (async_get_function)
87 *out_val = props->
Call(async_get_function.Get());
94 return GetPropertyValueBase(container, key, index, out_val);
98 QString C4PropertyDelegate::GetDisplayString(
const C4Value &v,
C4Object *obj,
bool short_names)
const
103 QColor C4PropertyDelegate::GetDisplayTextColor(
const C4Value &val,
class C4Object *obj)
const
108 QColor C4PropertyDelegate::GetDisplayBackgroundColor(
const C4Value &val,
class C4Object *obj)
const
113 C4PropertyPath C4PropertyDelegate::GetPathForProperty(C4ConsoleQtPropListModelProperty *editor_prop)
const
116 if (editor_prop->property_path.IsEmpty())
119 path = editor_prop->property_path;
120 return GetPathForProperty(path, editor_prop->key ? editor_prop->key->GetCStr() :
nullptr);
127 if (default_subpath && *default_subpath)
130 subpath = parent_path;
132 if (GetSetFunction())
134 subpath.
SetSetPath(parent_path, GetSetFunction(), set_function_type);
142 C4PropertyDelegateInt::C4PropertyDelegateInt(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
143 : C4PropertyDelegate(factory, props), min(
std::numeric_limits<int32_t>::min()), max(
std::numeric_limits<int32_t>::max()), step(1)
153 void C4PropertyDelegateInt::SetEditorData(QWidget *editor,
const C4Value &val,
const C4PropertyPath &property_path)
const
155 QSpinBox *spinBox =
static_cast<QSpinBox*
>(editor);
156 spinBox->setValue(val.
getInt());
159 void C4PropertyDelegateInt::SetModelData(QObject *editor,
const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape)
const
161 QSpinBox *spinBox =
static_cast<QSpinBox*
>(editor);
162 spinBox->interpretText();
164 factory->GetPropertyModel()->DoOnUpdateCall(property_path,
this);
167 QWidget *C4PropertyDelegateInt::CreateEditor(
const C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
169 QSpinBox *editor =
new QSpinBox(parent);
170 editor->setMinimum(min);
171 editor->setMaximum(max);
172 editor->setSingleStep(step);
173 connect(editor, &QSpinBox::editingFinished,
this, [editor,
this]() {
174 emit EditingDoneSignal(editor);
177 if (by_selection && is_child) editor->setFocus();
181 bool C4PropertyDelegateInt::IsPasteValid(
const C4Value &val)
const
186 return (ival >= min && ival <= max);
192 C4PropertyDelegateStringEditor::C4PropertyDelegateStringEditor(QWidget *parent,
bool has_localization_button)
193 : QWidget(parent), edit(nullptr), localization_button(nullptr), commit_pending(false), text_edited(false)
195 auto layout =
new QHBoxLayout(
this);
196 layout->setContentsMargins(0, 0, 0, 0);
197 layout->setMargin(0);
198 layout->setSpacing(0);
199 edit =
new QLineEdit(
this);
200 layout->addWidget(edit);
201 if (has_localization_button)
203 localization_button =
new QPushButton(QString(
LoadResStr(
"IDS_CNS_MORE")),
this);
204 layout->addWidget(localization_button);
205 connect(localization_button, &QPushButton::pressed,
this, [
this]() {
207 OpenLocalizationDialogue();
210 connect(edit, &QLineEdit::returnPressed,
this, [
this]() {
212 commit_pending =
true;
213 emit EditingDoneSignal();
215 connect(edit, &QLineEdit::textEdited,
this, [
this]() {
217 commit_pending =
true;
221 void C4PropertyDelegateStringEditor::OpenLocalizationDialogue()
223 if (!localization_dialogue)
239 localization_dialogue.reset(
new C4ConsoleQtLocalizeStringDlg(::
Console.GetState()->window.get(), value));
240 connect(localization_dialogue.get(), &C4ConsoleQtLocalizeStringDlg::accepted,
this, [
this]() {
244 SetValue(C4VPropList(localization_dialogue->GetTranslations()));
246 CloseLocalizationDialogue();
247 commit_pending = true;
248 emit EditingDoneSignal();
250 connect(localization_dialogue.get(), &C4ConsoleQtLocalizeStringDlg::rejected,
this, [
this]() {
251 CloseLocalizationDialogue();
253 localization_dialogue->show();
257 void C4PropertyDelegateStringEditor::CloseLocalizationDialogue()
259 if (localization_dialogue)
261 localization_dialogue->close();
262 localization_dialogue.reset();
266 void C4PropertyDelegateStringEditor::StoreEditedText()
271 QString new_value = edit->text();
272 new_value = new_value.replace(R
"(\)", R"(\\)").replace(R"(")", R"(\")");
276 if (localization_button)
278 C4PropList *value_proplist = this->value.getPropList();
288 this->value = text_value;
294 void C4PropertyDelegateStringEditor::SetValue(
const C4Value &val)
300 if (localization_button)
305 QFontMetrics fm(localization_button->font());
306 localization_button->setFixedWidth(fm.width(lang_code) + 4);
307 localization_button->setText(QString(lang_code));
313 edit->setText(QString(
s ?
s->GetCStr() :
""));
317 if (val != this->value)
337 C4Value C4PropertyDelegateStringEditor::GetValue()
345 C4PropertyDelegateString::C4PropertyDelegateString(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
346 : C4PropertyDelegate(factory, props)
354 void C4PropertyDelegateString::SetEditorData(QWidget *aeditor,
const C4Value &val,
const C4PropertyPath &property_path)
const
356 Editor *editor =
static_cast<Editor*
>(aeditor);
357 editor->SetValue(val);
360 void C4PropertyDelegateString::SetModelData(QObject *aeditor,
const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape)
const
362 Editor *editor =
static_cast<Editor*
>(aeditor);
364 if (editor->IsCommitPending())
367 factory->GetPropertyModel()->DoOnUpdateCall(property_path,
this);
368 editor->SetCommitPending(
false);
372 QWidget *C4PropertyDelegateString::CreateEditor(
const C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
374 Editor *editor =
new Editor(parent, translatable);
376 connect(editor, &Editor::EditingDoneSignal, editor, [
this, editor]() {
377 emit EditingDoneSignal(editor);
380 if (by_selection && is_child) editor->setFocus();
384 QString C4PropertyDelegateString::GetDisplayString(
const C4Value &v,
C4Object *obj,
bool short_names)
const
388 return QString(
s ?
s->GetCStr() :
"");
391 bool C4PropertyDelegateString::IsPasteValid(
const C4Value &val)
const
409 C4PropertyDelegateLabelAndButtonWidget::C4PropertyDelegateLabelAndButtonWidget(QWidget *parent)
410 : QWidget(parent), layout(nullptr), label(nullptr), button(nullptr), button_pending(false)
412 layout =
new QHBoxLayout(
this);
413 layout->setContentsMargins(0, 0, 0, 0);
414 layout->setMargin(0);
415 layout->setSpacing(0);
416 label =
new QLabel(
this);
417 QPalette palette = label->palette();
418 palette.setColor(label->foregroundRole(), palette.color(QPalette::HighlightedText));
419 palette.setColor(label->backgroundRole(), palette.color(QPalette::Highlight));
420 label->setPalette(palette);
421 layout->addWidget(label);
422 button =
new QPushButton(QString(
LoadResStr(
"IDS_CNS_MORE")),
this);
423 layout->addWidget(button);
426 setAutoFillBackground(
true);
432 C4PropertyDelegateDescendPath::C4PropertyDelegateDescendPath(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
433 : C4PropertyDelegate(factory, props), edit_on_selection(true)
443 void C4PropertyDelegateDescendPath::SetEditorData(QWidget *aeditor,
const C4Value &val,
const C4PropertyPath &property_path)
const
445 Editor *editor =
static_cast<Editor *
>(aeditor);
446 editor->label->setText(GetDisplayString(val,
nullptr,
false));
447 editor->last_value = val;
448 editor->property_path = property_path;
449 if (editor->button_pending) emit editor->button->pressed();
452 QWidget *C4PropertyDelegateDescendPath::CreateEditor(
const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
456 std::unique_ptr<Editor> peditor((editor =
new Editor(parent)));
457 connect(editor->button, &QPushButton::pressed,
this, [editor,
this]() {
461 C4Value val = editor->button_pending ? editor->last_value : editor->property_path.ResolveValue();
462 bool is_proplist = !!val.getPropList(), is_array = !!val.getArray();
463 if (is_proplist || is_array)
465 C4PropList *info_proplist = this->info_proplist.getPropList();
467 C4PropertyPath descend_property_path(editor->property_path);
468 if (is_proplist && descend_path)
471 val._getPropList()->GetPropertyByS(descend_path.Get(), &val);
475 C4PropList *info_editorprops = info_proplist->GetPropertyPropList(P_EditorProps);
476 if (info_editorprops)
478 C4Value sub_info_proplist_val;
479 info_editorprops->GetPropertyByS(descend_path.Get(), &sub_info_proplist_val);
480 info_proplist = sub_info_proplist_val.getPropList();
484 descend_property_path = C4PropertyPath(descend_property_path, descend_path->GetCStr());
487 if (!info_proplist) info_proplist = val.getPropList();
488 this->factory->GetPropertyModel()->DescendPath(val, info_proplist, descend_property_path);
489 ::Console.EditCursor.InvalidateSelection();
492 if (by_selection && edit_on_selection) editor->button_pending =
true;
493 return peditor.release();
499 C4PropertyDelegateArray::C4PropertyDelegateArray(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
500 : C4PropertyDelegateDescendPath(factory, props), max_array_display(0), element_delegate(nullptr)
508 void C4PropertyDelegateArray::ResolveElementDelegate()
const
510 if (!element_delegate)
512 C4Value element_delegate_value;
513 C4PropList *info_proplist = this->info_proplist.getPropList();
515 element_delegate = factory->GetDelegateByValue(element_delegate_value);
519 QString C4PropertyDelegateArray::GetDisplayString(
const C4Value &v,
C4Object *obj,
bool short_names)
const
522 if (!arr)
return QString(
LoadResStr(
"IDS_CNS_INVALID"));
524 ResolveElementDelegate();
525 if (max_array_display && n)
527 QString result =
"[";
528 for (int32_t i = 0; i < std::min<int32_t>(n, max_array_display); ++i)
530 if (i) result +=
",";
531 result += element_delegate->GetDisplayString(v.
_getArray()->
GetItem(i), obj,
true);
533 if (n > max_array_display) result +=
",...";
537 else if (n || !short_names)
540 return QString(
LoadResStr(
"IDS_CNS_ARRAYSHORT")).arg(n);
549 bool C4PropertyDelegateArray::IsPasteValid(
const C4Value &val)
const
553 if (!arr)
return false;
557 ResolveElementDelegate();
558 for (int32_t i = 0; i < arr->
GetSize(); ++i)
561 if (!element_delegate->IsPasteValid(item))
return false;
570 C4PropertyDelegatePropList::C4PropertyDelegatePropList(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
571 : C4PropertyDelegateDescendPath(factory, props)
579 QString C4PropertyDelegatePropList::GetDisplayString(
const C4Value &v,
C4Object *obj,
bool short_names)
const
582 if (!data)
return QString(
LoadResStr(
"IDS_CNS_INVALID"));
583 if (!display_string)
return QString(
"{...}");
584 C4PropList *info_proplist = this->info_proplist.getPropList();
587 QString result = display_string->GetCStr();
590 while ((pos0 = result.indexOf(
"{{")) >= 0)
592 pos1 = result.indexOf(
"}}", pos0+2);
595 QString substring = result.mid(pos0+2, pos1-pos0-2);
599 QString display_value;
600 if (info_editorprops)
605 C4PropertyDelegate *child_delegate = factory->GetDelegateByValue(child_delegate_val);
608 display_value = child_delegate->GetDisplayString(cv, obj,
true);
615 result.replace(pos0, pos1 - pos0 + 2, display_value);
620 bool C4PropertyDelegatePropList::IsPasteValid(
const C4Value &val)
const
624 if (!pval)
return false;
626 C4PropList *info_proplist = this->info_proplist.getPropList();
628 if (!info_editorprops)
return true;
632 for (
C4String *prop_name : properties)
636 if (!info_editorprops->
GetPropertyByS(prop_name, &child_delegate_val))
continue;
637 C4PropertyDelegate *child_delegate = factory->GetDelegateByValue(child_delegate_val);
638 if (!child_delegate)
continue;
641 if (!child_delegate->IsPasteValid(child_val))
return false;
649 C4PropertyDelegateEffectEditor::C4PropertyDelegateEffectEditor(QWidget *parent) : QWidget(parent), layout(nullptr), remove_button(nullptr), edit_button(nullptr)
651 layout =
new QHBoxLayout(
this);
652 layout->setContentsMargins(0, 0, 0, 0);
653 layout->setMargin(0);
654 layout->setSpacing(0);
655 remove_button =
new QPushButton(QString(
LoadResStr(
"IDS_CNS_REMOVE")),
this);
656 layout->addWidget(remove_button);
657 edit_button =
new QPushButton(QString(
LoadResStr(
"IDS_CNS_MORE")),
this);
658 layout->addWidget(edit_button);
660 setAutoFillBackground(
true);
663 C4PropertyDelegateEffect::C4PropertyDelegateEffect(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
664 : C4PropertyDelegate(factory, props)
668 void C4PropertyDelegateEffect::SetEditorData(QWidget *aeditor,
const C4Value &val,
const C4PropertyPath &property_path)
const
670 Editor *editor =
static_cast<Editor *
>(aeditor);
671 editor->property_path = property_path;
674 QWidget *C4PropertyDelegateEffect::CreateEditor(
const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
677 std::unique_ptr<Editor> peditor((editor =
new Editor(parent)));
679 connect(editor->remove_button, &QPushButton::pressed,
this, [editor,
this]() {
681 editor->property_path.DoCall(
"RemoveEffect(nil, nil, %s)");
682 emit EditingDoneSignal(editor);
685 connect(editor->edit_button, &QPushButton::pressed,
this, [editor,
this]() {
687 C4Value effect_val = editor->property_path.ResolveValue();
688 C4PropList *effect_proplist = effect_val.getPropList();
689 if (!effect_proplist)
692 emit EditingDoneSignal(editor);
697 this->factory->GetPropertyModel()->DescendPath(effect_val, effect_proplist, editor->property_path);
698 ::Console.EditCursor.InvalidateSelection();
701 return peditor.release();
704 QString C4PropertyDelegateEffect::GetDisplayString(
const C4Value &v,
C4Object *obj,
bool short_names)
const
712 return QString(
"t=%1, interval=%2").arg(effect->
iTime).arg(effect->
iInterval);
716 return QString(
LoadResStr(
"IDS_CNS_DEADEFFECT"));
721 return QString(
"nil");
725 bool C4PropertyDelegateEffect::GetPropertyValue(
const C4Value &container,
C4String *key, int32_t index,
C4Value *out_val)
const
728 if (!key)
return false;
733 C4PropertyPath C4PropertyDelegateEffect::GetPathForProperty(C4ConsoleQtPropListModelProperty *editor_prop)
const
736 return editor_prop->property_path;
742 C4PropertyDelegateColor::C4PropertyDelegateColor(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
743 : C4PropertyDelegate(factory, props), alpha_mask(0u)
754 uint8_t r = (background_color >> 16) & 0xff;
755 uint8_t g = (background_color >> 8) & 0xff;
756 uint8_t
b = (background_color >> 0) & 0xff;
757 int32_t lgt = r * 30 + g * 59 +
b * 11;
758 return (lgt > 16000) ? 0 : 0xffffff;
761 void C4PropertyDelegateColor::SetEditorData(QWidget *aeditor,
const C4Value &val,
const C4PropertyPath &property_path)
const
763 Editor *editor =
static_cast<Editor *
>(aeditor);
764 uint32_t background_color =
static_cast<uint32_t
>(val.
getInt()) & 0xffffff;
766 QPalette palette = editor->label->palette();
767 palette.setColor(editor->label->backgroundRole(), QColor(QRgb(background_color)));
768 palette.setColor(editor->label->foregroundRole(), QColor(QRgb(foreground_color)));
769 editor->label->setPalette(palette);
770 editor->label->setAutoFillBackground(
true);
771 editor->label->setText(GetDisplayString(val,
nullptr,
false));
772 editor->last_value = val;
775 void C4PropertyDelegateColor::SetModelData(QObject *aeditor,
const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape)
const
777 Editor *editor =
static_cast<Editor *
>(aeditor);
779 factory->GetPropertyModel()->DoOnUpdateCall(property_path,
this);
782 QWidget *C4PropertyDelegateColor::CreateEditor(
const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
785 std::unique_ptr<Editor> peditor((editor =
new Editor(parent)));
786 connect(editor->button, &QPushButton::pressed,
this, [editor,
this]() {
787 this->OpenColorDialogue(editor);
790 if (by_selection && is_child) OpenColorDialogue(editor);
791 return peditor.release();
794 QString C4PropertyDelegateColor::GetDisplayString(
const C4Value &v,
C4Object *obj,
bool short_names)
const
796 return QString(
"#%1").arg(uint32_t(v.
getInt()), 8, 16, QChar(
'0'));
799 QColor C4PropertyDelegateColor::GetDisplayTextColor(
const C4Value &val,
class C4Object *obj)
const
801 uint32_t background_color =
static_cast<uint32_t
>(val.
getInt()) & 0xffffff;
803 return QColor(foreground_color);
806 QColor C4PropertyDelegateColor::GetDisplayBackgroundColor(
const C4Value &val,
class C4Object *obj)
const
808 return static_cast<uint32_t
>(val.
getInt()) & 0xffffff;
811 bool C4PropertyDelegateColor::IsPasteValid(
const C4Value &val)
const
818 void C4PropertyDelegateColor::OpenColorDialogue(C4PropertyDelegateLabelAndButtonWidget *editor)
const
821 QColor clr = QColorDialog::getColor(QColor(editor->last_value.getInt() & (~alpha_mask)), editor, QString(), QColorDialog::ShowAlphaChannel);
822 editor->last_value.SetInt(clr.rgba() | alpha_mask);
823 this->SetEditorData(editor, editor->last_value,
C4PropertyPath());
824 emit EditingDoneSignal(editor);
830 bool C4StyledItemDelegateWithButton::editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index)
833 QEvent::Type trigger_type = (button_type == BT_Help) ?
QEvent::MouseMove : QEvent::MouseButtonPress;
834 if (event->type() == trigger_type)
836 QVariant btn = model->data(index, Qt::DecorationRole);
839 QMouseEvent *mevent =
static_cast<QMouseEvent *
>(event);
840 if (option.rect.contains(mevent->localPos().toPoint()))
842 if (mevent->localPos().x() >= option.rect.x() + option.rect.width() - option.rect.height())
849 QString tooltip_text = model->data(index, Qt::ToolTipRole).toString();
850 QToolTip::showText(mevent->globalPos(), tooltip_text);
861 return QStyledItemDelegate::editorEvent(event, model, option, index);
864 void C4StyledItemDelegateWithButton::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const
867 QStyleOptionViewItem override_option = option;
869 QStyledItemDelegate::paint(painter, override_option, index);
876 C4DeepQComboBox::C4DeepQComboBox(QWidget *parent, C4StyledItemDelegateWithButton::ButtonType button_type,
bool editable)
877 : QComboBox(parent), last_popup_height(0), is_next_close_blocked(false), editable(editable), manual_text_edited(false)
879 item_delegate = std::make_unique<C4StyledItemDelegateWithButton>(button_type);
880 QTreeView *view =
new QTreeView(
this);
881 view->setFrameShape(QFrame::NoFrame);
882 view->setSelectionBehavior(QTreeView::SelectRows);
883 view->setAllColumnsShowFocus(
true);
884 view->header()->hide();
885 view->setItemDelegate(item_delegate.get());
886 setEditable(editable);
888 connect(view, &QTreeView::expanded,
this, [
this, view](
const QModelIndex &index)
890 if (this->model() && view->parentWidget())
892 int child_row_count = this->model()->rowCount(index);
893 if (child_row_count > 0)
896 QModelIndex last_index = this->model()->index(child_row_count - 1, 0, index);
897 int needed_height = view->visualRect(last_index).bottom() - view->visualRect(index).top() + view->height() - view->parentWidget()->height() + view->visualRect(last_index).height();
898 int available_height = QApplication::desktop()->availableGeometry(view->mapToGlobal(QPoint(1, 1))).height();
899 int new_height = std::min(needed_height, available_height - 20);
900 if (view->parentWidget()->height() < new_height) view->parentWidget()->resize(view->parentWidget()->width(), (this->last_popup_height=new_height));
905 view->setMouseTracking(
true);
906 connect(view, &QTreeView::entered,
this, [
this](
const QModelIndex &index)
909 int32_t obj_number = this->model()->data(index, ObjectHighlightRole).toInt();
914 connect(
this,
static_cast<void(QComboBox::*)(
int)
>(&QComboBox::activated),
917 QModelIndex current = this->view()->currentIndex();
918 QVariant selected_data = this->model()->data(current, OptionIndexRole);
919 if (selected_data.type() == QVariant::Int)
922 manual_text_edited =
false;
924 setCurrentModelIndex(current);
925 emit NewItemSelected(selected_data.toInt());
932 connect(lineEdit(), &QLineEdit::textEdited, [
this](
const QString &
text)
934 manual_text_edited =
true;
935 last_edited_text =
text;
938 connect(lineEdit(), &QLineEdit::returnPressed, [
this]()
940 if (manual_text_edited)
942 emit TextChanged(last_edited_text);
943 manual_text_edited =
false;
946 connect(lineEdit(), &QLineEdit::editingFinished, [
this]()
948 if (manual_text_edited)
950 emit TextChanged(last_edited_text);
951 manual_text_edited =
false;
957 view->viewport()->installEventFilter(
this);
959 default_icon_size = iconSize();
960 setIconSize(QSize(0, 0));
963 void C4DeepQComboBox::showPopup()
966 setRootModelIndex(QModelIndex());
967 setIconSize(default_icon_size);
968 QComboBox::showPopup();
969 view()->setMinimumWidth(200);
970 if (last_popup_height && view()->parentWidget()) view()->parentWidget()->resize(view()->parentWidget()->width(), last_popup_height);
973 void C4DeepQComboBox::hidePopup()
977 setIconSize(QSize(0, 0));
978 QComboBox::hidePopup();
981 void C4DeepQComboBox::setCurrentModelIndex(QModelIndex new_index)
983 setRootModelIndex(new_index.parent());
984 setCurrentIndex(new_index.row());
988 lineEdit()->setText(this->model()->data(new_index, ValueStringRole).toString());
989 manual_text_edited =
false;
993 int32_t C4DeepQComboBox::GetCurrentSelectionIndex()
995 QVariant selected_data = model()->data(model()->index(currentIndex(), 0, rootModelIndex()), OptionIndexRole);
996 if (selected_data.type() == QVariant::Int)
999 return selected_data.toInt();
1009 bool C4DeepQComboBox::eventFilter(QObject *obj, QEvent *event)
1011 if (obj == view()->viewport())
1013 if (event->type() == QEvent::MouseButtonPress)
1015 QPoint pos =
static_cast<QMouseEvent *
>(event)->pos();
1016 QModelIndex pressed_index = view()->indexAt(pos);
1017 QRect item_rect = view()->visualRect(pressed_index);
1019 bool item_clicked = item_rect.contains(pos);
1022 QVariant selected_data = model()->data(pressed_index, OptionIndexRole);
1023 if (selected_data.type() != QVariant::Int)
1026 QTreeView *tview =
static_cast<QTreeView *
>(view());
1027 if (!tview->isExpanded(pressed_index))
1029 tview->setExpanded(pressed_index,
true);
1030 int32_t child_row_count = model()->rowCount(pressed_index);
1031 tview->scrollTo(model()->index(child_row_count - 1, 0, pressed_index), QAbstractItemView::EnsureVisible);
1032 tview->scrollTo(pressed_index, QAbstractItemView::EnsureVisible);
1034 is_next_close_blocked =
true;
1040 is_next_close_blocked =
true;
1045 QStyleOptionViewItem option;
1046 option.rect = view()->visualRect(pressed_index);
1047 if (item_delegate->editorEvent(event, model(), option, pressed_index))
1050 is_next_close_blocked =
true;
1054 else if (event->type() == QEvent::MouseButtonRelease)
1056 if (is_next_close_blocked)
1058 is_next_close_blocked =
false;
1063 return QComboBox::eventFilter(obj, event);
1069 void C4PropertyDelegateEnumEditor::paintEvent(QPaintEvent *ev)
1072 QWidget::paintEvent(ev);
1074 if (paint_parameter_delegate && parameter_widget)
1077 QStyleOptionViewItem view_item;
1078 view_item.rect.setTopLeft(parameter_widget->mapToParent(parameter_widget->rect().topLeft()));
1079 view_item.rect.setBottomRight(parameter_widget->mapToParent(parameter_widget->rect().bottomRight()));
1080 paint_parameter_delegate->Paint(&p, view_item, last_parameter_val);
1088 C4PropertyDelegateEnum::C4PropertyDelegateEnum(
const C4PropertyDelegateFactory *factory,
C4PropList *props,
const C4ValueArray *poptions)
1089 : C4PropertyDelegate(factory, props), allow_editing(false), sorted(false)
1093 C4String *default_option_key, *default_value_key =
nullptr;
1101 default_option.option_key = default_option_key;
1102 default_option.value_key = default_value_key;
1106 options.reserve(poptions->
GetSize());
1107 for (int32_t i = 0; i < poptions->
GetSize(); ++i)
1111 if (!props)
continue;
1113 option.props.SetPropList(props);
1119 if (!option.value_key) option.value_key = default_value_key;
1121 if (option.value.GetType() ==
C4V_Nil && empty_name) option.name = empty_name.Get();
1123 if (!option.short_name) option.short_name = option.name.Get();
1127 if (!option.option_key) option.option_key = default_option_key;
1130 option.storage_type = Option::StorageByType;
1131 else if (option.option_key && option.value.GetType() !=
C4V_Nil)
1132 option.storage_type = Option::StorageByKey;
1134 option.storage_type = Option::StorageByValue;
1139 options.push_back(option);
1144 QStandardItemModel *C4PropertyDelegateEnum::CreateOptionModel()
const
1147 std::unique_ptr<QStandardItemModel> model(
new QStandardItemModel());
1148 model->setColumnCount(1);
1150 for (
const Option &opt : options)
1152 QStandardItem *new_item = model->invisibleRootItem(), *parent =
nullptr;
1153 QStringList group_names;
1154 if (opt.group) group_names.append(QString(opt.group->GetCStr()).split(QString(
"/")));
1155 group_names.append(opt.name->GetCStr());
1156 for (
const QString &group_name : group_names)
1160 for (
int check_row_index = 0; check_row_index < new_item->rowCount(); ++check_row_index)
1161 if (new_item->child(check_row_index, 0)->text() == group_name)
1163 row_index = check_row_index;
1164 new_item = new_item->child(check_row_index, 0);
1169 QStandardItem *new_group =
new QStandardItem(group_name);
1173 new_group->setData(
"010000000"+group_name, C4DeepQComboBox::PriorityNameSortRole);
1175 new_item->appendRow(new_group);
1176 new_item = new_group;
1180 if (new_item->data(C4DeepQComboBox::OptionIndexRole).isValid())
1182 new_item =
new QStandardItem(QString(opt.name->GetCStr()));
1183 parent->appendRow(new_item);
1189 new_item->setData(QString(
FormatString(
"%09d%s", (
int)(10000000-opt.priority), opt.name->GetCStr()).
getData()), C4DeepQComboBox::PriorityNameSortRole);
1191 new_item->setData(QVariant(idx), C4DeepQComboBox::OptionIndexRole);
1192 C4Object *item_obj_data = opt.value.getObj();
1193 if (item_obj_data) new_item->setData(QVariant(item_obj_data->
Number), C4DeepQComboBox::ObjectHighlightRole);
1194 QString help = QString((opt.help ? opt.help : opt.name)->GetCStr());
1195 new_item->setData(help.replace(
'|',
'\n'), Qt::ToolTipRole);
1196 if (opt.help) new_item->setData(QIcon(
":/editor/res/Help.png"), Qt::DecorationRole);
1197 if (opt.sound_name) new_item->setData(QIcon(
":/editor/res/Sound.png"), Qt::DecorationRole);
1201 new_item->setData(QString(
s ?
s->GetCStr() :
""), C4DeepQComboBox::ValueStringRole);
1208 model->setSortRole(C4DeepQComboBox::PriorityNameSortRole);
1209 model->sort(0, Qt::AscendingOrder);
1211 return model.release();
1214 void C4PropertyDelegateEnum::ClearOptions()
1219 void C4PropertyDelegateEnum::ReserveOptions(int32_t num)
1221 options.reserve(num);
1228 option.short_name = name;
1231 option.storage_type = Option::StorageByType;
1232 option.adelegate = adelegate;
1233 options.push_back(option);
1240 option.short_name = name;
1241 option.group = group;
1243 option.storage_type = Option::StorageByValue;
1246 option.sound_name = sound_name;
1247 option.help = sound_name;
1249 options.push_back(option);
1252 int32_t C4PropertyDelegateEnum::GetOptionByValue(
const C4Value &val)
const
1256 for (
auto &option : options)
1258 switch (option.storage_type)
1260 case Option::StorageByType:
1261 match = (val.
GetTypeEx() == option.type);
1263 case Option::StorageByValue:
1264 match = (val == option.value);
1266 case Option::StorageByKey:
1269 C4PropList *def_props = option.value.getPropList();
1270 if (props && def_props)
1275 match = (defval == propval);
1285 return match ? iopt : Editor::INDEX_Custom_Value;
1288 void C4PropertyDelegateEnum::UpdateEditorParameter(C4PropertyDelegateEnum::Editor *editor,
bool by_selection)
const
1291 if (editor->parameter_widget)
1293 editor->parameter_widget->deleteLater();
1294 editor->parameter_widget =
nullptr;
1296 editor->paint_parameter_delegate =
nullptr;
1297 int32_t idx = editor->last_selection_index;
1300 idx = editor->option_box->GetCurrentSelectionIndex();
1303 if (idx < 0 || idx >= options.size())
return;
1304 const Option &option = options[idx];
1306 EnsureOptionDelegateResolved(option);
1308 if (option.adelegate)
1315 parameter_val = editor->last_val;
1320 parameter_val = option.value;
1323 SetOptionValue(editor->last_get_path, option, option.value);
1326 if (option.value_key)
1330 if (props) props->
GetPropertyByS(option.value_key.Get(), &child_val);
1331 parameter_val = child_val;
1334 editor->parameter_widget = option.adelegate->CreateEditor(factory, editor, QStyleOptionViewItem(), by_selection,
true);
1335 if (editor->parameter_widget)
1337 editor->layout->addWidget(editor->parameter_widget);
1339 if (option.value_key) delegate_value_path =
C4PropertyPath(delegate_value_path, option.value_key->GetCStr());
1340 option.adelegate->SetEditorData(editor->parameter_widget, parameter_val, delegate_value_path);
1342 connect(option.adelegate, &C4PropertyDelegate::EditorValueChangedSignal, editor->parameter_widget, [
this, editor](QWidget *changed_editor)
1344 if (changed_editor == editor->parameter_widget)
1345 if (!editor->updating)
1346 emit EditorValueChangedSignal(editor);
1348 connect(option.adelegate, &C4PropertyDelegate::EditingDoneSignal, editor->parameter_widget, [
this, editor](QWidget *changed_editor)
1350 if (changed_editor == editor->parameter_widget) emit EditingDoneSignal(editor);
1356 const C4PropertyDelegateShape *shape_delegate = option.adelegate->GetDirectShapeDelegate();
1360 editor->parameter_widget =
new QWidget(editor);
1361 editor->layout->addWidget(editor->parameter_widget);
1362 editor->parameter_widget->setAttribute(Qt::WA_NoSystemBackground);
1363 editor->parameter_widget->setAttribute(Qt::WA_TranslucentBackground);
1364 editor->parameter_widget->setAttribute(Qt::WA_TransparentForMouseEvents);
1365 editor->paint_parameter_delegate = shape_delegate;
1366 editor->last_parameter_val = parameter_val;
1372 QModelIndex C4PropertyDelegateEnum::GetModelIndexByID(QStandardItemModel *model, QStandardItem *parent_item, int32_t
id,
const QModelIndex &parent)
const
1375 for (
int row = 0; row < parent_item->rowCount(); ++row)
1377 QStandardItem *child = parent_item->child(row, 0);
1378 QVariant v = child->data(C4DeepQComboBox::OptionIndexRole);
1379 if (v.type() == QVariant::Int && v.toInt() ==
id)
return model->index(row, 0, parent);
1380 if (child->rowCount())
1382 QModelIndex child_match = GetModelIndexByID(model, child,
id, model->index(row, 0, parent));
1383 if (child_match.isValid())
return child_match;
1386 return QModelIndex();
1389 void C4PropertyDelegateEnum::SetEditorData(QWidget *aeditor,
const C4Value &val,
const C4PropertyPath &property_path)
const
1391 Editor *editor =
static_cast<Editor*
>(aeditor);
1392 editor->last_val = val;
1393 editor->last_get_path = property_path;
1394 editor->updating =
true;
1396 int32_t index = GetOptionByValue(val);
1397 if (index == Editor::INDEX_Custom_Value && !allow_editing)
1402 if (index == Editor::INDEX_Custom_Value)
1406 QString edit_string = val_string ? QString(val_string->
GetCStr()) : QString(val.GetDataString().getData());
1407 editor->option_box->setEditText(edit_string);
1412 QStandardItemModel *model =
static_cast<QStandardItemModel *
>(editor->option_box->model());
1413 editor->option_box->setCurrentModelIndex(GetModelIndexByID(model, model->invisibleRootItem(), index, QModelIndex()));
1415 editor->last_selection_index = index;
1417 UpdateEditorParameter(editor,
false);
1418 editor->updating =
false;
1420 if (editor->dropdown_pending)
1422 editor->dropdown_pending =
false;
1423 QMetaObject::invokeMethod(editor->option_box,
"doShowPopup", Qt::QueuedConnection);
1424 editor->option_box->showPopup();
1428 void C4PropertyDelegateEnum::SetModelData(QObject *aeditor,
const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape)
const
1431 Editor *editor =
static_cast<Editor*
>(aeditor);
1438 int32_t idx = editor->last_selection_index;
1439 const Option *option;
1443 option = &default_option;
1444 option_value = &editor->last_val;
1448 option = &options[std::max<int32_t>(idx, 0)];
1449 option_value = &option->value;
1453 if (option->value_key.Get())
1454 use_path =
C4PropertyPath(property_path, option->value_key->GetCStr());
1456 use_path = property_path;
1458 if (option->adelegate)
1461 if (editor->option_changed) SetOptionValue(property_path, *option, *option_value);
1464 use_path = option->adelegate->GetPathForProperty(use_path,
nullptr);
1465 option->adelegate->SetModelData(editor->parameter_widget, use_path, prop_shape);
1470 if (editor->option_changed) SetOptionValue(property_path, *option, *option_value);
1472 editor->option_changed =
false;
1475 void C4PropertyDelegateEnum::SetOptionValue(
const C4PropertyPath &use_path,
const C4PropertyDelegateEnum::Option &option,
const C4Value &option_value)
const
1481 if (option.force_serialization)
1484 if (ignore_base_props) ignore_base_props = (ignore_base_props->
IsStatic() ? ignore_base_props->
IsStatic()->
GetParent() :
nullptr);
1488 ignore_base_props = option.props.getPropList();
1497 use_path.
SetProperty(option_value, ignore_base_props_static);
1499 factory->GetPropertyModel()->DoOnUpdateCall(use_path,
this);
1502 QWidget *C4PropertyDelegateEnum::CreateEditor(
const C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
1504 Editor *editor =
new Editor(parent);
1505 editor->layout =
new QHBoxLayout(editor);
1506 editor->layout->setContentsMargins(0, 0, 0, 0);
1507 editor->layout->setMargin(0);
1508 editor->layout->setSpacing(0);
1509 editor->updating =
true;
1510 editor->option_box =
new C4DeepQComboBox(editor, GetOptionComboBoxButtonType(), allow_editing);
1511 editor->layout->addWidget(editor->option_box);
1512 for (
auto &option : options) editor->option_box->addItem(option.name->GetCStr());
1513 editor->option_box->setModel(CreateOptionModel());
1514 editor->option_box->model()->setParent(editor->option_box);
1516 connect(editor->option_box, &C4DeepQComboBox::NewItemSelected, editor, [editor,
this](int32_t newval) {
1517 if (!editor->updating) this->UpdateOptionIndex(editor, newval, nullptr); });
1521 connect(editor->option_box, &C4DeepQComboBox::TextChanged, editor, [editor,
this](
const QString &new_text) {
1522 if (!editor->updating)
1524 this->UpdateOptionIndex(editor, GetOptionByValue(C4VString(new_text.toUtf8())), &new_text);
1529 editor->updating =
false;
1531 editor->dropdown_pending = by_selection && is_child;
1535 void C4PropertyDelegateEnum::UpdateOptionIndex(C4PropertyDelegateEnum::Editor *editor,
int newval,
const QString *custom_text)
const
1537 bool has_changed =
false;
1541 C4String *last_value_string = editor->last_val.getStr();
1542 if (!last_value_string || last_value_string->
GetData() != custom_text->toUtf8())
1544 editor->last_val =
C4VString(custom_text->toUtf8());
1549 if (newval != editor->last_selection_index)
1551 editor->last_selection_index = newval;
1552 UpdateEditorParameter(editor, !custom_text);
1558 editor->option_changed =
true;
1559 emit EditorValueChangedSignal(editor);
1563 void C4PropertyDelegateEnum::EnsureOptionDelegateResolved(
const Option &option)
const
1566 if (!option.adelegate && option.adelegate_val.GetType() !=
C4V_Nil)
1567 option.adelegate = factory->GetDelegateByValue(option.adelegate_val);
1570 QString C4PropertyDelegateEnum::GetDisplayString(
const C4Value &v,
class C4Object *obj,
bool short_names)
const
1573 int32_t idx = GetOptionByValue(v);
1574 if (idx == Editor::INDEX_Custom_Value)
1580 return QString(custom_string->
GetCStr());
1584 return C4PropertyDelegate::GetDisplayString(v, obj, short_names);
1590 const Option &option = options[idx];
1591 QString result = (short_names ? option.short_name : option.name)->GetCStr();
1593 EnsureOptionDelegateResolved(option);
1594 if (option.adelegate)
1597 if (option.value_key.Get())
1603 if (!result.isEmpty()) result +=
" ";
1604 result += option.adelegate->GetDisplayString(param_val, obj, short_names);
1610 const C4PropertyDelegateShape *C4PropertyDelegateEnum::GetShapeDelegate(
C4Value &val,
C4PropertyPath *shape_path)
const
1613 int32_t option_idx = GetOptionByValue(val);
1614 if (option_idx == Editor::INDEX_Custom_Value)
return nullptr;
1615 const Option &option = options[option_idx];
1616 EnsureOptionDelegateResolved(option);
1617 if (!option.adelegate)
return nullptr;
1618 if (option.value_key.Get())
1620 *shape_path = option.adelegate->GetPathForProperty(*shape_path, option.value_key->GetCStr());
1625 return option.adelegate->GetShapeDelegate(val, shape_path);
1628 bool C4PropertyDelegateEnum::Paint(QPainter *painter,
const QStyleOptionViewItem &option,
const C4Value &val)
const
1631 int32_t option_idx = GetOptionByValue(val);
1632 if (option_idx == Editor::INDEX_Custom_Value)
return false;
1633 const Option &selected_option = options[option_idx];
1634 EnsureOptionDelegateResolved(selected_option);
1635 if (!selected_option.adelegate)
return false;
1636 if (selected_option.adelegate->HasCustomPaint())
1638 QStyleOptionViewItem parameter_option = QStyleOptionViewItem(option);
1639 parameter_option.rect.adjust(parameter_option.rect.width()/2, 0, 0, 0);
1641 if (selected_option.value_key.Get())
1643 parameter_val.
Set0();
1645 if (vp) vp->
GetPropertyByS(selected_option.value_key, ¶meter_val);
1647 selected_option.adelegate->Paint(painter, parameter_option, parameter_val);
1653 bool C4PropertyDelegateEnum::IsPasteValid(
const C4Value &val)
const
1658 int32_t option_idx = GetOptionByValue(val);
1659 if (option_idx == Editor::INDEX_Custom_Value)
return false;
1660 const Option &option = options[option_idx];
1662 EnsureOptionDelegateResolved(option);
1663 if (!option.adelegate)
return true;
1665 if (option.value_key.Get())
1668 if (!vp)
return false;
1673 parameter_val = val;
1675 return option.adelegate->IsPasteValid(parameter_val);
1681 C4PropertyDelegateDef::C4PropertyDelegateDef(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
1682 : C4PropertyDelegateEnum(factory, props)
1688 if (filter_property)
1692 std::sort(defs.begin(), defs.end(), [](
C4Def *
a,
C4Def *
b) ->
bool {
1693 return strcmp(a->GetName(), b->GetName()) < 0;
1696 for (
C4Def *def : defs)
1699 AddConstOption(option_name,
C4Value(def),
nullptr);
1705 C4ConsoleQtDefinitionListModel *def_list_model = factory->GetDefinitionListModel();
1707 AddDefinitions(def_list_model, QModelIndex(),
nullptr);
1711 void C4PropertyDelegateDef::AddDefinitions(C4ConsoleQtDefinitionListModel *def_list_model, QModelIndex parent,
C4String *group)
1713 int32_t count = def_list_model->rowCount(parent);
1714 for (int32_t i = 0; i < count; ++i)
1716 QModelIndex index = def_list_model->index(i, 0, parent);
1717 C4Def *def = def_list_model->GetDefByModelIndex(index);
1719 if (def) AddConstOption(name.
Get(),
C4Value(def), group);
1720 if (def_list_model->rowCount(index))
1727 bool C4PropertyDelegateDef::IsPasteValid(
const C4Value &val)
const
1732 if (!def)
return false;
1734 if (filter_property)
1737 if (!def->
GetPropertyByS(filter_property, &prop_val))
return false;
1738 if (!prop_val)
return false;
1746 C4PropertyDelegateObject::C4PropertyDelegateObject(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
1747 : C4PropertyDelegateEnum(factory, props), max_nearby_objects(20)
1763 void C4PropertyDelegateObject::UpdateObjectList()
1768 std::vector<C4Object *> objects;
1775 if (!filter_val)
continue;
1777 objects.push_back(obj);
1780 std::vector<C4Object *> objects_by_distance;
1784 objects_by_distance = objects;
1785 auto ObjDist = [cx, cy](
C4Object *o) {
return (o->GetX() - cx)*(o->GetX() - cx) + (o->GetY() - cy)*(o->GetY() - cy); };
1786 std::stable_sort(objects_by_distance.begin(), objects_by_distance.end(), [&ObjDist](
C4Object *
a,
C4Object *
b) { return ObjDist(a) < ObjDist(b); });
1788 size_t num_nearby = objects_by_distance.size();
1789 bool has_all_objects_list = (num_nearby > max_nearby_objects);
1790 if (has_all_objects_list) num_nearby = max_nearby_objects;
1792 ReserveOptions(1 + num_nearby + !!num_nearby + (has_all_objects_list ? objects.size() : 0));
1802 for (int32_t i = 0; i < num_nearby; ++i)
1804 C4Object *obj = objects_by_distance[i];
1805 AddConstOption(GetObjectEntryString(obj).Get(),
C4VObj(obj), nearby_group.
Get());
1808 if (has_all_objects_list)
1811 for (
C4Object *obj : objects) AddConstOption(GetObjectEntryString(obj).Get(),
C4VObj(obj), all_group.
Get());
1816 QWidget *C4PropertyDelegateObject::CreateEditor(
const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
1820 const_cast<C4PropertyDelegateObject *
>(
this)->UpdateObjectList();
1821 return C4PropertyDelegateEnum::CreateEditor(parent_delegate, parent, option, by_selection, is_child);
1824 QString C4PropertyDelegateObject::GetDisplayString(
const C4Value &v,
class C4Object *obj,
bool short_names)
const
1830 return QString(
s->GetCStr());
1838 bool C4PropertyDelegateObject::IsPasteValid(
const C4Value &val)
const
1843 if (!obj)
return false;
1849 if (!prop_val)
return false;
1857 C4PropertyDelegateSound::C4PropertyDelegateSound(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
1858 : C4PropertyDelegateEnum(factory, props)
1866 StdStrBuf full_name_s(fx->GetFullName(),
true);
1868 const char *full_name = full_name_s.getData();
1869 const char *base_name = full_name, *pos;
1871 while ((pos =
SSearch(base_name,
"::")))
1874 group_string.
Append(base_name, pos - base_name - 2);
1886 QString C4PropertyDelegateSound::GetDisplayString(
const C4Value &v,
class C4Object *obj,
bool short_names)
const
1890 return val_string ? QString(val_string->
GetCStr()) : QString(v.GetDataString().getData());
1893 bool C4PropertyDelegateSound::IsPasteValid(
const C4Value &val)
const
1904 C4PropertyDelegateBool::C4PropertyDelegateBool(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
1905 : C4PropertyDelegateEnum(factory, props)
1913 bool C4PropertyDelegateBool::GetPropertyValue(
const C4Value &container,
C4String *key, int32_t index,
C4Value *out_val)
const
1916 bool success = C4PropertyDelegateEnum::GetPropertyValue(container, key, index, out_val);
1921 bool C4PropertyDelegateBool::IsPasteValid(
const C4Value &val)
const
1931 C4PropertyDelegateHasEffect::C4PropertyDelegateHasEffect(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
1932 : C4PropertyDelegateBool(factory, props)
1937 bool C4PropertyDelegateHasEffect::GetPropertyValue(
const C4Value &container,
C4String *key, int32_t index,
C4Value *out_val)
const
1942 bool has_effect =
false;
1945 if (!strcmp(fx->GetName(), effect->GetCStr()))
1950 *out_val =
C4VBool(has_effect);
1959 C4PropertyDelegateC4ValueEnum::C4PropertyDelegateC4ValueEnum(
const C4PropertyDelegateFactory *factory,
C4PropList *props)
1960 : C4PropertyDelegateEnum(factory, props)
1979 C4PropertyDelegateC4ValueInputEditor::C4PropertyDelegateC4ValueInputEditor(QWidget *parent)
1980 : QWidget(parent), layout(nullptr), edit(nullptr), extended_button(nullptr), commit_pending(false)
1982 layout =
new QHBoxLayout(
this);
1983 layout->setContentsMargins(0, 0, 0, 0);
1984 layout->setMargin(0);
1985 layout->setSpacing(0);
1986 edit =
new QLineEdit(
this);
1987 layout->addWidget(edit);
1988 extended_button =
new QPushButton(
"...",
this);
1989 extended_button->setMaximumWidth(extended_button->fontMetrics().boundingRect(
"...").width() + 6);
1990 layout->addWidget(extended_button);
1991 extended_button->hide();
1996 void C4PropertyDelegateC4ValueInput::SetEditorData(QWidget *aeditor,
const C4Value &val,
const C4PropertyPath &property_path)
const
1998 Editor *editor =
static_cast<Editor *
>(aeditor);
2002 editor->extended_button->show();
2003 editor->property_path = property_path;
2007 editor->extended_button->hide();
2011 void C4PropertyDelegateC4ValueInput::SetModelData(QObject *aeditor,
const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape)
const
2014 Editor *editor =
static_cast<Editor *
>(aeditor);
2015 if (editor->commit_pending)
2017 property_path.
SetProperty(editor->edit->text().toUtf8());
2018 factory->GetPropertyModel()->DoOnUpdateCall(property_path,
this);
2019 editor->commit_pending =
false;
2023 QWidget *C4PropertyDelegateC4ValueInput::CreateEditor(
const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent,
const QStyleOptionViewItem &option,
bool by_selection,
bool is_child)
const
2026 Editor *editor =
new Editor(parent);
2028 connect(editor->edit, &QLineEdit::returnPressed, editor, [
this, editor]() {
2029 editor->commit_pending = true;
2030 emit EditingDoneSignal(editor);
2032 connect(editor->extended_button, &QPushButton::pressed, editor, [
this, editor]() {
2033 C4Value val = editor->property_path.ResolveValue();
2034 if (val.getPropList() || val.getArray())
2036 this->factory->GetPropertyModel()->DescendPath(val, val.getPropList(), editor->property_path);
2037 ::Console.EditCursor.InvalidateSelection();
2041 if (by_selection && is_child) editor->edit->setFocus();
2048 C4PropertyDelegateShape::C4PropertyDelegateShape(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
2049 : C4PropertyDelegate(factory, props), clr(0xffff0000)
2057 void C4PropertyDelegateShape::SetModelData(QObject *editor,
const C4PropertyPath &property_path, C4ConsoleQtShape *prop_shape)
const
2062 if (prop_shape && prop_shape->GetParentDelegate() ==
this)
2064 property_path.
SetProperty(prop_shape->GetValue());
2065 factory->GetPropertyModel()->DoOnUpdateCall(property_path,
this);
2070 bool C4PropertyDelegateShape::Paint(QPainter *painter,
const QStyleOptionViewItem &option,
const C4Value &val)
const
2073 if (option.state & QStyle::State_Selected)
2074 painter->fillRect(option.rect, option.palette.highlight());
2076 painter->fillRect(option.rect, option.palette.base());
2079 QColor frame_color = QColor(QRgb(clr & 0xffffff));
2080 int32_t width = Clamp<int32_t>(option.rect.height() / 8, 2, 6) &~1;
2081 QPen rect_pen(QBrush(frame_color), width, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
2082 painter->setPen(rect_pen);
2083 QRect inner_rect = option.rect.adjusted(width / 2, width / 2, -width / 2, -width / 2);
2084 if (inner_rect.width() > inner_rect.height())
2087 inner_rect.adjust(inner_rect.width() - inner_rect.height(), 0, 0, 0);
2090 DoPaint(painter, inner_rect);
2096 void C4PropertyDelegateShape::ConnectSignals(C4ConsoleQtShape *shape,
const C4PropertyPath &property_path)
const
2098 connect(shape, &C4ConsoleQtShape::ShapeDragged,
this, [
this, shape, property_path]() {
2099 this->SetModelData(
nullptr, property_path, shape);
2105 C4PropertyDelegateRect::C4PropertyDelegateRect(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
2106 : C4PropertyDelegateShape(factory, props)
2114 void C4PropertyDelegateRect::DoPaint(QPainter *painter,
const QRect &inner_rect)
const
2116 painter->drawRect(inner_rect);
2119 bool C4PropertyDelegateRect::IsPasteValid(
const C4Value &val)
const
2129 property_names = def_property_names[0];
2133 property_names = def_property_names[1];
2138 if (!val_proplist)
return false;
2139 for (int32_t i = 0; i < 4; ++i)
2142 if (!val_proplist->
GetProperty(property_names[i], &propval))
return false;
2151 if (!val_arr || val_arr->
GetSize() != 4)
return false;
2161 C4PropertyDelegateCircle::C4PropertyDelegateCircle(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
2162 : C4PropertyDelegateShape(factory, props)
2170 void C4PropertyDelegateCircle::DoPaint(QPainter *painter,
const QRect &inner_rect)
const
2172 painter->drawEllipse(inner_rect);
2173 if (can_move_center) painter->drawPoint(inner_rect.center());
2176 bool C4PropertyDelegateCircle::IsPasteValid(
const C4Value &val)
const
2182 if (!val_arr || val_arr->
GetSize() != 3)
return false;
2191 C4PropertyDelegatePoint::C4PropertyDelegatePoint(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
2192 : C4PropertyDelegateShape(factory, props)
2201 void C4PropertyDelegatePoint::DoPaint(QPainter *painter,
const QRect &inner_rect)
const
2203 QPoint ctr = inner_rect.center();
2204 int r = inner_rect.height() * 7 / 20;
2205 if (horizontal_fix && !vertical_fix)
2207 painter->drawLine(ctr + QPoint(0, -r), ctr + QPoint(0, +r));
2208 painter->drawLine(ctr + QPoint(-r / 2, -r), ctr + QPoint(+r / 2, -r));
2209 painter->drawLine(ctr + QPoint(-r / 2, +r), ctr + QPoint(+r / 2, +r));
2211 else if (vertical_fix && !horizontal_fix)
2213 painter->drawLine(ctr + QPoint(-r, 0), ctr + QPoint(+r, 0));
2214 painter->drawLine(ctr + QPoint(-r, -r / 2), ctr + QPoint(-r, +r / 2));
2215 painter->drawLine(ctr + QPoint(+r, -r / 2), ctr + QPoint(+r, +r / 2));
2219 if (!horizontal_fix)
2221 painter->drawLine(ctr + QPoint(-r, -r), ctr + QPoint(+r, +r));
2223 painter->drawLine(ctr + QPoint(+r, -r), ctr + QPoint(-r, +r));
2224 painter->drawEllipse(inner_rect);
2228 bool C4PropertyDelegatePoint::IsPasteValid(
const C4Value &val)
const
2232 if (!val_arr || val_arr->
GetSize() != 2)
return false;
2240 C4PropertyDelegateGraph::C4PropertyDelegateGraph(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
2241 : C4PropertyDelegateShape(factory, props)
2251 void C4PropertyDelegateGraph::DoPaint(QPainter *painter,
const QRect &inner_rect)
const
2254 QPoint ctr = inner_rect.center();
2255 int r = inner_rect.height() * 7 / 20;
2256 painter->drawLine(ctr, ctr + QPoint(-r / 2, -r));
2257 painter->drawLine(ctr, ctr + QPoint(+r / 2, -r));
2258 painter->drawLine(ctr, ctr + QPoint(0, +r));
2261 bool C4PropertyDelegateGraph::IsVertexPasteValid(
const C4Value &val)
const
2265 if (!arr || !arr->
GetSize())
return false;
2267 const int32_t n_props = 2;
2269 for (int32_t i_pt = 0; i_pt < arr->
GetSize(); ++i_pt)
2273 if (!ptp)
return false;
2274 for (
auto & property_name : property_names)
2277 if (!ptp->
GetProperty(property_name, &ptprop))
return false;
2284 bool C4PropertyDelegateGraph::IsEdgePasteValid(
const C4Value &val)
const
2289 if (!arr || !arr->
GetSize())
return false;
2291 for (int32_t i_pt = 0; i_pt < arr->
GetSize(); ++i_pt)
2296 if (!ptp)
return false;
2298 if (!pta)
return false;
2300 if (pta->
GetSize() < 2)
return false;
2305 bool C4PropertyDelegateGraph::IsPasteValid(
const C4Value &val)
const
2310 if (horizontal_fix || vertical_fix || structure_fix)
return false;
2312 const int32_t n_props = 2;
2316 if (!val_proplist)
return false;
2317 for (int32_t i = 0; i < n_props; ++i)
2319 val_proplist->
GetProperty(property_names[i], &prop_vals[i]);
2323 return IsVertexPasteValid(prop_vals[0]) && IsEdgePasteValid(prop_vals[1]);
2326 void C4PropertyDelegateGraph::ConnectSignals(C4ConsoleQtShape *shape,
const C4PropertyPath &property_path)
const
2328 C4ConsoleQtGraph *shape_graph =
static_cast<C4ConsoleQtGraph *
>(shape);
2329 connect(shape_graph, &C4ConsoleQtGraph::GraphEdit,
this, [
this, shape, property_path](
C4ControlEditGraph::Action action, int32_t index, int32_t x, int32_t y) {
2333 factory->GetPropertyModel()->DoOnUpdateCall(property_path,
this);
2335 connect(shape, &C4ConsoleQtShape::BorderSelectionChanged,
this, []() {
2345 C4PropertyDelegatePolyline::C4PropertyDelegatePolyline(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
2346 : C4PropertyDelegateGraph(factory, props)
2350 void C4PropertyDelegatePolyline::DoPaint(QPainter *painter,
const QRect &inner_rect)
const
2353 QPoint ctr = inner_rect.center();
2354 int r = inner_rect.height() * 7 / 20;
2355 painter->drawLine(ctr + QPoint(-r, +r), ctr + QPoint(-r/3, -r));
2356 painter->drawLine(ctr + QPoint(-r / 3, -r), ctr + QPoint(+r / 3, +r));
2357 painter->drawLine(ctr + QPoint(+r / 3, +r), ctr + QPoint(+r, -r));
2360 bool C4PropertyDelegatePolyline::IsPasteValid(
const C4Value &val)
const
2363 return IsVertexPasteValid(val);
2369 C4PropertyDelegatePolygon::C4PropertyDelegatePolygon(
const class C4PropertyDelegateFactory *factory,
C4PropList *props)
2370 : C4PropertyDelegateGraph(factory, props)
2374 void C4PropertyDelegatePolygon::DoPaint(QPainter *painter,
const QRect &inner_rect)
const
2377 QPoint ctr = inner_rect.center();
2378 int r = inner_rect.height() * 7 / 20;
2379 painter->drawLine(ctr + QPoint(-r * 3 / 2, +r), ctr + QPoint(-r, -r));
2380 painter->drawLine(ctr + QPoint(-r, -r), ctr + QPoint(+r * 3 / 2, -r));
2381 painter->drawLine(ctr + QPoint(+r * 3 / 2, -r), ctr + QPoint(+r, +r));
2382 painter->drawLine(ctr + QPoint(+r, +r), ctr + QPoint(-r * 3 / 2, +r));
2385 bool C4PropertyDelegatePolygon::IsPasteValid(
const C4Value &val)
const
2388 return IsVertexPasteValid(val);
2394 C4PropertyDelegateFactory::C4PropertyDelegateFactory() : effect_delegate(this, nullptr)
2399 C4PropertyDelegate *C4PropertyDelegateFactory::CreateDelegateByPropList(
C4PropList *props)
const
2407 if (str->
GetData() ==
"int")
return new C4PropertyDelegateInt(
this, props);
2408 if (str->
GetData() ==
"string")
return new C4PropertyDelegateString(
this, props);
2409 if (str->
GetData() ==
"array")
return new C4PropertyDelegateArray(
this, props);
2410 if (str->
GetData() ==
"proplist")
return new C4PropertyDelegatePropList(
this, props);
2411 if (str->
GetData() ==
"color")
return new C4PropertyDelegateColor(
this, props);
2412 if (str->
GetData() ==
"def")
return new C4PropertyDelegateDef(
this, props);
2413 if (str->
GetData() ==
"object")
return new C4PropertyDelegateObject(
this, props);
2414 if (str->
GetData() ==
"enum")
return new C4PropertyDelegateEnum(
this, props);
2415 if (str->
GetData() ==
"sound")
return new C4PropertyDelegateSound(
this, props);
2416 if (str->
GetData() ==
"bool")
return new C4PropertyDelegateBool(
this, props);
2417 if (str->
GetData() ==
"has_effect")
return new C4PropertyDelegateHasEffect(
this, props);
2418 if (str->
GetData() ==
"c4valueenum")
return new C4PropertyDelegateC4ValueEnum(
this, props);
2419 if (str->
GetData() ==
"rect")
return new C4PropertyDelegateRect(
this, props);
2420 if (str->
GetData() ==
"circle")
return new C4PropertyDelegateCircle(
this, props);
2421 if (str->
GetData() ==
"point")
return new C4PropertyDelegatePoint(
this, props);
2422 if (str->
GetData() ==
"graph")
return new C4PropertyDelegateGraph(
this, props);
2423 if (str->
GetData() ==
"polyline")
return new C4PropertyDelegatePolyline(
this, props);
2424 if (str->
GetData() ==
"polygon")
return new C4PropertyDelegatePolygon(
this, props);
2425 if (str->
GetData() ==
"any")
return new C4PropertyDelegateC4ValueInput(
this, props);
2427 LogF(
"Invalid delegate type: %s.", str->
GetCStr());
2431 return new C4PropertyDelegateC4ValueInput(
this, props);
2434 C4PropertyDelegate *C4PropertyDelegateFactory::GetDelegateByValue(
const C4Value &val)
const
2437 if (iter != delegates.end())
return iter->second.get();
2438 C4PropertyDelegate *new_delegate = CreateDelegateByPropList(val.
getPropList());
2439 delegates.insert(std::make_pair(val.
getPropList(), std::unique_ptr<C4PropertyDelegate>(new_delegate)));
2440 return new_delegate;
2443 C4PropertyDelegate *C4PropertyDelegateFactory::GetDelegateByIndex(
const QModelIndex &index)
const
2445 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2446 if (!prop)
return nullptr;
2447 if (!prop->delegate) prop->delegate = GetDelegateByValue(prop->delegate_info);
2448 return prop->delegate;
2451 void C4PropertyDelegateFactory::ClearDelegates()
2456 void C4PropertyDelegateFactory::EditorValueChanged(QWidget *editor)
2458 emit commitData(editor);
2461 void C4PropertyDelegateFactory::EditingDone(QWidget *editor)
2463 emit commitData(editor);
2467 void C4PropertyDelegateFactory::setEditorData(QWidget *editor,
const QModelIndex &index)
const
2470 C4PropertyDelegate *d = GetDelegateByIndex(index);
2471 if (!CheckCurrentEditor(d, editor))
return;
2473 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2476 d->GetPropertyValue(prop->parent_value, prop->key, index.row(), &val);
2477 if (!prop->about_to_edit && val == last_edited_value)
return;
2478 last_edited_value = val;
2479 prop->about_to_edit =
false;
2480 d->SetEditorData(editor, val, d->GetPathForProperty(prop));
2483 void C4PropertyDelegateFactory::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index)
const
2486 C4PropertyDelegate *d = GetDelegateByIndex(index);
2487 if (!CheckCurrentEditor(d, editor))
return;
2488 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2489 SetPropertyData(d, editor, prop);
2492 void C4PropertyDelegateFactory::SetPropertyData(
const C4PropertyDelegate *d, QObject *editor, C4ConsoleQtPropListModel::Property *editor_prop)
const
2495 const C4PropertyPath set_path = d->GetPathForProperty(editor_prop);
2496 d->SetModelData(editor, set_path, editor_prop->shape ? editor_prop->shape->Get() :
nullptr);
2499 QWidget *C4PropertyDelegateFactory::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const
2501 C4PropertyDelegate *d = GetDelegateByIndex(index);
2502 if (!d)
return nullptr;
2503 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2504 prop->about_to_edit =
true;
2505 QWidget *editor = d->CreateEditor(
this, parent, option,
true,
false);
2510 connect(d, &C4PropertyDelegate::EditorValueChangedSignal, editor, [editor,
this](QWidget *signal_editor) {
2511 if (signal_editor == editor)
const_cast<C4PropertyDelegateFactory *
>(
this)->EditorValueChanged(editor);
2513 connect(d, &C4PropertyDelegate::EditingDoneSignal, editor, [editor,
this](QWidget *signal_editor) {
2514 if (signal_editor == editor)
const_cast<C4PropertyDelegateFactory *
>(
this)->EditingDone(editor);
2517 current_editor = editor;
2518 current_editor_delegate = d;
2522 void C4PropertyDelegateFactory::destroyEditor(QWidget *editor,
const QModelIndex &index)
const
2524 if (editor == current_editor)
2526 current_editor =
nullptr;
2527 current_editor_delegate =
nullptr;
2530 QStyledItemDelegate::destroyEditor(editor, index);
2533 void C4PropertyDelegateFactory::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const
2535 C4PropertyDelegate *d = GetDelegateByIndex(index);
2536 if (!CheckCurrentEditor(d, editor))
return;
2537 return d->UpdateEditorGeometry(editor, option);
2540 QSize C4PropertyDelegateFactory::sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &index)
const
2542 int height = QApplication::fontMetrics().height() + 4;
2543 return QSize(100, height);
2546 void C4PropertyDelegateFactory::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const
2549 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2550 C4PropertyDelegate *d = GetDelegateByIndex(index);
2551 if (d && prop && d->HasCustomPaint())
2554 d->GetPropertyValue(prop->parent_value, prop->key, index.row(), &val);
2555 if (d->Paint(painter, option, val))
return;
2558 QStyledItemDelegate::paint(painter, option, index);
2561 void C4PropertyDelegateFactory::OnPropListChanged()
2563 if (current_editor) emit closeEditor(current_editor);
2566 bool C4PropertyDelegateFactory::CheckCurrentEditor(C4PropertyDelegate *d, QWidget *editor)
const
2568 if (!d || (editor && editor != current_editor) || d != current_editor_delegate)
2571 destroyEditor(current_editor, QModelIndex());
2577 static const QString property_mime_type(
"application/OpenClonkProperty");
2579 void C4PropertyDelegateFactory::CopyToClipboard(
const QModelIndex &index)
2582 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2583 C4PropertyDelegate *d = GetDelegateByIndex(index);
2584 if (!prop || !d)
return;
2587 d->GetPropertyValue(prop->parent_value, prop->key, index.row(), &val);
2591 QClipboard *clipboard = QApplication::clipboard();
2593 std::unique_ptr<QMimeData> data(
new QMimeData());
2594 data->setData(property_mime_type, QByteArray(data_str.getData(), data_str.getSize()));
2595 data->setText(data_str.getData());
2596 clipboard->setMimeData(data.release());
2599 bool C4PropertyDelegateFactory::PasteFromClipboard(
const QModelIndex &index,
bool check_only)
2602 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2603 C4PropertyDelegate *d = GetDelegateByIndex(index);
2604 if (!prop || !d)
return false;
2606 QClipboard *clipboard = QApplication::clipboard();
2607 const QMimeData *data = clipboard->mimeData();
2608 if (!data)
return false;
2611 if (data->hasFormat(property_mime_type))
2613 QByteArray prop_data = data->data(property_mime_type);
2614 str_data.
Copy(prop_data);
2617 if (!d->IsPasteValid(val))
return false;
2619 else if (data->hasText())
2623 QString
text = data->text();
2631 if (check_only)
return true;
2633 d->GetPathForProperty(prop).SetProperty(str_data.
getData());
2637 bool C4PropertyDelegateFactory::editorEvent(QEvent *event, QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index)
2644 if (event->type() == QEvent::Type::MouseButtonPress)
2646 QMouseEvent *mev =
static_cast<QMouseEvent *
>(event);
2647 if (mev->button() == Qt::MouseButton::RightButton)
2650 C4ConsoleQtPropListModel::Property *prop = property_model->GetPropByIndex(index);
2651 C4PropertyDelegate *d = GetDelegateByIndex(index);
2655 QMenu *context =
new QMenu(
const_cast<QWidget *
>(option.widget));
2656 QAction *copy_action =
new QAction(
LoadResStr(
"IDS_DLG_COPY"), context);
2657 QAction *paste_action =
new QAction(
LoadResStr(
"IDS_DLG_PASTE"), context);
2658 QModelIndex index_copy(index);
2659 connect(copy_action, &QAction::triggered,
this, [
this, index_copy]() {
2660 this->CopyToClipboard(index_copy);
2662 connect(paste_action, &QAction::triggered,
this, [
this, index_copy]() {
2663 this->PasteFromClipboard(index_copy,
false);
2665 paste_action->setEnabled(PasteFromClipboard(index_copy,
true));
2666 context->addAction(copy_action);
2667 context->addAction(paste_action);
2668 context->popup(mev->globalPos());
2669 context->connect(context, &QMenu::aboutToHide, context, &QWidget::deleteLater);
2671 QItemSelectionModel *sel_model = property_model->GetSelectionModel();
2672 QItemSelection new_sel;
2673 new_sel.select(model->index(index.row(), 0, index.parent()), index);
2674 sel_model->select(new_sel, QItemSelectionModel::SelectionFlag::SelectCurrent);
2679 return QStyledItemDelegate::editorEvent(event, model, option, index);
2685 C4ConsoleQtPropListModel::C4ConsoleQtPropListModel(C4PropertyDelegateFactory *delegate_factory)
2686 : delegate_factory(delegate_factory), selection_model(nullptr)
2688 header_font.setBold(
true);
2689 important_property_font.setBold(
true);
2690 connect(
this, &C4ConsoleQtPropListModel::ProplistChanged,
this, &C4ConsoleQtPropListModel::UpdateSelection, Qt::QueuedConnection);
2691 layout_valid =
false;
2694 C4ConsoleQtPropListModel::~C4ConsoleQtPropListModel() =
default;
2696 bool C4ConsoleQtPropListModel::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)
2700 if (!property_names.size())
return false;
2702 if (property_groups.size() == group_index)
2704 layout_valid =
false;
2705 property_groups.resize(group_index + 1);
2707 PropertyGroup &properties = property_groups[group_index];
2719 std::vector<PropAndKey> new_properties_resolved;
2720 new_properties_resolved.reserve(property_names.size());
2721 for (
C4String *prop_name : property_names)
2729 if (!name) name = prop_name;
2731 new_properties_resolved.emplace_back(PropAndKey({ prop, prop_name,
priority, name }));
2735 std::sort(new_properties_resolved.begin(), new_properties_resolved.end(), [](
const PropAndKey &
a,
const PropAndKey &
b) ->
bool {
2736 if (a.priority != b.priority) return a.priority > b.priority;
2737 return strcmp(a.name->GetCStr(), b.name->GetCStr()) < 0;
2740 properties.name = name;
2741 if (properties.props.size() != new_properties_resolved.size())
2743 layout_valid =
false;
2744 properties.props.resize(new_properties_resolved.size());
2746 for (int32_t i = 0; i < new_properties_resolved.size(); ++i)
2748 Property *prop = &properties.props[i];
2750 prop->parent_value.SetPropList(target_proplist);
2751 prop->property_path = group_target_path;
2753 const PropAndKey &prop_def = new_properties_resolved[i];
2754 if (default_selection == prop_def.key) *default_selection_index = i;
2756 prop->help_text =
nullptr;
2757 prop->delegate_info.Set0();
2758 prop->group_idx = group_index;
2759 prop->about_to_edit =
false;
2760 prop->key = prop_def.prop->GetPropertyStr(
P_Key);
2761 if (!prop->key) properties.props[i].key = prop_def.key;
2762 prop->display_name = prop_def.name;
2763 if (!prop->display_name) prop->display_name = prop_def.key;
2764 prop->help_text = prop_def.prop->GetPropertyStr(
P_EditorHelp);
2765 prop->priority = prop_def.priority;
2766 prop->delegate_info.SetPropList(prop_def.prop);
2767 prop->delegate = delegate_factory->GetDelegateByValue(prop->delegate_info);
2770 prop->delegate->GetPropertyValue(v_target_proplist, prop->key, 0, &v);
2772 C4PropertyPath new_shape_property_path = prop->delegate->GetPathForProperty(prop);
2773 const C4PropertyDelegateShape *new_shape_delegate = prop->delegate->GetShapeDelegate(v, &new_shape_property_path);
2774 if (new_shape_delegate != prop->shape_delegate || !(prop->shape_property_path == new_shape_property_path))
2776 prop->shape_delegate = new_shape_delegate;
2777 prop->shape_property_path = new_shape_property_path;
2778 if (new_shape_delegate)
2781 std::string shape_index = std::string(prop->shape_property_path.GetGetPath());
2782 prop->shape = &shapes[shape_index];
2783 C4ConsoleQtShape *shape = prop->shape->Get();
2786 if (shape->GetProperties() != new_shape_delegate->GetCreationProps().getPropList())
2795 shape =
::Console.
EditCursor.GetShapes()->CreateShape(base_object ? base_object : target_proplist->
GetObject(), new_shape_delegate->GetCreationProps().getPropList(), v, new_shape_delegate);
2796 new_shape_delegate->ConnectSignals(shape, prop->shape_property_path);
2797 prop->shape->Set(shape);
2798 prop->shape->SetLastValue(v);
2803 prop->shape =
nullptr;
2809 prop->shape->visit();
2811 if (!prop->shape->GetLastValue().IsIdenticalTo(v))
2813 prop->shape->Get()->SetValue(v);
2814 prop->shape->SetLastValue(v);
2821 bool C4ConsoleQtPropListModel::AddEffectGroup(int32_t group_index,
C4Object *base_object)
2825 int32_t num_effects = 0;
2826 for (
C4Effect *effect = *effect_list; effect; effect = effect->
pNext)
2831 if (!num_effects)
return false;
2833 if (property_groups.size() == group_index)
2835 layout_valid =
false;
2836 property_groups.resize(group_index + 1);
2838 PropertyGroup &properties = property_groups[group_index];
2839 if (properties.props.size() != num_effects)
2841 layout_valid =
false;
2842 properties.props.resize(num_effects);
2844 properties.name =
LoadResStr(
"IDS_CNS_EFFECTS");
2846 int32_t num_added = 0;
2847 for (
C4Effect *effect = *effect_list; effect; effect = effect->
pNext)
2851 Property *prop = &properties.props[num_added++];
2854 prop->help_text =
nullptr;
2855 prop->delegate_info.Set0();
2856 prop->group_idx = group_index;
2860 prop->delegate = delegate_factory->GetEffectDelegate();
2861 prop->shape =
nullptr;
2862 prop->shape_delegate =
nullptr;
2863 prop->shape_property_path.Clear();
2864 prop->about_to_edit =
false;
2865 prop->group_idx = group_index;
2872 void C4ConsoleQtPropListModel::SetBasePropList(
C4PropList *new_proplist)
2876 target_value.SetPropList(new_proplist);
2877 base_proplist.SetPropList(new_proplist);
2879 info_proplist.SetPropList(target_value.getObj());
2881 target_path_stack.clear();
2883 delegate_factory->OnPropListChanged();
2889 target_path_stack.emplace_back(target_path, target_value, info_proplist);
2891 target_value = new_value;
2892 info_proplist.SetPropList(new_info_proplist);
2893 target_path = new_path;
2895 delegate_factory->OnPropListChanged();
2898 void C4ConsoleQtPropListModel::AscendPath()
2903 if (!target_path_stack.size())
2905 SetBasePropList(
nullptr);
2908 TargetStackEntry entry = target_path_stack.back();
2909 target_path_stack.pop_back();
2910 if (!entry.value || !entry.info_proplist)
continue;
2912 C4Value target = entry.path.ResolveValue();
2915 target_path = entry.path;
2916 target_value = entry.value;
2917 info_proplist = entry.info_proplist;
2922 delegate_factory->OnPropListChanged();
2925 void C4ConsoleQtPropListModel::UpdateValue(
bool select_default)
2927 emit layoutAboutToBeChanged();
2929 target_value = target_path.ResolveValue();
2931 C4ConsoleQtShapeHolder::begin_visit();
2933 int32_t num_groups, default_selection_group = -1, default_selection_index = -1;
2934 switch (target_value.GetType())
2937 num_groups = UpdateValuePropList(target_value._getPropList(), &default_selection_group, &default_selection_index);
2940 num_groups = UpdateValueArray(target_value._getArray(), &default_selection_group, &default_selection_index);
2947 for (
auto iter = shapes.begin(); iter != shapes.end(); )
2949 if (!iter->second.was_visited())
2951 iter = shapes.erase(iter);
2959 if (num_groups != property_groups.size())
2961 layout_valid =
false;
2962 property_groups.resize(num_groups);
2968 layout_valid =
true;
2970 emit layoutChanged();
2971 QModelIndex topLeft = index(0, 0, QModelIndex());
2972 QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1, QModelIndex());
2973 emit dataChanged(topLeft, bottomRight);
2975 if (select_default) emit ProplistChanged(default_selection_group, default_selection_index);
2978 void C4ConsoleQtPropListModel::UpdateSelection(int32_t major_sel, int32_t minor_sel)
const
2980 if (selection_model)
2983 selection_model->clearSelection();
2986 QModelIndex sel = index(major_sel, 0, QModelIndex());
2987 if (minor_sel >= 0) sel = index(minor_sel, 0, sel);
2988 selection_model->select(sel, QItemSelectionModel::SelectCurrent);
2992 selection_model->select(QModelIndex(), QItemSelectionModel::SelectCurrent);
2997 int32_t C4ConsoleQtPropListModel::UpdateValuePropList(
C4PropList *target_proplist, int32_t *default_selection_group, int32_t *default_selection_index)
2999 assert(target_proplist);
3000 C4Object *base_obj = this->base_proplist.getObj(), *obj =
nullptr;
3001 C4PropList *info_proplist = this->info_proplist.getPropList();
3002 int32_t num_groups = 0;
3010 Property *prop =
nullptr;
3011 for (PropertyGroup &grp : property_groups)
3013 for (Property &check_prop : grp.props)
3015 if (check_prop.shape && check_prop.shape->Get() == selected_shape)
3024 if (prop && prop->delegate)
3026 C4PropList *shape_item_editorprops, *shape_item_value;
3027 C4String *shape_item_name =
nullptr;
3031 prop->delegate->GetPropertyValue(v_target_proplist, prop->key, 0, &v);
3032 C4PropertyPath shape_property_path = prop->delegate->GetPathForProperty(prop);
3033 prop->delegate->GetShapeDelegate(v, &shape_property_path);
3034 if (::
Console.
EditCursor.GetShapes()->GetSelectedShapeData(v, prop->shape_property_path, &shape_item_editorprops, &shape_item_value, &shape_item_name, &shape_item_target_path))
3036 if (AddPropertyGroup(shape_item_editorprops, num_groups, QString(shape_item_name ? shape_item_name->
GetCStr() :
"???"), shape_item_value, shape_item_target_path, obj,
nullptr,
nullptr))
3053 if (!fx->IsActive())
continue;
3054 QString name = fx->GetName();
3056 if (effect_editorprops && AddPropertyGroup(effect_editorprops, num_groups, name, fx,
C4PropertyPath(fx, obj), obj,
nullptr,
nullptr))
3061 if (obj || !info_proplist->
GetDef())
3064 if (info_editorprops)
3066 QString name = info_proplist->
GetName();
3067 if (AddPropertyGroup(info_editorprops, num_groups, name, target_proplist, target_path, base_obj, default_selection, default_selection_index))
3070 if (*default_selection_index >= 0)
3072 *default_selection_group = num_groups - 1;
3073 default_selection =
nullptr;
3085 if (AddPropertyGroup(info_editorprops, num_groups,
LoadResStr(
"IDS_CNS_OBJECT"), target_proplist, target_path, base_obj,
nullptr,
nullptr))
3092 if (property_groups.size() == num_groups) property_groups.resize(num_groups + 1);
3093 PropertyGroup &internal_properties = property_groups[num_groups];
3094 internal_properties.name =
LoadResStr(
"IDS_CNS_INTERNAL");
3095 internal_properties.props.resize(new_properties.size());
3096 for (int32_t i = 0; i < new_properties.size(); ++i)
3098 internal_properties.props[i].parent_value = this->target_value;
3099 internal_properties.props[i].property_path = target_path;
3100 internal_properties.props[i].key = new_properties[i];
3101 internal_properties.props[i].display_name = new_properties[i];
3102 internal_properties.props[i].help_text =
nullptr;
3103 internal_properties.props[i].priority = 0;
3104 internal_properties.props[i].delegate_info.Set0();
3105 internal_properties.props[i].delegate =
nullptr;
3106 internal_properties.props[i].group_idx = num_groups;
3107 internal_properties.props[i].shape =
nullptr;
3108 internal_properties.props[i].shape_property_path.Clear();
3109 internal_properties.props[i].shape_delegate =
nullptr;
3110 internal_properties.props[i].about_to_edit =
false;
3118 if (AddEffectGroup(num_groups, obj))
3126 if (AddEffectGroup(num_groups,
nullptr))
3134 int32_t C4ConsoleQtPropListModel::UpdateValueArray(
C4ValueArray *target_array, int32_t *default_selection_group, int32_t *default_selection_index)
3136 if (property_groups.empty())
3138 layout_valid =
false;
3139 property_groups.resize(1);
3141 C4PropList *info_proplist = this->info_proplist.getPropList();
3142 C4Value elements_delegate_value;
3144 property_groups[0].name = (info_proplist ? info_proplist->
GetName() :
LoadResStr(
"IDS_CNS_ARRAYEDIT"));
3145 PropertyGroup &properties = property_groups[0];
3146 if (properties.props.size() != target_array->
GetSize())
3148 layout_valid =
false;
3149 properties.props.resize(target_array->
GetSize());
3151 C4PropertyDelegate *item_delegate = delegate_factory->GetDelegateByValue(elements_delegate_value);
3152 for (int32_t i = 0; i < properties.props.size(); ++i)
3154 Property &prop = properties.props[i];
3156 prop.parent_value = target_value;
3158 prop.help_text =
nullptr;
3161 prop.delegate_info = elements_delegate_value;
3162 prop.delegate = item_delegate;
3163 prop.about_to_edit =
false;
3165 prop.shape =
nullptr;
3166 prop.shape_property_path.Clear();
3167 prop.shape_delegate =
nullptr;
3172 void C4ConsoleQtPropListModel::DoOnUpdateCall(
const C4PropertyPath &updated_path,
const C4PropertyDelegate *delegate)
3175 const char *update_callback = delegate->GetUpdateCallback();
3176 if (update_callback)
3181 C4PropList *base_proplist = this->base_proplist.getPropList();
3189 C4ConsoleQtPropListModel::Property *C4ConsoleQtPropListModel::GetPropByIndex(
const QModelIndex &index)
const
3191 if (!index.isValid())
return nullptr;
3193 int32_t group_index = index.internalId(), row = index.row();
3195 if (!group_index)
return nullptr;
3197 if (group_index >= property_groups.size())
return nullptr;
3198 if (row < 0 || row >= property_groups[group_index].props.size())
return nullptr;
3199 return const_cast<Property *
>(&property_groups[group_index].props[row]);
3202 int C4ConsoleQtPropListModel::rowCount(
const QModelIndex & parent)
const
3204 QModelIndex grandparent;
3206 if (!parent.isValid())
3208 return property_groups.size();
3211 grandparent = parent.parent();
3212 if (!grandparent.isValid())
3214 if (parent.row() >= 0 && parent.row() < property_groups.size())
3215 return property_groups[parent.row()].props.size();
3220 int C4ConsoleQtPropListModel::columnCount(
const QModelIndex & parent)
const
3225 QVariant C4ConsoleQtPropListModel::headerData(
int section, Qt::Orientation orientation,
int role)
const
3228 if (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal)
3231 if (target_value.GetType() ==
C4V_Array)
3232 return QVariant(
LoadResStr(
"IDS_CNS_INDEXSHORT"));
3235 if (section == 1)
return QVariant(
LoadResStr(
"IDS_CNS_VALUE"));
3240 QVariant C4ConsoleQtPropListModel::data(
const QModelIndex & index,
int role)
const
3243 int32_t group_index = index.internalId();
3246 if (!index.column())
3248 if (role == Qt::DisplayRole)
3250 if (index.row() >= 0 && index.row() < property_groups.size())
3251 return property_groups[index.row()].name;
3253 else if (role == Qt::FontRole)
3261 Property *prop = GetPropByIndex(index);
3262 if (!prop)
return QVariant();
3263 if (!prop->delegate) prop->delegate = delegate_factory->GetDelegateByValue(prop->delegate_info);
3264 if (role == Qt::DisplayRole)
3266 switch (index.column())
3269 return QVariant(prop->display_name->GetCStr());
3273 prop->delegate->GetPropertyValue(prop->parent_value, prop->key, index.row(), &v);
3274 return QVariant(prop->delegate->GetDisplayString(v, target_value.getObj(),
true));
3278 else if (role == Qt::BackgroundColorRole && index.column()==1)
3281 prop->delegate->GetPropertyValue(prop->parent_value, prop->key, index.row(), &v);
3282 QColor bgclr = prop->delegate->GetDisplayBackgroundColor(v, target_value.getObj());
3283 if (bgclr.isValid())
return bgclr;
3285 else if (role == Qt::TextColorRole && index.column() == 1)
3288 prop->delegate->GetPropertyValue(prop->parent_value, prop->key, index.row(), &v);
3289 QColor txtclr = prop->delegate->GetDisplayTextColor(v, target_value.getObj());
3290 if (txtclr.isValid())
return txtclr;
3295 return QIcon(
":/editor/res/Help.png");
3297 else if (role == Qt::FontRole && index.column() == 0)
3299 if (prop->priority >= 100)
return important_property_font;
3301 else if (role == Qt::ToolTipRole && index.column() == 0)
3304 if (prop->help_text)
3305 return QString(prop->help_text->GetCStr());
3307 return QString(prop->display_name->GetCStr());
3313 QModelIndex C4ConsoleQtPropListModel::index(
int row,
int column,
const QModelIndex &parent)
const
3315 if (column < 0 || column > 1)
return QModelIndex();
3317 if (!parent.isValid())
3320 if (row < 0 || row >= property_groups.size())
return QModelIndex();
3321 return createIndex(row, column, (quintptr)0u);
3323 if (parent.internalId())
return QModelIndex();
3325 const PropertyGroup *property_group =
nullptr;
3326 if (parent.row() >= 0 && parent.row() < property_groups.size())
3328 property_group = &property_groups[parent.row()];
3329 if (row < 0 || row >= property_group->props.size())
return QModelIndex();
3330 return createIndex(row, column, (quintptr)parent.row()+1);
3332 return QModelIndex();
3335 QModelIndex C4ConsoleQtPropListModel::parent(
const QModelIndex &index)
const
3338 auto parent_idx = index.internalId();
3339 if (parent_idx)
return createIndex(parent_idx - 1, 0, (quintptr)0u);
3340 return QModelIndex();
3343 Qt::ItemFlags C4ConsoleQtPropListModel::flags(
const QModelIndex &index)
const
3345 Qt::ItemFlags flags = QAbstractItemModel::flags(index) | Qt::ItemIsDropEnabled;
3346 Property *prop = GetPropByIndex(index);
3347 if (index.isValid() && prop)
3349 flags &= ~Qt::ItemIsDropEnabled;
3350 if (index.column() == 0)
3353 if (prop->parent_value.GetType() ==
C4V_Array) flags |= Qt::ItemIsDragEnabled;
3355 else if (index.column() == 1)
3359 bool readonly = IsTargetReadonly() && prop->delegate != delegate_factory->GetEffectDelegate();
3361 flags |= Qt::ItemIsEditable;
3363 flags &= ~Qt::ItemIsEnabled;
3369 Qt::DropActions C4ConsoleQtPropListModel::supportedDropActions()
const
3371 return Qt::MoveAction;
3374 bool C4ConsoleQtPropListModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent)
3377 if (action != Qt::MoveAction)
return false;
3379 if (!arr)
return false;
3380 if (!data->hasFormat(
"application/vnd.text"))
return false;
3381 if (row < 0)
return false;
3382 if (!parent.isValid())
return false;
3384 QByteArray encodedData = data->data(
"application/vnd.text");
3386 rearrange_call.
Format(
"MoveArrayItems(%%s, [%s], %d)", encodedData.constData(), row);
3387 target_path.DoCall(rearrange_call.
getData());
3391 QStringList C4ConsoleQtPropListModel::mimeTypes()
const
3394 types <<
"application/vnd.text";
3398 QMimeData *C4ConsoleQtPropListModel::mimeData(
const QModelIndexList &indexes)
const
3401 QMimeData *mimeData =
new QMimeData();
3402 QByteArray encodedData;
3404 for (
const QModelIndex &index : indexes)
3406 if (index.isValid() && index.internalId())
3408 if (count) encodedData.append(
",");
3409 encodedData.append(QString::number(index.row()));
3413 mimeData->setData(
"application/vnd.text", encodedData);
3417 QString C4ConsoleQtPropListModel::GetTargetPathHelp()
const
3420 C4PropList *info_proplist = this->info_proplist.getPropList();
3421 if (!info_proplist)
return QString();
3424 if (!desc)
return QString();
3425 QString result = QString(desc->
GetCStr());
3426 result = result.replace(
'|',
'\n');
3430 const char *C4ConsoleQtPropListModel::GetTargetPathName()
const
3433 C4PropList *info_proplist = this->info_proplist.getPropList();
3434 if (!info_proplist)
return nullptr;
3436 return name ? name->
GetCStr() :
nullptr;
3439 void C4ConsoleQtPropListModel::AddArrayElement()
3442 C4PropList *info_proplist = this->info_proplist.getPropList();
3447 info_proplist_static = info_proplist->
IsStatic();
3452 void C4ConsoleQtPropListModel::RemoveArrayElement()
3456 for (QModelIndex idx : selection_model->selectedIndexes())
3457 if (idx.isValid() && idx.column() == 0)
3465 bool C4ConsoleQtPropListModel::IsTargetReadonly()
const
3467 if (target_path.IsEmpty())
return true;
3468 switch (target_value.GetType())
3475 C4PropList *parent_proplist = target_value._getPropList();
3476 if (parent_proplist->
IsFrozen())
return true;
3484 class C4ConsoleQtShape *C4ConsoleQtPropListModel::GetShapeByPropertyPath(
const char *property_path)
3487 auto entry = shapes.find(std::string(property_path));
3488 if (entry == shapes.end())
return nullptr;
3489 return entry->second.Get();
uint32_t GetTextColorForBackground(uint32_t background_color)
C4Def * C4Id2Def(C4ID id)
C4AulScriptEngine ScriptEngine
C4Application Application
const char * LoadResStr(const char *id)
bool LogF(const char *strMessage,...)
C4SoundInstance * StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
@ P_EditorPropertyChanged
C4Value C4VObj(C4Object *pObj)
C4Value C4VInt(int32_t i)
C4Value C4VPropList(C4PropList *p)
C4Value C4VString(C4String *pStr)
const char * SSearch(const char *szString, const char *szIndex)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
StdStrBuf FormatString(const char *szFmt,...)
void RemoveExtension(char *szFilename)
C4SoundSystem SoundSystem
C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors=false, C4AulScriptContext *context=nullptr, bool parse_function=false)
C4PropListStatic * GetPropList()
C4Effect * pGlobalEffects
char LanguageEx[CFG_MaxString+1]
C4ConfigDeveloper Developer
std::vector< C4Def * > GetAllDefs(C4String *filter_property=nullptr) const
void EMControl(enum C4PacketType eCtrlType, class C4ControlPacket *pCtrl)
bool GetCurrentSelectionPosition(int32_t *x, int32_t *y)
void SetHighlightedObject(C4Object *new_highlight)
void InvalidateSelection()
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
C4String * GetTranslatedString(const class C4Value &input_string, C4Value *selected_language, bool fail_silently) const
C4PropList * AllocateTranslatedString()
C4Object * SafeObjectPointer(int32_t object_number)
static const C4ID EditorBase
bool GetPropertyByS(const C4String *k, C4Value *pResult) const override
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val=0) const
int32_t GetPropertyBool(C4PropertyName n, bool default_val=false) const
virtual C4Object * GetObject()
virtual const char * GetName() const
virtual class C4PropListStatic * IsStatic()
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const
C4ValueArray * GetPropertyArray(C4PropertyName n) const
virtual C4Effect * GetEffect()
std::vector< C4String * > GetSortedLocalProperties(bool add_prototype=true) const
C4PropList * GetPropertyPropList(C4PropertyName k) const
C4String * GetPropertyStr(C4PropertyName k) const
virtual C4Def const * GetDef() const
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
virtual void SetPropertyByS(C4String *k, const C4Value &to)
bool GetProperty(C4PropertyName k, C4Value *pResult) const
std::vector< C4String * > GetUnsortedProperties(const char *prefix, C4PropList *ignore_parent=nullptr) const
const C4PropListStatic * GetParent() const
void SetProperty(const char *set_string) const
const char * GetRoot() const
const char * GetGetPath() const
void SetSetPath(const C4PropertyPath &parent, const char *child_property, PathType path_type)
C4SoundEffect * GetFirstSound() const
StdStrBuf GetData() const
const char * GetCStr() const
C4String * RegString(StdStrBuf String)
const C4Value & GetItem(int32_t iElem) const
C4ValueArray * getArray() const
C4V_Type GetTypeEx() const
C4Object * getObj() const
StdStrBuf GetDataString(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
C4PropList * _getPropList() const
C4String * getStr() const
C4ValueArray * _getArray() const
bool IsIdenticalTo(const C4Value &cmp) const
C4PropList * getPropList() const
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
const char * getData() const
void AppendChar(char cChar)
void Append(const char *pnData, size_t iChars)
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
void MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP)