forked from Mirrors/openclonk
Qt Editor: Improve proplist and array display/editing
* Add/Remove element buttons * Display customization of user delegatesqteditor
parent
4d04135cda
commit
16e31098b4
|
@ -223,3 +223,14 @@ global func MoveArrayItems(array arr, array source_indices, int insert_before)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deletes multiple indexes from an array, does not change the order of items in the array.
|
||||
global func RemoveArrayIndices(array arr, array indices)
|
||||
{
|
||||
indices = indices[:];
|
||||
SortArray(indices, true);
|
||||
for (idx in indices)
|
||||
if (idx < GetLength(arr))
|
||||
RemoveArrayIndex(arr, idx);
|
||||
return true;
|
||||
}
|
|
@ -32,6 +32,8 @@ IDS_BTN_YES=Ja
|
|||
IDS_CHAT_NOTCONNECTED=nicht verbunden
|
||||
IDS_CHAT_SERVER=Server
|
||||
IDS_CNS_ACTION=Aktivität:
|
||||
IDS_CNS_ARRAYADD=Element hinzufügen
|
||||
IDS_CNS_ARRAYREMOVE=Element entfernen
|
||||
IDS_CNS_ARRAYSHORT=[%1 Elemente]
|
||||
IDS_CNS_BACK=<< Zurück
|
||||
IDS_CNS_BROWSE=Durchsuchen...
|
||||
|
|
|
@ -32,6 +32,8 @@ IDS_BTN_YES=Yes
|
|||
IDS_CHAT_NOTCONNECTED=not connected
|
||||
IDS_CHAT_SERVER=Server
|
||||
IDS_CNS_ACTION=Action:
|
||||
IDS_CNS_ARRAYADD=Add item
|
||||
IDS_CNS_ARRAYREMOVE=Remove item
|
||||
IDS_CNS_ARRAYSHORT=[%1 items]
|
||||
IDS_CNS_BACK=<< Back
|
||||
IDS_CNS_BROWSE=Browse...
|
||||
|
|
|
@ -483,6 +483,27 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="arrayEditingLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="arrayAddButton">
|
||||
<property name="text">
|
||||
<string comment="res">IDS_CNS_ARRAYADD</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="arrayRemoveButton">
|
||||
<property name="text">
|
||||
<string comment="res">IDS_CNS_ARRAYREMOVE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="propertyTable">
|
||||
<property name="acceptDrops">
|
||||
|
@ -1445,6 +1466,38 @@
|
|||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>arrayAddButton</sender>
|
||||
<signal>pressed()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>AddArrayElement()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>756</x>
|
||||
<y>116</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>477</x>
|
||||
<y>312</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>arrayRemoveButton</sender>
|
||||
<signal>pressed()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>RemoveArrayElement()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>886</x>
|
||||
<y>116</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>477</x>
|
||||
<y>312</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>PlayPressed(bool)</slot>
|
||||
|
@ -1478,5 +1531,7 @@
|
|||
<slot>FileNew()</slot>
|
||||
<slot>WelcomeLinkActivated(QString)</slot>
|
||||
<slot>AscendPropertyPath()</slot>
|
||||
<slot>AddArrayElement()</slot>
|
||||
<slot>RemoveArrayElement()</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
|
|
|
@ -250,7 +250,7 @@ C4PropertyDelegateDescendPath::C4PropertyDelegateDescendPath(const class C4Prope
|
|||
edit_on_selection = props->GetPropertyBool(P_EditOnSelection, edit_on_selection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void C4PropertyDelegateDescendPath::SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const
|
||||
{
|
||||
Editor *editor = static_cast<Editor *>(aeditor);
|
||||
|
@ -282,16 +282,72 @@ QWidget *C4PropertyDelegateDescendPath::CreateEditor(const class C4PropertyDeleg
|
|||
return peditor.release();
|
||||
}
|
||||
|
||||
QString C4PropertyDelegateDescendPath::GetDisplayString(const C4Value &v, C4Object *obj) const
|
||||
C4PropertyDelegateArray::C4PropertyDelegateArray(const class C4PropertyDelegateFactory *factory, C4PropList *props)
|
||||
: C4PropertyDelegateDescendPath(factory, props), max_array_display(0), element_delegate(nullptr)
|
||||
{
|
||||
switch (v.GetType())
|
||||
if (props)
|
||||
{
|
||||
case C4V_PropList: return QString("{...}");
|
||||
case C4V_Array: return QString(LoadResStr("IDS_CNS_ARRAYSHORT")).arg(uint32_t(v.getInt()));
|
||||
default: return QString(LoadResStr("IDS_CNS_INVALID"));
|
||||
max_array_display = props->GetPropertyInt(P_Display);
|
||||
}
|
||||
}
|
||||
|
||||
QString C4PropertyDelegateArray::GetDisplayString(const C4Value &v, C4Object *obj) const
|
||||
{
|
||||
C4ValueArray *arr = v.getArray();
|
||||
if (!arr) return QString(LoadResStr("IDS_CNS_INVALID"));
|
||||
int32_t n = v._getArray()->GetSize();
|
||||
if (!element_delegate) element_delegate = factory->GetDelegateByValue(info_proplist);
|
||||
if (max_array_display && n)
|
||||
{
|
||||
QString result = "[";
|
||||
for (int32_t i = 0; i < std::min<int32_t>(n, max_array_display); ++i)
|
||||
{
|
||||
if (i) result += ",";
|
||||
result += element_delegate->GetDisplayString(v._getArray()->GetItem(i), obj);
|
||||
}
|
||||
if (n > max_array_display) result += ",...";
|
||||
result += "]";
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default display (or display with 0 elements): Just show element number
|
||||
return QString(LoadResStr("IDS_CNS_ARRAYSHORT")).arg(n);
|
||||
}
|
||||
}
|
||||
|
||||
C4PropertyDelegatePropList::C4PropertyDelegatePropList(const class C4PropertyDelegateFactory *factory, C4PropList *props)
|
||||
: C4PropertyDelegateDescendPath(factory, props)
|
||||
{
|
||||
if (props)
|
||||
{
|
||||
display_string = props->GetPropertyStr(P_Display);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QString C4PropertyDelegatePropList::GetDisplayString(const C4Value &v, C4Object *obj) const
|
||||
{
|
||||
C4PropList *data = v.getPropList();
|
||||
if (!data) return QString(LoadResStr("IDS_CNS_INVALID"));
|
||||
if (!display_string) return QString("{...}");
|
||||
// Replace all {{name}} by property values of name
|
||||
QString result = display_string->GetCStr();
|
||||
int32_t pos0, pos1;
|
||||
C4Value cv;
|
||||
while ((pos0 = result.indexOf("{{")) >= 0)
|
||||
{
|
||||
pos1 = result.indexOf("}}", pos0+2);
|
||||
if (pos1 < 0) break; // placeholder not closed
|
||||
QString substring = result.mid(pos0+2, pos1-pos0-2);
|
||||
if (!data->GetPropertyByS(::Strings.RegString(substring.toUtf8()), &cv)) cv.Set0();
|
||||
// TODO: May want to resolve child delegates for this in the future
|
||||
// For now, just use GetDataString()
|
||||
result.replace(pos0, pos1 - pos0 + 2, cv.GetDataString().getData());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
C4PropertyDelegateColor::C4PropertyDelegateColor(const class C4PropertyDelegateFactory *factory, C4PropList *props)
|
||||
: C4PropertyDelegate(factory, props)
|
||||
|
@ -832,8 +888,8 @@ C4PropertyDelegate *C4PropertyDelegateFactory::CreateDelegateByString(const C4St
|
|||
if (!str) return NULL;
|
||||
// create default base types
|
||||
if (str->GetData() == "int") return new C4PropertyDelegateInt(this, props);
|
||||
if (str->GetData() == "array") return new C4PropertyDelegateDescendPath(this, props);
|
||||
if (str->GetData() == "proplist") return new C4PropertyDelegateDescendPath(this, props);
|
||||
if (str->GetData() == "array") return new C4PropertyDelegateArray(this, props);
|
||||
if (str->GetData() == "proplist") return new C4PropertyDelegatePropList(this, props);
|
||||
if (str->GetData() == "color") return new C4PropertyDelegateColor(this, props);
|
||||
if (str->GetData() == "def") return new C4PropertyDelegateDef(this, props);
|
||||
if (str->GetData() == "enum") return new C4PropertyDelegateEnum(this, props);
|
||||
|
@ -906,7 +962,7 @@ void C4PropertyDelegateFactory::setEditorData(QWidget *editor, const QModelIndex
|
|||
{
|
||||
// Put property value from proplist into editor
|
||||
C4PropertyDelegate *d = GetDelegateByIndex(index);
|
||||
if (!d) return;
|
||||
if (!CheckCurrentEditor(d, editor)) return;
|
||||
// Fetch property only first time - ignore further updates to the same value to simplify editing
|
||||
C4ConsoleQtPropListModel::Property *prop = static_cast<C4ConsoleQtPropListModel::Property *>(index.internalPointer());
|
||||
if (!prop) return;
|
||||
|
@ -922,7 +978,7 @@ void C4PropertyDelegateFactory::setModelData(QWidget *editor, QAbstractItemModel
|
|||
{
|
||||
// Fetch property value from editor and set it into proplist
|
||||
C4PropertyDelegate *d = GetDelegateByIndex(index);
|
||||
if (!d) return;
|
||||
if (!CheckCurrentEditor(d, editor)) return;
|
||||
C4ConsoleQtPropListModel::Property *prop = static_cast<C4ConsoleQtPropListModel::Property *>(index.internalPointer());
|
||||
SetPropertyData(d, editor, prop);
|
||||
}
|
||||
|
@ -952,19 +1008,24 @@ QWidget *C4PropertyDelegateFactory::createEditor(QWidget *parent, const QStyleOp
|
|||
});
|
||||
}
|
||||
current_editor = editor;
|
||||
current_editor_delegate = d;
|
||||
return editor;
|
||||
}
|
||||
|
||||
void C4PropertyDelegateFactory::destroyEditor(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
if (editor == current_editor) current_editor = nullptr;
|
||||
if (editor == current_editor)
|
||||
{
|
||||
current_editor = nullptr;
|
||||
current_editor_delegate = nullptr;
|
||||
}
|
||||
QStyledItemDelegate::destroyEditor(editor, index);
|
||||
}
|
||||
|
||||
void C4PropertyDelegateFactory::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
C4PropertyDelegate *d = GetDelegateByIndex(index);
|
||||
if (!d) return;
|
||||
if (!CheckCurrentEditor(d, editor)) return;
|
||||
return d->UpdateEditorGeometry(editor, option);
|
||||
}
|
||||
|
||||
|
@ -995,6 +1056,17 @@ void C4PropertyDelegateFactory::OnPropListChanged()
|
|||
if (current_editor) emit closeEditor(current_editor);
|
||||
}
|
||||
|
||||
bool C4PropertyDelegateFactory::CheckCurrentEditor(C4PropertyDelegate *d, QWidget *editor) const
|
||||
{
|
||||
if (!d || (editor && editor != current_editor) || d != current_editor_delegate)
|
||||
{
|
||||
//const_cast<C4PropertyDelegateFactory *>(this)->emit closeEditor(current_editor);
|
||||
destroyEditor(current_editor, QModelIndex());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Proplist table view */
|
||||
|
||||
|
@ -1458,10 +1530,7 @@ Qt::ItemFlags C4ConsoleQtPropListModel::flags(const QModelIndex &index) const
|
|||
}
|
||||
else if (index.column() == 1)
|
||||
{
|
||||
bool readonly = false;
|
||||
C4PropList *parent_proplist = prop->parent_value.getPropList();
|
||||
if (parent_proplist && parent_proplist->IsFrozen()) readonly = true;
|
||||
if (target_path.IsEmpty()) readonly = true;
|
||||
bool readonly = IsTargetReadonly();
|
||||
// Only object properties are editable at the moment
|
||||
if (!readonly)
|
||||
flags |= Qt::ItemIsEditable;
|
||||
|
@ -1517,4 +1586,44 @@ QMimeData *C4ConsoleQtPropListModel::mimeData(const QModelIndexList &indexes) co
|
|||
}
|
||||
mimeData->setData("application/vnd.text", encodedData);
|
||||
return mimeData;
|
||||
}
|
||||
}
|
||||
|
||||
void C4ConsoleQtPropListModel::AddArrayElement()
|
||||
{
|
||||
C4Value new_val;
|
||||
C4PropList *info_proplist = this->info_proplist.getPropList();
|
||||
if (info_proplist) info_proplist->GetProperty(P_DefaultValue, &new_val);
|
||||
target_path.DoCall(FormatString("PushBack(%%s, %s)", new_val.GetDataString(10).getData()).getData());
|
||||
}
|
||||
|
||||
void C4ConsoleQtPropListModel::RemoveArrayElement()
|
||||
{
|
||||
// Compose script command to remove all selected array indices
|
||||
StdStrBuf script;
|
||||
for (QModelIndex idx : selection_model->selectedIndexes())
|
||||
if (idx.isValid() && idx.column() == 0)
|
||||
if (script.getLength())
|
||||
script.AppendFormat(",%d", idx.row());
|
||||
else
|
||||
script.AppendFormat("%d", idx.row());
|
||||
if (script.getLength()) target_path.DoCall(FormatString("RemoveArrayIndices(%%s, [%s])", script.getData()).getData());
|
||||
}
|
||||
|
||||
bool C4ConsoleQtPropListModel::IsTargetReadonly() const
|
||||
{
|
||||
if (target_path.IsEmpty()) return true;
|
||||
switch (target_value.GetType())
|
||||
{
|
||||
case C4V_Array:
|
||||
// Arrays are never frozen
|
||||
return false;
|
||||
case C4V_PropList:
|
||||
{
|
||||
C4PropList *parent_proplist = target_value._getPropList();
|
||||
if (parent_proplist->IsFrozen()) return true;
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
|
||||
class C4PropertyDelegateDescendPath : public C4PropertyDelegate
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
C4Value info_proplist;
|
||||
bool edit_on_selection;
|
||||
public:
|
||||
|
@ -136,6 +136,26 @@ public:
|
|||
|
||||
void SetEditorData(QWidget *aeditor, const C4Value &val, const C4PropertyPath &property_path) const override;
|
||||
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option, bool by_selection) const override;
|
||||
};
|
||||
|
||||
class C4PropertyDelegateArray : public C4PropertyDelegateDescendPath
|
||||
{
|
||||
private:
|
||||
int32_t max_array_display;
|
||||
mutable C4PropertyDelegate *element_delegate; // lazy eval
|
||||
public:
|
||||
C4PropertyDelegateArray(const class C4PropertyDelegateFactory *factory, C4PropList *props);
|
||||
|
||||
QString GetDisplayString(const C4Value &v, C4Object *obj) const override;
|
||||
};
|
||||
|
||||
class C4PropertyDelegatePropList : public C4PropertyDelegateDescendPath
|
||||
{
|
||||
private:
|
||||
C4RefCntPointer<C4String> display_string;
|
||||
public:
|
||||
C4PropertyDelegatePropList(const class C4PropertyDelegateFactory *factory, C4PropList *props);
|
||||
|
||||
QString GetDisplayString(const C4Value &v, C4Object *obj) const override;
|
||||
};
|
||||
|
||||
|
@ -308,6 +328,7 @@ class C4PropertyDelegateFactory : public QStyledItemDelegate
|
|||
|
||||
mutable std::map<C4Value, std::unique_ptr<C4PropertyDelegate> > delegates;
|
||||
mutable QWidget *current_editor;
|
||||
mutable C4PropertyDelegate *current_editor_delegate;
|
||||
mutable C4Value last_edited_value;
|
||||
class C4ConsoleQtPropListModel *property_model;
|
||||
|
||||
|
@ -325,6 +346,7 @@ public:
|
|||
void SetPropertyModel(class C4ConsoleQtPropListModel *new_property_model) { property_model = new_property_model; }
|
||||
class C4ConsoleQtPropListModel *GetPropertyModel() const { return property_model; }
|
||||
void OnPropListChanged();
|
||||
bool CheckCurrentEditor(C4PropertyDelegate *d, QWidget *editor) const;
|
||||
|
||||
private:
|
||||
void EditorValueChanged(QWidget *editor);
|
||||
|
@ -422,6 +444,10 @@ public:
|
|||
class C4PropList *GetBasePropList() const { return base_proplist.getPropList(); }
|
||||
int32_t GetTargetPathStackSize() const { return target_path_stack.size(); }
|
||||
const char *GetTargetPathText() const { return target_path.GetPath(); }
|
||||
bool IsArray() const { return !!target_value.getArray(); }
|
||||
void AddArrayElement();
|
||||
void RemoveArrayElement();
|
||||
bool IsTargetReadonly() const;
|
||||
|
||||
public:
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
|
||||
|
|
|
@ -316,6 +316,17 @@ void C4ConsoleQtMainWindow::AscendPropertyPath()
|
|||
::Console.EditCursor.InvalidateSelection();
|
||||
}
|
||||
|
||||
void C4ConsoleQtMainWindow::AddArrayElement()
|
||||
{
|
||||
if (state->property_model) state->property_model->AddArrayElement();
|
||||
}
|
||||
|
||||
void C4ConsoleQtMainWindow::RemoveArrayElement()
|
||||
{
|
||||
if (state->property_model) state->property_model->RemoveArrayElement();
|
||||
}
|
||||
|
||||
|
||||
bool C4ConsoleQtMainWindow::HandleEditorKeyDown(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key())
|
||||
|
@ -508,6 +519,9 @@ bool C4ConsoleGUIState::CreateConsoleWindow(C4AbstractApp *app)
|
|||
ui.creatorTreeView->setModel(definition_list_model.get());
|
||||
window->connect(ui.creatorTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, window.get(), &C4ConsoleQtMainWindow::OnCreatorSelectionChanged);
|
||||
window->connect(ui.creatorTreeView->selectionModel(), &QItemSelectionModel::currentChanged, window.get(), &C4ConsoleQtMainWindow::OnCreatorCurrentChanged);
|
||||
window->connect(ui.propertyTable->selectionModel(), &QItemSelectionModel::currentChanged, window.get(), [this]() {
|
||||
this->ui.arrayRemoveButton->setDisabled(this->property_model->IsTargetReadonly() || this->ui.propertyTable->selectionModel()->selectedRows().empty());
|
||||
});
|
||||
|
||||
// Welcome page
|
||||
InitWelcomeScreen();
|
||||
|
@ -688,6 +702,7 @@ void C4ConsoleGUIState::SetInputFunctions(std::list<const char*> &functions)
|
|||
void C4ConsoleGUIState::PropertyDlgUpdate(C4EditCursorSelection &rSelection, bool force_function_update)
|
||||
{
|
||||
int sel_count = rSelection.size();
|
||||
bool is_array = false;
|
||||
if (sel_count != 1)
|
||||
{
|
||||
// Multi object selection: Hide property view; show info label
|
||||
|
@ -713,9 +728,18 @@ void C4ConsoleGUIState::PropertyDlgUpdate(C4EditCursorSelection &rSelection, boo
|
|||
}
|
||||
ui.selectionInfoLabel->setText(property_model->GetTargetPathText());
|
||||
ui.propertyEditAscendPathButton->setVisible(property_model->GetTargetPathStackSize() >= 1);
|
||||
is_array = property_model->IsArray();
|
||||
if (is_array)
|
||||
{
|
||||
bool is_readonly = property_model->IsTargetReadonly();
|
||||
ui.arrayAddButton->setDisabled(is_readonly);
|
||||
ui.arrayRemoveButton->setDisabled(is_readonly || ui.propertyTable->selectionModel()->selectedRows().empty());
|
||||
}
|
||||
ui.propertyTable->setEnabled(true);
|
||||
::Console.EditCursor.ValidateSelection();
|
||||
}
|
||||
ui.arrayAddButton->setVisible(is_array);
|
||||
ui.arrayRemoveButton->setVisible(is_array);
|
||||
// Function update in script combo box
|
||||
if (force_function_update)
|
||||
{
|
||||
|
|
|
@ -130,6 +130,8 @@ public slots:
|
|||
void OnCreatorCurrentChanged(const QModelIndex & current, const QModelIndex & previous);
|
||||
void OnObjectListSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected);
|
||||
void AscendPropertyPath();
|
||||
void AddArrayElement();
|
||||
void RemoveArrayElement();
|
||||
// Global editor key processing
|
||||
bool HandleEditorKeyDown(QKeyEvent *event);
|
||||
bool HandleEditorKeyUp(QKeyEvent *event);
|
||||
|
|
|
@ -266,6 +266,8 @@ C4StringTable::C4StringTable()
|
|||
P[P_EditOnSelection] = "EditOnSelection";
|
||||
P[P_DefaultEditorProp] = "DefaultEditorProp";
|
||||
P[P_CopyDefault] = "CopyDefault";
|
||||
P[P_Display] = "Display";
|
||||
P[P_DefaultValue] = "DefaultValue";
|
||||
P[DFA_WALK] = "WALK";
|
||||
P[DFA_FLIGHT] = "FLIGHT";
|
||||
P[DFA_KNEEL] = "KNEEL";
|
||||
|
|
|
@ -490,6 +490,8 @@ enum C4PropertyName
|
|||
P_EditOnSelection,
|
||||
P_DefaultEditorProp,
|
||||
P_CopyDefault,
|
||||
P_Display,
|
||||
P_DefaultValue,
|
||||
// Default Action Procedures
|
||||
DFA_WALK,
|
||||
DFA_FLIGHT,
|
||||
|
|
Loading…
Reference in New Issue