Qt Editor: More editor property types and functionality

qteditor
Sven Eberhardt 2016-04-16 22:02:24 -04:00
parent 0580132d81
commit c0228be4e7
14 changed files with 466 additions and 104 deletions

View File

@ -43,15 +43,18 @@ IDS_CNS_DROPNODEF=Ungültiges oder nicht geladenes Objekt: %s
IDS_CNS_EDITOR=OpenClonk Editor
IDS_CNS_EFFECTS=Effekte:
IDS_CNS_EXACTTOSTATIC=Beim Wechsel von einer exakten zur statischen Landschaft gehen sämtliche in der exakten Karte gemachten Änderungen verloren.
IDS_CNS_FALSE=Nein
IDS_CNS_FILENAME=Dateiname
IDS_CNS_FILLNOHALT=Das Füllwerkzeug kann nicht im Haltemodus eingesetzt werden.
IDS_CNS_GAMECLOSED=Spiel deinitialisiert.
IDS_CNS_GAMEMODE=Spielmodus
IDS_CNS_GAMESAVED=Spiel gespeichert.
IDS_CNS_HEIGHT=Höhe
IDS_CNS_INTERNAL=Intern
IDS_CNS_LOG=Log
IDS_CNS_MAPSIZE=Breite
IDS_CNS_MAPZOOM=Zoom
IDS_CNS_MORE=...
IDS_CNS_MULTIPLEOBJECTS=%i Objekte ausgewählt.
IDS_CNS_NEWPLRVIEWPORT=Neu für %s
IDS_CNS_NEWSCENARIO=Neues Szenario
@ -61,6 +64,7 @@ IDS_CNS_NOMATDEF=Zeichnen nicht möglich, da die angegebene Material-Textur-Komb
IDS_CNS_NONETEDIT=Bei Wiedergabe einer Aufnahme kann nicht editiert werden.
IDS_CNS_NOOBJECT=Kein Objekt ausgewählt.
IDS_CNS_NOTHING=Nichts
IDS_CNS_OBJECT=Objekt
IDS_CNS_OBJECTS=Objektliste
IDS_CNS_OWNER=Besitzer: %s
IDS_CNS_PLRQUIT=%s löschen
@ -74,6 +78,7 @@ IDS_CNS_SCENARIOSETTINGS=Szenarieneinstellungen
IDS_CNS_SCRIPT=Script
IDS_CNS_SCRIPTCREATEDOBJECTS=Das Script dieses Szenarios hat beim Start der Runde Objekte erschaffen.
IDS_CNS_TITLE=Titel
IDS_CNS_TRUE=Ja
IDS_CNS_TYPE=Typ: %s (%s)
IDS_CNS_VALUE=Wert
IDS_CNS_VIEWPORT=Sichtfenster

View File

@ -43,15 +43,18 @@ IDS_CNS_DROPNODEF=Object invalid or not loaded: %s
IDS_CNS_EDITOR=OpenClonk Editor
IDS_CNS_EFFECTS=Effects:
IDS_CNS_EXACTTOSTATIC=When switching from exact to dynamic mode all changes made in dynamic mode will be lost.
IDS_CNS_FALSE=No
IDS_CNS_FILENAME=Filename
IDS_CNS_FILLNOHALT=The fill tool cannot be used in halt mode.
IDS_CNS_GAMECLOSED=Game cleared.
IDS_CNS_GAMEMODE=Game mode
IDS_CNS_GAMESAVED=Game saved.
IDS_CNS_HEIGHT=Height
IDS_CNS_INTERNAL=Internal
IDS_CNS_LOG=Log
IDS_CNS_MAPSIZE=Map size
IDS_CNS_MAPZOOM=Zoom
IDS_CNS_MORE=...
IDS_CNS_MULTIPLEOBJECTS=%i selected objects.
IDS_CNS_NEWPLRVIEWPORT=New for %s
IDS_CNS_NEWSCENARIO=New Scenario
@ -61,6 +64,7 @@ IDS_CNS_NOMATDEF=Cannot draw because selected material-texture combination '%s-%
IDS_CNS_NONETEDIT=No editing while replaying.
IDS_CNS_NOOBJECT=No selected objects.
IDS_CNS_NOTHING=Nothing
IDS_CNS_OBJECT=Object
IDS_CNS_OBJECTS=Object list
IDS_CNS_OWNER=Owner: %s
IDS_CNS_PLRQUIT=Remove %s
@ -74,6 +78,7 @@ IDS_CNS_SCENARIOSETTINGS=Scenario settings
IDS_CNS_SCRIPT=Script
IDS_CNS_SCRIPTCREATEDOBJECTS=This scenario's script has created objects on initialization.
IDS_CNS_TITLE=Title
IDS_CNS_TRUE=Yes
IDS_CNS_TYPE=Type: %s (%s)
IDS_CNS_VALUE=Value
IDS_CNS_VIEWPORT=Viewport

View File

@ -466,7 +466,7 @@
<item>
<widget class="QTreeView" name="propertyTable">
<property name="editTriggers">
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
<set>QAbstractItemView::AllEditTriggers</set>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>

View File

@ -21,6 +21,7 @@
#include "object/C4Object.h"
#include "object/C4DefList.h"
#include "object/C4Def.h"
#include "script/C4Effect.h"
/* Property path for property setting synchronization */
@ -64,11 +65,15 @@ void C4PropertyPath::SetProperty(const C4Value &to_val) const
/* Property editing */
C4PropertyDelegate::C4PropertyDelegate(const C4PropertyDelegateFactory *factory, const C4PropList *props)
C4PropertyDelegate::C4PropertyDelegate(const C4PropertyDelegateFactory *factory, C4PropList *props)
: QObject(), factory(factory)
{
// Resolve setter callback name
if (props) set_function = props->GetPropertyStr(P_Set);
// Resolve getter+setter callback names
if (props)
{
set_function = props->GetPropertyStr(P_Set);
async_get_function = props->GetPropertyStr(P_AsyncGet);
}
}
void C4PropertyDelegate::UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const
@ -76,7 +81,35 @@ void C4PropertyDelegate::UpdateEditorGeometry(QWidget *editor, const QStyleOptio
editor->setGeometry(option.rect);
}
C4PropertyDelegateInt::C4PropertyDelegateInt(const C4PropertyDelegateFactory *factory, const C4PropList *props)
bool C4PropertyDelegate::GetPropertyValue(C4PropList *props, C4String *key, C4Value *out_val) const
{
if (async_get_function)
{
*out_val = props->Call(async_get_function.Get());
return true;
}
else
{
return props->GetPropertyByS(key, out_val);
}
}
QString C4PropertyDelegate::GetDisplayString(const C4Value &v, C4Object *obj) const
{
return QString(v.GetDataString().getData());
}
QColor C4PropertyDelegate::GetDisplayTextColor(const C4Value &val, class C4Object *obj) const
{
return QColor(); // invalid = default
}
QColor C4PropertyDelegate::GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const
{
return QColor(); // invalid = default
}
C4PropertyDelegateInt::C4PropertyDelegateInt(const C4PropertyDelegateFactory *factory, C4PropList *props)
: C4PropertyDelegate(factory, props), min(std::numeric_limits<int32_t>::min()), max(std::numeric_limits<int32_t>::max()), step(1)
{
// TODO min/max/step
@ -113,7 +146,85 @@ QWidget *C4PropertyDelegateInt::CreateEditor(const C4PropertyDelegateFactory *pa
return editor;
}
C4PropertyDelegateEnum::C4PropertyDelegateEnum(const C4PropertyDelegateFactory *factory, const C4PropList *props, const C4ValueArray *poptions)
C4PropertyDelegateLabelAndButtonWidget::C4PropertyDelegateLabelAndButtonWidget(QWidget *parent)
: QWidget(parent), layout(nullptr), label(nullptr), button(nullptr)
{
layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setMargin(0);
layout->setSpacing(0);
label = new QLabel(this);
layout->addWidget(label);
button = new QPushButton(QString(LoadResStr("IDS_CNS_MORE")), this);
layout->addWidget(button);
}
C4PropertyDelegateColor::C4PropertyDelegateColor(const class C4PropertyDelegateFactory *factory, C4PropList *props)
: C4PropertyDelegate(factory, props)
{
}
uint32_t GetTextColorForBackground(uint32_t background_color)
{
// White text on dark background; black text on bright background
uint8_t r = (background_color >> 16) & 0xff;
uint8_t g = (background_color >> 8) & 0xff;
uint8_t b = (background_color >> 0) & 0xff;
int32_t lgt = r * 30 + g * 59 + b * 11;
return (lgt > 16000) ? 0 : 0xffffff;
}
void C4PropertyDelegateColor::SetEditorData(QWidget *aeditor, const C4Value &val) const
{
Editor *editor = static_cast<Editor *>(aeditor);
uint32_t background_color = static_cast<uint32_t>(val.getInt()) & 0xffffff;
uint32_t foreground_color = GetTextColorForBackground(background_color);
QPalette palette = editor->label->palette();
palette.setColor(editor->label->backgroundRole(), QColor(QRgb(background_color)));
palette.setColor(editor->label->foregroundRole(), QColor(QRgb(foreground_color)));
editor->label->setPalette(palette);
editor->label->setAutoFillBackground(true);
editor->label->setText(GetDisplayString(val, NULL));
editor->last_value = val;
}
void C4PropertyDelegateColor::SetModelData(QWidget *aeditor, const C4PropertyPath &property_path) const
{
Editor *editor = static_cast<Editor *>(aeditor);
property_path.SetProperty(editor->last_value);
}
QWidget *C4PropertyDelegateColor::CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const
{
Editor *editor;
std::unique_ptr<Editor> peditor((editor = new Editor(parent)));
connect(editor->button, &QPushButton::pressed, this, [editor, this]() {
QColor clr = QColorDialog::getColor(QColor(editor->last_value.getInt()));
editor->last_value.SetInt(clr.rgba());
this->SetEditorData(editor, editor->last_value); // force update on display
emit EditingDoneSignal(editor);
});
return peditor.release();
}
QString C4PropertyDelegateColor::GetDisplayString(const C4Value &v, C4Object *obj) const
{
return QString("#%1").arg(v.getInt(), 8, 16, QChar('0'));
}
QColor C4PropertyDelegateColor::GetDisplayTextColor(const C4Value &val, class C4Object *obj) const
{
uint32_t background_color = static_cast<uint32_t>(val.getInt()) & 0xffffff;
uint32_t foreground_color = GetTextColorForBackground(background_color);
return QColor(foreground_color);
}
QColor C4PropertyDelegateColor::GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const
{
return static_cast<uint32_t>(val.getInt()) & 0xffffff;
}
C4PropertyDelegateEnum::C4PropertyDelegateEnum(const C4PropertyDelegateFactory *factory, C4PropList *props, const C4ValueArray *poptions)
: C4PropertyDelegate(factory, props)
{
// Build enum options from C4Value definitions in script
@ -124,7 +235,7 @@ C4PropertyDelegateEnum::C4PropertyDelegateEnum(const C4PropertyDelegateFactory *
for (int32_t i = 0; i < poptions->GetSize(); ++i)
{
const C4Value &v = poptions->GetItem(i);
const C4PropList *props = v.getPropList();
C4PropList *props = v.getPropList();
if (!props) continue;
Option option;
option.name = props->GetPropertyStr(P_Name);
@ -175,9 +286,9 @@ void C4PropertyDelegateEnum::AddConstOption(C4String *name, const C4Value &val)
int32_t C4PropertyDelegateEnum::GetOptionByValue(const C4Value &val) const
{
int32_t iopt = 0;
bool match = false;
for (auto &option : options)
{
bool match = false;
switch (option.storage_type)
{
case Option::StorageByType:
@ -203,7 +314,7 @@ int32_t C4PropertyDelegateEnum::GetOptionByValue(const C4Value &val) const
++iopt;
}
// If no option matches, just pick first
return iopt % options.size();
return match ? iopt : -1;
}
void C4PropertyDelegateEnum::UpdateEditorParameter(C4PropertyDelegateEnum::Editor *editor) const
@ -257,7 +368,8 @@ void C4PropertyDelegateEnum::SetEditorData(QWidget *aeditor, const C4Value &val)
editor->last_val = val;
editor->updating = true;
// Update option selection
editor->option_box->setCurrentIndex(GetOptionByValue(val));
int32_t index = std::max<int32_t>(GetOptionByValue(val), 0);
editor->option_box->setCurrentIndex(index);
// Update parameter
UpdateEditorParameter(editor);
editor->updating = false;
@ -302,7 +414,7 @@ QWidget *C4PropertyDelegateEnum::CreateEditor(const C4PropertyDelegateFactory *p
editor->updating = true;
editor->option_box = new QComboBox(editor);
editor->layout->addWidget(editor->option_box);
for (auto &option : options) editor->option_box->addItem(option.name->GetData().getData());
for (auto &option : options) editor->option_box->addItem(option.name->GetCStr());
void (QComboBox::*currentIndexChanged)(int) = &QComboBox::currentIndexChanged;
connect(editor->option_box, currentIndexChanged, editor, [editor, this](int newval) {
if (!editor->updating) this->UpdateOptionIndex(editor, newval); });
@ -316,7 +428,39 @@ void C4PropertyDelegateEnum::UpdateOptionIndex(C4PropertyDelegateEnum::Editor *e
emit EditorValueChangedSignal(editor);
}
C4PropertyDelegateDef::C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, const C4PropList *props)
QString C4PropertyDelegateEnum::GetDisplayString(const C4Value &v, class C4Object *obj) const
{
// Display string from value
int32_t idx = GetOptionByValue(v);
if (idx < 0)
{
// Value not found: Default display
return C4PropertyDelegate::GetDisplayString(v, obj);
}
else
{
// Value found: Display option string plus parameter
Option option = options[idx];
QString result = option.name->GetCStr();
// Lazy-resolve parameter delegate
if (!option.adelegate && option.adelegate_val.GetType() != C4V_Nil)
option.adelegate = factory->GetDelegateByValue(option.adelegate_val);
if (option.adelegate)
{
C4Value param_val = v;
if (option.value_key.Get())
{
C4PropList *vp = v.getPropList();
if (vp) vp->GetPropertyByS(option.value_key, &param_val);
}
result += " ";
result += option.adelegate->GetDisplayString(param_val, obj);
}
return result;
}
}
C4PropertyDelegateDef::C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, C4PropList *props)
: C4PropertyDelegateEnum(factory, props)
{
// Collect sorted definitions
@ -334,7 +478,50 @@ C4PropertyDelegateDef::C4PropertyDelegateDef(const C4PropertyDelegateFactory *fa
}
}
C4PropertyDelegateC4ValueEnum::C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, const C4PropList *props)
C4PropertyDelegateBool::C4PropertyDelegateBool(const C4PropertyDelegateFactory *factory, C4PropList *props)
: C4PropertyDelegateEnum(factory, props)
{
// Add boolean options
ReserveOptions(2);
AddConstOption(::Strings.RegString(LoadResStr("IDS_CNS_FALSE")), C4VBool(false));
AddConstOption(::Strings.RegString(LoadResStr("IDS_CNS_TRUE")), C4VBool(true));
}
bool C4PropertyDelegateBool::GetPropertyValue(C4PropList *props, C4String *key, C4Value *out_val) const
{
// Force value to bool
props->GetPropertyByS(key, out_val);
if (out_val->GetType() != C4V_Bool) *out_val = C4VBool(!!*out_val);
return true;
}
C4PropertyDelegateHasEffect::C4PropertyDelegateHasEffect(const class C4PropertyDelegateFactory *factory, C4PropList *props)
: C4PropertyDelegateBool(factory, props)
{
if (props) effect = props->GetPropertyStr(P_Effect);
}
bool C4PropertyDelegateHasEffect::GetPropertyValue(C4PropList *props, C4String *key, C4Value *out_val) const
{
const C4Object *obj = props->GetObject();
if (obj && effect)
{
bool has_effect = false;
for (C4Effect *fx = obj->pEffects; fx; fx = fx->pNext)
if (!fx->IsDead())
if (!strcmp(fx->GetName(), effect->GetCStr()))
{
has_effect = true;
break;
}
*out_val = C4VBool(has_effect);
return true;
}
return false;
}
C4PropertyDelegateC4ValueEnum::C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, C4PropList *props)
: C4PropertyDelegateEnum(factory, props)
{
// Add default C4Value selections
@ -395,13 +582,17 @@ QWidget *C4PropertyDelegateC4ValueInput::CreateEditor(const class C4PropertyDele
/* Delegate factory: Create delegates based on the C4Value type */
C4PropertyDelegate *C4PropertyDelegateFactory::CreateDelegateByString(const C4String *str, const C4PropList *props) const
C4PropertyDelegate *C4PropertyDelegateFactory::CreateDelegateByString(const C4String *str, C4PropList *props) const
{
// safety
if (!str) return NULL;
// create default base types
if (str->GetData() == "int") return new C4PropertyDelegateInt(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);
if (str->GetData() == "bool") return new C4PropertyDelegateBool(this, props);
if (str->GetData() == "has_effect") return new C4PropertyDelegateHasEffect(this, props);
if (str->GetData() == "c4valueenum") return new C4PropertyDelegateC4ValueEnum(this, props);
if (str->GetData() == "any") return new C4PropertyDelegateC4ValueInput(this, props);
// unknown type
@ -474,10 +665,10 @@ void C4PropertyDelegateFactory::setEditorData(QWidget *editor, const QModelIndex
if (!prop || !prop->about_to_edit) return;
prop->about_to_edit = false;
C4Value val;
C4PropList *props = prop->parent_proplist->getPropList();
C4PropList *props = prop->parent_proplist.getPropList();
if (props)
{
props->GetPropertyByS(prop->name.Get(), &val);
d->GetPropertyValue(props, prop->key, &val);
d->SetEditorData(editor, val);
}
}
@ -488,16 +679,16 @@ void C4PropertyDelegateFactory::setModelData(QWidget *editor, QAbstractItemModel
C4PropertyDelegate *d = GetDelegateByIndex(index);
if (!d) return;
C4ConsoleQtPropListModel::Property *prop = static_cast<C4ConsoleQtPropListModel::Property *>(index.internalPointer());
C4PropList *props = prop->parent_proplist->getPropList();
C4PropList *props = prop->parent_proplist.getPropList();
if (props)
{
// Compose set command
C4PropertyPath path(prop->parent_proplist->GetDataString().getData());
C4PropertyPath path(prop->parent_proplist.GetDataString().getData());
C4PropertyPath subpath;
if (d->GetSetFunction())
subpath = C4PropertyPath(path, d->GetSetFunction(), C4PropertyPath::PPT_SetFunction);
else
subpath = C4PropertyPath(path, prop->name->GetCStr());
subpath = C4PropertyPath(path, prop->key->GetCStr());
d->SetModelData(editor, subpath);
}
}
@ -536,7 +727,8 @@ QSize C4PropertyDelegateFactory::sizeHint(const QStyleOptionViewItem &option, co
/* Proplist table view */
C4ConsoleQtPropListModel::C4ConsoleQtPropListModel()
C4ConsoleQtPropListModel::C4ConsoleQtPropListModel(C4PropertyDelegateFactory *delegate_factory)
: delegate_factory(delegate_factory)
{
header_font.setBold(true);
}
@ -545,57 +737,94 @@ C4ConsoleQtPropListModel::~C4ConsoleQtPropListModel()
{
}
bool C4ConsoleQtPropListModel::AddPropertyGroup(C4PropList *add_proplist, int32_t group_index, QString name, C4PropList *target_proplist)
{
const char *editor_prop_prefix = "EditorProp_";
auto new_properties = add_proplist->GetSortedLocalProperties(editor_prop_prefix, target_proplist);
if (!new_properties.size()) return false;
if (property_groups.size() == group_index) property_groups.resize(group_index + 1);
PropertyGroup &properties = property_groups[group_index];
C4PropListStatic *proplist_static = add_proplist->IsStatic();
properties.name = name;
properties.props.resize(new_properties.size());
for (int32_t i = 0; i < new_properties.size(); ++i)
{
properties.props[i].parent_proplist.SetPropList(target_proplist);
properties.props[i].key = NULL;
properties.props[i].display_name = NULL;
properties.props[i].delegate_info.Set0(); // default C4Value delegate
properties.props[i].delegate = NULL; // init when needed
properties.props[i].group_idx = group_index;
C4Value published_prop_val;
add_proplist->GetPropertyByS(new_properties[i], &published_prop_val);
C4PropList *published_prop = published_prop_val.getPropList();
if (published_prop)
{
properties.props[i].key = published_prop->GetPropertyStr(P_Key);
properties.props[i].display_name = published_prop->GetPropertyStr(P_Name);
properties.props[i].delegate_info.SetPropList(published_prop);
}
if (!properties.props[i].key) properties.props[i].key = ::Strings.RegString(new_properties[i]->GetCStr() + strlen(editor_prop_prefix));
if (!properties.props[i].display_name) properties.props[i].display_name = ::Strings.RegString(new_properties[i]->GetCStr() + strlen(editor_prop_prefix));
}
return true;
}
void C4ConsoleQtPropListModel::SetPropList(class C4PropList *new_proplist)
{
// Update properties
proplist.SetPropList(new_proplist);
// Determine number of property groups
int32_t num_groups = 0;
if (new_proplist)
{
// Objects only: Published properties
C4Object *obj = new_proplist->GetObject();
if (obj)
{
// Properties from effects (no inheritance supported)
for (C4Effect *fx = obj->pEffects; fx; fx = fx->pNext)
{
QString name = fx->GetName();
if (AddPropertyGroup(fx, num_groups, name, fx))
++num_groups;
}
// Properties from object
for (C4PropList *check_proplist = new_proplist; check_proplist; check_proplist = check_proplist->GetPrototype())
{
QString name;
C4PropListStatic *proplist_static = check_proplist->IsStatic();
if (proplist_static)
name = QString(proplist_static->GetDataString().getData());
else
name = check_proplist->GetName();
if (AddPropertyGroup(check_proplist, num_groups, name, new_proplist))
++num_groups;
}
// properties from global list
C4Def *editor_base = C4Id2Def(C4ID::EditorBase);
if (editor_base)
if (AddPropertyGroup(editor_base, num_groups, LoadResStr("IDS_CNS_OBJECT"), new_proplist))
++num_groups;
}
// Always: Internal properties
auto new_properties = new_proplist->GetSortedLocalProperties();
internal_properties.resize(new_properties.size());
if (property_groups.size() == num_groups) property_groups.resize(num_groups + 1);
PropertyGroup &internal_properties = property_groups[num_groups];
internal_properties.name = LoadResStr("IDS_CNS_INTERNAL");
internal_properties.props.resize(new_properties.size());
for (int32_t i = 0; i < new_properties.size(); ++i)
{
internal_properties[i].parent_proplist = &proplist;
internal_properties[i].name = new_properties[i];
internal_properties[i].delegate_info.Set0(); // default C4Value delegate
internal_properties[i].delegate = NULL; // init when needed
internal_properties[i].is_internal = true;
}
// Objects only: Published properties
if (new_proplist->GetObject())
{
const char *editor_prop_prefix = "EditorProp_";
auto new_properties = new_proplist->GetSortedProperties(editor_prop_prefix);
published_properties.resize(new_properties.size());
for (int32_t i = 0; i < new_properties.size(); ++i)
{
published_properties[i].parent_proplist = &proplist;
published_properties[i].name = NULL;
published_properties[i].delegate_info.Set0(); // default C4Value delegate
published_properties[i].delegate = NULL; // init when needed
published_properties[i].is_internal = false;
C4Value published_prop_val;
new_proplist->GetPropertyByS(new_properties[i], &published_prop_val);
C4PropList *published_prop = published_prop_val.getPropList();
if (published_prop)
{
published_properties[i].name = published_prop->GetPropertyStr(P_Name);
published_properties[i].delegate_info.SetPropList(published_prop);
}
if (!published_properties[i].name) published_properties[i].name = ::Strings.RegString(new_properties[i]->GetCStr() + strlen(editor_prop_prefix));
}
}
else
{
published_properties.clear();
internal_properties.props[i].parent_proplist = proplist;
internal_properties.props[i].key = new_properties[i];
internal_properties.props[i].display_name = new_properties[i];
internal_properties.props[i].delegate_info.Set0(); // default C4Value delegate
internal_properties.props[i].delegate = NULL; // init when needed
internal_properties.props[i].group_idx = num_groups;
}
++num_groups;
}
else
{
internal_properties.clear();
published_properties.clear();
}
property_groups.resize(num_groups);
QModelIndex topLeft = index(0, 0, QModelIndex());
QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1, QModelIndex());
emit dataChanged(topLeft, bottomRight);
@ -606,14 +835,14 @@ int C4ConsoleQtPropListModel::rowCount(const QModelIndex & parent) const
{
// Nothing loaded?
if (!proplist.getPropList()) return 0;
// Top level: Published and internal properties
if (!parent.isValid()) return 2;
// Top level: Property groups
if (!parent.isValid()) return property_groups.size();
// Mid level: Descend into property lists
QModelIndex grandparent = parent.parent();
if (!grandparent.isValid())
{
if (parent.row() == 0) return published_properties.size();
if (parent.row() == 1) return internal_properties.size();
if (parent.row() >= 0 && parent.row() < property_groups.size())
return property_groups[parent.row()].props.size();
}
return 0;
}
@ -647,8 +876,8 @@ QVariant C4ConsoleQtPropListModel::data(const QModelIndex & index, int role) con
{
if (role == Qt::DisplayRole)
{
if (index.row() == 0) return QVariant(LoadResStr("IDS_CNS_PROPERTIES"));
if (index.row() == 1) return QVariant(LoadResStr("IDS_CNS_INTERNAL"));
if (index.row() >= 0 && index.row() < property_groups.size())
return property_groups[index.row()].name;
}
else if (role == Qt::FontRole)
{
@ -658,22 +887,37 @@ QVariant C4ConsoleQtPropListModel::data(const QModelIndex & index, int role) con
return QVariant();
}
// Query latest data from prop list
Property *prop = static_cast<Property *>(index.internalPointer());
if (!prop) return QVariant();
if (!prop->delegate) prop->delegate = delegate_factory->GetDelegateByValue(prop->delegate_info);
if (role == Qt::DisplayRole)
{
Property *prop = static_cast<Property *>(index.internalPointer());
if (!prop) return QVariant();
switch (index.column())
{
case 0: // First col: Property Name
return QVariant(prop->name->GetCStr());
return QVariant(prop->display_name->GetCStr());
case 1: // Second col: Property value
{
C4Value v;
props->GetPropertyByS(prop->name, &v); // Just display nil if property is not set
return QVariant(v.GetDataString().getData());
prop->delegate->GetPropertyValue(props, prop->key, &v);
return QVariant(prop->delegate->GetDisplayString(v, props->GetObject()));
}
}
}
else if (role == Qt::BackgroundColorRole && index.column()==1)
{
C4Value v;
prop->delegate->GetPropertyValue(props, prop->key, &v);
QColor bgclr = prop->delegate->GetDisplayBackgroundColor(v, props->GetObject());
if (bgclr.isValid()) return bgclr;
}
else if (role == Qt::TextColorRole && index.column() == 1)
{
C4Value v;
prop->delegate->GetPropertyValue(props, prop->key, &v);
QColor txtclr = prop->delegate->GetDisplayTextColor(v, props->GetObject());
if (txtclr.isValid()) return txtclr;
}
// Nothing to show
return QVariant();
}
@ -685,20 +929,19 @@ QModelIndex C4ConsoleQtPropListModel::index(int row, int column, const QModelInd
if (!parent.isValid())
{
// Top level has headers only
if (row == 0 || row == 1) return createIndex(row, column, nullptr);
return QModelIndex();
if (row < 0 || row >= property_groups.size()) return QModelIndex();
return createIndex(row, column, nullptr);
}
// Property?
QModelIndex grandparent = parent.parent();
if (!grandparent.isValid())
{
const std::vector< Property > *property_list = NULL;
if (parent.row() == 0) property_list = &published_properties;
if (parent.row() == 1) property_list = &internal_properties;
if (property_list)
const PropertyGroup *property_group = NULL;
if (parent.row() >= 0 && parent.row() < property_groups.size())
{
if (row < 0 || row >= property_list->size()) return QModelIndex();
const Property * prop = &(*property_list)[row];
property_group = &property_groups[parent.row()];
if (row < 0 || row >= property_group->props.size()) return QModelIndex();
const Property * prop = &(property_group->props[row]);
return createIndex(row, column, const_cast<Property *>(prop));
}
}
@ -710,7 +953,7 @@ QModelIndex C4ConsoleQtPropListModel::parent(const QModelIndex &index) const
Property *prop = static_cast<Property *>(index.internalPointer());
if (!prop) return QModelIndex();
// Find list to use
return createIndex(prop->is_internal ? 1 : 0, 0, nullptr);
return createIndex(prop->group_idx, 0, nullptr);
}
Qt::ItemFlags C4ConsoleQtPropListModel::flags(const QModelIndex &index) const
@ -719,7 +962,7 @@ Qt::ItemFlags C4ConsoleQtPropListModel::flags(const QModelIndex &index) const
if (index.isValid() && index.column() == 1 && index.internalPointer())
{
Property *prop = static_cast<Property *>(index.internalPointer());
C4PropList *parent_proplist = prop->parent_proplist->getPropList();
C4PropList *parent_proplist = prop->parent_proplist.getPropList();
// Only object properties are editable at the moment
if (parent_proplist && !parent_proplist->IsFrozen() && (parent_proplist->GetObject()==parent_proplist))
flags |= Qt::ItemIsEditable;

View File

@ -57,16 +57,20 @@ class C4PropertyDelegate : public QObject
protected:
const class C4PropertyDelegateFactory *factory;
C4RefCntPointer<C4String> set_function;
C4RefCntPointer<C4String> set_function, async_get_function;
public:
C4PropertyDelegate(const class C4PropertyDelegateFactory *factory, const C4PropList *props);
C4PropertyDelegate(const class C4PropertyDelegateFactory *factory, C4PropList *props);
virtual ~C4PropertyDelegate() { }
virtual void SetEditorData(QWidget *editor, const C4Value &val) const = 0;
virtual void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const = 0;
virtual QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const = 0;
virtual void UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const;
virtual bool GetPropertyValue(C4PropList *props, C4String *key, C4Value *out_val) const;
virtual QString GetDisplayString(const C4Value &val, class C4Object *obj) const;
virtual QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const;
virtual QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const;
const char *GetSetFunction() const { return set_function.Get() ? set_function->GetCStr() : NULL; } // get name of setter function for this property
@ -80,13 +84,42 @@ class C4PropertyDelegateInt : public C4PropertyDelegate
private:
int32_t min, max, step;
public:
C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, const C4PropList *props);
C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, C4PropList *props);
void SetEditorData(QWidget *editor, const C4Value &val) const override;
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override;
};
// Editor: Text displaying value plus a button that opens an extended editor
class C4PropertyDelegateLabelAndButtonWidget : public QWidget
{
Q_OBJECT
public:
QHBoxLayout *layout;
QLabel *label;
QPushButton *button;
C4Value last_value;
C4PropertyDelegateLabelAndButtonWidget(QWidget *parent);
};
class C4PropertyDelegateColor : public C4PropertyDelegate
{
public:
typedef C4PropertyDelegateLabelAndButtonWidget Editor;
C4PropertyDelegateColor(const class C4PropertyDelegateFactory *factory, C4PropList *props);
void SetEditorData(QWidget *editor, const C4Value &val) const override;
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override;
QString GetDisplayString(const C4Value &v, C4Object *obj) const override;
QColor GetDisplayTextColor(const C4Value &val, class C4Object *obj) const override;
QColor GetDisplayBackgroundColor(const C4Value &val, class C4Object *obj) const override;
};
// Widget holder class
class C4PropertyDelegateEnumEditor : public QWidget
{
@ -136,7 +169,7 @@ private:
protected:
void ReserveOptions(int32_t num);
public:
C4PropertyDelegateEnum(const class C4PropertyDelegateFactory *factory, const C4PropList *props, const C4ValueArray *poptions=NULL);
C4PropertyDelegateEnum(const class C4PropertyDelegateFactory *factory, C4PropList *props, const C4ValueArray *poptions=NULL);
void AddTypeOption(C4String *name, C4V_Type type, const C4Value &val, C4PropertyDelegate *adelegate=NULL);
void AddConstOption(C4String *name, const C4Value &val);
@ -144,6 +177,7 @@ public:
void SetEditorData(QWidget *editor, const C4Value &val) const override;
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const override;
QString GetDisplayString(const C4Value &val, class C4Object *obj) const override;
private:
int32_t GetOptionByValue(const C4Value &val) const;
@ -157,14 +191,34 @@ public slots:
class C4PropertyDelegateDef : public C4PropertyDelegateEnum
{
public:
C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, const C4PropList *props);
C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, C4PropList *props);
};
// true or false
class C4PropertyDelegateBool : public C4PropertyDelegateEnum
{
public:
C4PropertyDelegateBool(const class C4PropertyDelegateFactory *factory, C4PropList *props);
bool GetPropertyValue(C4PropList *props, C4String *key, C4Value *out_val) const override;
};
// true or false depending on whether effect is present
class C4PropertyDelegateHasEffect : public C4PropertyDelegateBool
{
private:
C4RefCntPointer<C4String> effect;
public:
C4PropertyDelegateHasEffect(const class C4PropertyDelegateFactory *factory, C4PropList *props);
bool GetPropertyValue(C4PropList *props, C4String *key, C4Value *out_val) const override;
};
// C4Value setting using an enum
class C4PropertyDelegateC4ValueEnum : public C4PropertyDelegateEnum
{
public:
C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, const C4PropList *props);
C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, C4PropList *props);
};
class C4PropertyDelegateC4ValueInputEditor : public QWidget
@ -187,7 +241,7 @@ class C4PropertyDelegateC4ValueInput : public C4PropertyDelegate
public:
typedef C4PropertyDelegateC4ValueInputEditor Editor;
C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory *factory, const C4PropList *props) : C4PropertyDelegate(factory, props) { }
C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory *factory, C4PropList *props) : C4PropertyDelegate(factory, props) { }
void SetEditorData(QWidget *editor, const C4Value &val) const override;
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
@ -200,7 +254,7 @@ class C4PropertyDelegateFactory : public QStyledItemDelegate
mutable std::map<C4Value, std::unique_ptr<C4PropertyDelegate> > delegates;
C4PropertyDelegate *CreateDelegateByString(const C4String *str, const C4PropList *props=NULL) const;
C4PropertyDelegate *CreateDelegateByString(const C4String *str, C4PropList *props=NULL) const;
C4PropertyDelegate *CreateDelegateByValue(const C4Value &val) const;
C4PropertyDelegate *GetDelegateByIndex(const QModelIndex &index) const;
public:
@ -232,24 +286,32 @@ public:
struct Property
{
C4PropertyPath property_path;
C4Value *parent_proplist;
C4RefCntPointer<C4String> name;
C4Value parent_proplist;
C4RefCntPointer<C4String> display_name;
C4RefCntPointer<C4String> key;
C4Value delegate_info;
C4PropertyDelegate *delegate;
bool about_to_edit;
bool is_internal;
int32_t group_idx;
Property() : parent_proplist(NULL), delegate(NULL), about_to_edit(false), is_internal(false) {}
Property() : delegate(NULL), about_to_edit(false), group_idx(-1) {}
};
struct PropertyGroup
{
QString name;
std::vector<Property> props;
};
private:
C4Value proplist;
std::vector< Property > published_properties; // custom properties defined by definitions
std::vector<PropertyGroup> property_groups;
std::vector< Property > internal_properties; // proplist-properties
QFont header_font;
C4PropertyDelegateFactory *delegate_factory;
public:
C4ConsoleQtPropListModel();
C4ConsoleQtPropListModel(C4PropertyDelegateFactory *delegate_factory);
~C4ConsoleQtPropListModel();
bool AddPropertyGroup(C4PropList *add_proplist, int32_t group_index, QString name, C4PropList *ignore_overridden);
void SetPropList(class C4PropList *new_proplist);
class C4PropList *GetPropList() const { return proplist.getPropList(); }

View File

@ -482,8 +482,12 @@ bool C4ConsoleGUIState::CreateConsoleWindow(C4AbstractApp *app)
viewport_area->setCentralWidget(foo);
foo->hide();
// Property editor
property_delegate_factory.reset(new C4PropertyDelegateFactory());
ui.propertyTable->setItemDelegateForColumn(1, property_delegate_factory.get());
// View models
property_model.reset(new C4ConsoleQtPropListModel());
property_model.reset(new C4ConsoleQtPropListModel(property_delegate_factory.get()));
ui.propertyTable->setModel(property_model.get());
object_list_model.reset(new C4ConsoleQtObjectListModel());
ui.objectListView->setModel(object_list_model.get());
@ -492,12 +496,7 @@ 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);
// Property editor
property_delegate_factory.reset(new C4PropertyDelegateFactory());
ui.propertyTable->setItemDelegateForColumn(1, property_delegate_factory.get());
//ui.propertyTable->verticalHeader()->setDefaultSectionSize(ui.propertyTable->fontMetrics().height()+4);
// Welcome page
InitWelcomeScreen();
ShowWelcomeScreen();

View File

@ -496,6 +496,11 @@ public:
StdStrBuf str(pCData); // GCC needs this, for some obscure reason
return Compare(str, iAt);
}
bool BeginsWith(const char *beginning) const
{
// Return whether string starts with beginning
return strncmp((const char *)pData, beginning, strlen(beginning)) == 0;
}
// Grows the string to contain the specified number more/less characters.
// Note: Will set the terminator, but won't initialize - use Append* instead.

View File

@ -29,6 +29,7 @@ C4ID::LookupTable C4ID::lookup;
const C4ID C4ID::None(std::string("None"));
const C4ID C4ID::Clonk(std::string("Clonk"));
const C4ID C4ID::Bubble(std::string("Fx_Bubble"));
const C4ID C4ID::EditorBase(std::string("EditorBase"));
// TODO: Remove these eventually, since they are deprecated.
const C4ID C4ID::Flag(std::string("FLAG"));

View File

@ -45,6 +45,7 @@ public:
static const C4ID Clonk;
static const C4ID Melee;
static const C4ID Bubble;
static const C4ID EditorBase;
C4ID(): v(None.v) {}
C4ID(const C4ID &other): v(other.v) {}

View File

@ -83,7 +83,7 @@ public:
C4MeshDenumerator(C4Def* def): Def(def), Object(NULL) {}
C4MeshDenumerator(C4Object* object): Def(NULL), Object(object) {}
C4Object* GetObject() const { return Object; }
C4Object* GetObject() { return Object; }
virtual void CompileFunc(StdCompiler* pComp, StdMeshInstance::AttachedMesh* attach);
virtual void DenumeratePointers(StdMeshInstance::AttachedMesh* attach);
@ -395,6 +395,7 @@ public:
// overloaded from C4PropList
virtual C4Object * GetObject() { return this; }
virtual C4Object const * GetObject() const { return this; }
virtual void SetPropertyByS(C4String * k, const C4Value & to);
virtual void ResetProperty(C4String * k);
virtual bool GetPropertyByS(C4String *k, C4Value *pResult) const;

View File

@ -472,6 +472,33 @@ std::vector< C4String * > C4PropList::GetSortedLocalProperties() const
return result;
}
std::vector< C4String * > C4PropList::GetSortedLocalProperties(const char *prefix, const C4PropList *ignore_overridden) const
{
// return property list without descending into prototype
// ignore properties that have been overridden by proplist given in ignore_overridden or any of its prototypes up to and excluding this
std::vector< C4String * > result;
for (const C4Property *pp = Properties.First(); pp; pp = Properties.Next(pp))
if (pp->Key != &::Strings.P[P_Prototype])
if (!prefix || pp->Key->GetData().BeginsWith(prefix))
{
// Override check
const C4PropList *check = ignore_overridden;
bool overridden = false;
if (check && check != this)
{
if (check->HasProperty(pp->Key)) { overridden = true; break; }
check = check->GetPrototype();
}
result.push_back(pp->Key);
}
// Sort
std::sort(result.begin(), result.end(), [](const C4String *a, const C4String *b) -> bool
{
return strcmp(a->GetCStr(), b->GetCStr()) < 0;
});
return result;
}
std::vector< C4String * > C4PropList::GetSortedProperties(const char *prefix) const
{
// Return property list with descending into prototype
@ -482,7 +509,7 @@ std::vector< C4String * > C4PropList::GetSortedProperties(const char *prefix) co
{
for (const C4Property *pp = p->Properties.First(); pp; pp = p->Properties.Next(pp))
if (pp->Key != &::Strings.P[P_Prototype])
if (!prefix || !pp->Key->GetData().Compare_(prefix))
if (!prefix || !pp->Key->GetData().BeginsWith(prefix))
result.push_back(pp->Key);
p = p->GetPrototype();
} while (p);
@ -512,13 +539,18 @@ void C4PropList::SetName(const char* NewName)
}
C4Object * C4PropList::GetObject()
{
if (GetPrototype()) return GetPrototype()->GetObject();
return 0;
}
C4Object const * C4PropList::GetObject() const
{
if (GetPrototype()) return GetPrototype()->GetObject();
return 0;
}
C4Def * C4PropList::GetDef()
{
if (GetPrototype()) return GetPrototype()->GetDef();

View File

@ -73,6 +73,7 @@ public:
virtual C4Def const * GetDef() const;
virtual C4Def * GetDef();
virtual C4Object * GetObject();
virtual C4Object const * GetObject() const;
virtual C4Effect * GetEffect();
virtual C4PropListNumbered * GetPropListNumbered();
virtual class C4MapScriptLayer * GetMapScriptLayer();
@ -134,6 +135,7 @@ public:
void CompileFunc(StdCompiler *pComp, C4ValueNumbers *);
void AppendDataString(StdStrBuf * out, const char * delim, int depth = 3) const;
std::vector< C4String * > GetSortedLocalProperties() const;
std::vector< C4String * > GetSortedLocalProperties(const char *prefix, const C4PropList *ignore_overridden) const;
std::vector< C4String * > GetSortedProperties(const char *prefix) const;
bool operator==(const C4PropList &b) const;

View File

@ -254,6 +254,9 @@ C4StringTable::C4StringTable()
P[P_Max] = "Max";
P[P_Set] = "Set";
P[P_Options] = "Options";
P[P_Key] = "Key";
P[P_Effect] = "Effect";
P[P_AsyncGet] = "AsyncGet";
P[DFA_WALK] = "WALK";
P[DFA_FLIGHT] = "FLIGHT";
P[DFA_KNEEL] = "KNEEL";

View File

@ -478,6 +478,9 @@ enum C4PropertyName
P_Max,
P_Set,
P_Options,
P_Key,
P_Effect,
P_AsyncGet,
// Default Action Procedures
DFA_WALK,
DFA_FLIGHT,