Qt Editor: Add user properties

qteditor
Sven Eberhardt 2016-04-09 14:20:31 -04:00
parent de98bbef46
commit 2a2fc68e3f
13 changed files with 343 additions and 85 deletions

View File

@ -449,6 +449,12 @@
</item>
<item>
<widget class="QLabel" name="selectionInfoLabel">
<property name="baseSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>SELECTION</string>
</property>
@ -458,16 +464,16 @@
</widget>
</item>
<item>
<widget class="QTableView" name="propertyTable">
<widget class="QTreeView" name="propertyTable">
<property name="editTriggers">
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<property name="indentation">
<number>9</number>
</property>
</widget>
</item>
<item>

View File

@ -53,7 +53,8 @@ void C4ConsoleQtObjectListModel::Invalidate()
void C4ConsoleQtObjectListModel::OnItemRemoved(C4PropList *p)
{
for (auto idx : this->persistentIndexList())
QModelIndexList list = this->persistentIndexList();
for (auto idx : list)
if (idx.internalPointer() == p)
this->changePersistentIndex(idx, QModelIndex());
Invalidate();

View File

@ -19,6 +19,8 @@
#include "editor/C4ConsoleQtPropListViewer.h"
#include "editor/C4Console.h"
#include "object/C4Object.h"
#include "object/C4DefList.h"
#include "object/C4Def.h"
/* Property path for property setting synchronization */
@ -62,15 +64,28 @@ void C4PropertyPath::SetProperty(const C4Value &to_val) const
/* Property editing */
C4PropertyDelegate::C4PropertyDelegate(const C4PropertyDelegateFactory *factory, const C4PropList *props)
: QObject(), factory(factory)
{
// Resolve setter callback name
if (props) set_function = props->GetPropertyStr(P_Set);
}
void C4PropertyDelegate::UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const
{
editor->setGeometry(option.rect);
}
C4PropertyDelegateInt::C4PropertyDelegateInt(const C4PropertyDelegateFactory *factory, const C4PropList *props)
: C4PropertyDelegate(factory)
: C4PropertyDelegate(factory, props), min(std::numeric_limits<int32_t>::min()), max(std::numeric_limits<int32_t>::max()), step(1)
{
// TODO min/max/step
if (props)
{
min = props->GetPropertyInt(P_Min, min);
max = props->GetPropertyInt(P_Max, max);
step = props->GetPropertyInt(P_Step, step);
}
}
void C4PropertyDelegateInt::SetEditorData(QWidget *editor, const C4Value &val) const
@ -89,48 +104,54 @@ void C4PropertyDelegateInt::SetModelData(QWidget *editor, const C4PropertyPath &
QWidget *C4PropertyDelegateInt::CreateEditor(const C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const
{
QSpinBox *editor = new QSpinBox(parent);
editor->setMinimum(min);
editor->setMaximum(max);
editor->setSingleStep(step);
connect(editor, &QSpinBox::editingFinished, this, [editor, this]() {
emit EditingDoneSignal(editor);
});
return editor;
}
C4PropertyDelegateEnum::C4PropertyDelegateEnum(const C4PropertyDelegateFactory *factory, int reserve_count)
: C4PropertyDelegate(factory)
{
options.reserve(reserve_count);
}
C4PropertyDelegateEnum::C4PropertyDelegateEnum(const C4PropertyDelegateFactory *factory, const C4ValueArray &props)
: C4PropertyDelegate(factory)
C4PropertyDelegateEnum::C4PropertyDelegateEnum(const C4PropertyDelegateFactory *factory, const C4PropList *props, const C4ValueArray *poptions)
: C4PropertyDelegate(factory, props)
{
// Build enum options from C4Value definitions in script
options.reserve(props.GetSize());
for (int32_t i = 0; i < props.GetSize(); ++i)
if (!poptions && props) poptions = props->GetPropertyArray(P_Options);
if (poptions)
{
const C4Value &v = props.GetItem(i);
const C4PropList *props = v.getPropList();
if (!props) continue;
Option option;
option.name = props->GetPropertyStr(P_Name);
if (!option.name.Get()) option.name = ::Strings.RegString("???");
option.value_key = props->GetPropertyStr(P_ValueKey);
props->GetProperty(P_Value, &option.value);
option.type = C4V_Type(props->GetPropertyInt(P_Type, C4V_Any));
option.option_key = props->GetPropertyStr(P_OptionKey);
// Derive storage type from given elements in delegate definition
if (option.type != C4V_Any)
option.storage_type = Option::StorageByType;
else if (option.option_key.Get())
option.storage_type = Option::StorageByKey;
else
option.storage_type = Option::StorageByValue;
// Child delegate for value (resolved at runtime because there may be circular references)
props->GetProperty(P_Delegate, &option.adelegate_val);
options.push_back(option);
options.reserve(poptions->GetSize());
for (int32_t i = 0; i < poptions->GetSize(); ++i)
{
const C4Value &v = poptions->GetItem(i);
const C4PropList *props = v.getPropList();
if (!props) continue;
Option option;
option.name = props->GetPropertyStr(P_Name);
if (!option.name.Get()) option.name = ::Strings.RegString("???");
option.value_key = props->GetPropertyStr(P_ValueKey);
props->GetProperty(P_Value, &option.value);
option.type = C4V_Type(props->GetPropertyInt(P_Type, C4V_Any));
option.option_key = props->GetPropertyStr(P_OptionKey);
// Derive storage type from given elements in delegate definition
if (option.type != C4V_Any)
option.storage_type = Option::StorageByType;
else if (option.option_key.Get())
option.storage_type = Option::StorageByKey;
else
option.storage_type = Option::StorageByValue;
// Child delegate for value (resolved at runtime because there may be circular references)
props->GetProperty(P_Delegate, &option.adelegate_val);
options.push_back(option);
}
}
}
void C4PropertyDelegateEnum::ReserveOptions(int32_t num)
{
options.reserve(num);
}
void C4PropertyDelegateEnum::AddTypeOption(C4String *name, C4V_Type type, const C4Value &val, C4PropertyDelegate *adelegate)
{
Option option;
@ -142,6 +163,15 @@ void C4PropertyDelegateEnum::AddTypeOption(C4String *name, C4V_Type type, const
options.push_back(option);
}
void C4PropertyDelegateEnum::AddConstOption(C4String *name, const C4Value &val)
{
Option option;
option.name = name;
option.value = val;
option.storage_type = Option::StorageByValue;
options.push_back(option);
}
int32_t C4PropertyDelegateEnum::GetOptionByValue(const C4Value &val) const
{
int32_t iopt = 0;
@ -249,7 +279,10 @@ void C4PropertyDelegateEnum::SetModelData(QWidget *aeditor, const C4PropertyPath
// Value from a parameter or directly from the enum?
if (option.adelegate)
{
// Value from a parameter?
// Value from a parameter.
// Using a setter function?
if (option.adelegate->GetSetFunction())
use_path = C4PropertyPath(use_path, option.adelegate->GetSetFunction(), C4PropertyPath::PPT_SetFunction);
option.adelegate->SetModelData(editor->parameter_widget, use_path);
}
else
@ -283,10 +316,29 @@ void C4PropertyDelegateEnum::UpdateOptionIndex(C4PropertyDelegateEnum::Editor *e
emit EditorValueChangedSignal(editor);
}
C4PropertyDelegateC4ValueEnum::C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory)
: C4PropertyDelegateEnum(factory, 10)
C4PropertyDelegateDef::C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, const C4PropList *props)
: C4PropertyDelegateEnum(factory, props)
{
// Collect sorted definitions
std::vector<C4Def *> defs = ::Definitions.GetAllDefs(props ? props->GetPropertyStr(P_Filter) : NULL);
std::sort(defs.begin(), defs.end(), [](C4Def *a, C4Def *b) -> bool {
return strcmp(a->GetName(), b->GetName()) < 0;
});
// Add them
ReserveOptions(defs.size() + 1);
AddConstOption(::Strings.RegString("nil"), C4VNull); // nil is always an option
for (C4Def *def : defs)
{
C4RefCntPointer<C4String> option_name = ::Strings.RegString(FormatString("%s (%s)", def->id.ToString(), def->GetName()));
AddConstOption(option_name, C4Value(def));
}
}
C4PropertyDelegateC4ValueEnum::C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, const C4PropList *props)
: C4PropertyDelegateEnum(factory, props)
{
// Add default C4Value selections
ReserveOptions(10);
AddTypeOption(::Strings.RegString("nil"), C4V_Nil, C4VNull);
AddTypeOption(::Strings.RegString("bool"), C4V_Bool, C4VNull, factory->GetDelegateByValue(C4VString("bool")));
AddTypeOption(::Strings.RegString("int"), C4V_Int, C4VNull, factory->GetDelegateByValue(C4VString("int")));
@ -349,8 +401,9 @@ 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() == "c4valueenum") return new C4PropertyDelegateC4ValueEnum(this);
if (str->GetData() == "any") return new C4PropertyDelegateC4ValueInput(this);
if (str->GetData() == "def") return new C4PropertyDelegateDef(this, props);
if (str->GetData() == "c4valueenum") return new C4PropertyDelegateC4ValueEnum(this, props);
if (str->GetData() == "any") return new C4PropertyDelegateC4ValueInput(this, props);
// unknown type
return NULL;
}
@ -360,9 +413,9 @@ C4PropertyDelegate *C4PropertyDelegateFactory::CreateDelegateByValue(const C4Val
switch (val.GetType())
{
case C4V_Nil:
return new C4PropertyDelegateC4ValueInput(this);
return new C4PropertyDelegateC4ValueInput(this, NULL);
case C4V_Array:
return new C4PropertyDelegateEnum(this, *val.getArray());
return new C4PropertyDelegateEnum(this, NULL, val.getArray());
case C4V_PropList:
{
C4PropList *props = val._getPropList();
@ -438,9 +491,13 @@ void C4PropertyDelegateFactory::setModelData(QWidget *editor, QAbstractItemModel
C4PropList *props = prop->parent_proplist->getPropList();
if (props)
{
// Set value view path
// Compose set command
C4PropertyPath path(prop->parent_proplist->GetDataString().getData());
C4PropertyPath subpath(path, prop->name->GetCStr());
C4PropertyPath subpath;
if (d->GetSetFunction())
subpath = C4PropertyPath(path, d->GetSetFunction(), C4PropertyPath::PPT_SetFunction);
else
subpath = C4PropertyPath(path, prop->name->GetCStr());
d->SetModelData(editor, subpath);
}
}
@ -470,11 +527,18 @@ void C4PropertyDelegateFactory::updateEditorGeometry(QWidget *editor, const QSty
return d->UpdateEditorGeometry(editor, option);
}
QSize C4PropertyDelegateFactory::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
int height = QApplication::fontMetrics().height() + 4;
return QSize(100, height);
}
/* Proplist table view */
C4ConsoleQtPropListModel::C4ConsoleQtPropListModel()
{
header_font.setBold(true);
}
C4ConsoleQtPropListModel::~C4ConsoleQtPropListModel()
@ -487,19 +551,50 @@ void C4ConsoleQtPropListModel::SetPropList(class C4PropList *new_proplist)
proplist.SetPropList(new_proplist);
if (new_proplist)
{
// Always: Internal properties
auto new_properties = new_proplist->GetSortedLocalProperties();
properties.resize(new_properties.size());
internal_properties.resize(new_properties.size());
for (int32_t i = 0; i < new_properties.size(); ++i)
{
properties[i].parent_proplist = &proplist;
properties[i].name = new_properties[i];
properties[i].delegate_info.Set0();
properties[i].delegate = NULL; // init when needed
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();
}
}
else
{
properties.clear();
internal_properties.clear();
published_properties.clear();
}
QModelIndex topLeft = index(0, 0, QModelIndex());
QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1, QModelIndex());
@ -509,13 +604,22 @@ void C4ConsoleQtPropListModel::SetPropList(class C4PropList *new_proplist)
int C4ConsoleQtPropListModel::rowCount(const QModelIndex & parent) const
{
if (parent.isValid()) return 0;
return properties.size();
// Nothing loaded?
if (!proplist.getPropList()) return 0;
// Top level: Published and internal properties
if (!parent.isValid()) return 2;
// 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();
}
return 0;
}
int C4ConsoleQtPropListModel::columnCount(const QModelIndex & parent) const
{
if (parent.isValid()) return 0;
return 2; // Name + Data
}
@ -525,16 +629,36 @@ QVariant C4ConsoleQtPropListModel::headerData(int section, Qt::Orientation orien
if (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal)
{
if (section == 0) return QVariant(LoadResStr("IDS_CTL_NAME"));
if (section == 1) return QVariant(LoadResStr("IDS_CTL_VALUE"));
if (section == 1) return QVariant(LoadResStr("IDS_CNS_VALUE"));
}
return QVariant();
}
QVariant C4ConsoleQtPropListModel::data(const QModelIndex & index, int role) const
{
// Query latest data from prop list
// Anything loaded?
C4PropList *props = proplist.getPropList();
if (role == Qt::DisplayRole && props)
if (!props) return QVariant();
// Headers
QModelIndex parent = index.parent();
if (!parent.isValid())
{
if (!index.column())
{
if (role == Qt::DisplayRole)
{
if (index.row() == 0) return QVariant(LoadResStr("IDS_CNS_PROPERTIES"));
if (index.row() == 1) return QVariant(LoadResStr("IDS_CNS_INTERNAL"));
}
else if (role == Qt::FontRole)
{
return header_font;
}
}
return QVariant();
}
// Query latest data from prop list
if (role == Qt::DisplayRole)
{
Property *prop = static_cast<Property *>(index.internalPointer());
if (!prop) return QVariant();
@ -545,7 +669,7 @@ QVariant C4ConsoleQtPropListModel::data(const QModelIndex & index, int role) con
case 1: // Second col: Property value
{
C4Value v;
if (!props->GetPropertyByS(prop->name, &v)) return QVariant("???"); /* Property got removed between update calls */
props->GetPropertyByS(prop->name, &v); // Just display nil if property is not set
return QVariant(v.GetDataString().getData());
}
}
@ -556,18 +680,37 @@ QVariant C4ConsoleQtPropListModel::data(const QModelIndex & index, int role) con
QModelIndex C4ConsoleQtPropListModel::index(int row, int column, const QModelIndex &parent) const
{
// Flat model
if (parent.isValid()) return QModelIndex();
// In range?
if (column < 0 || column > 1) return QModelIndex();
if (row < 0 || row >= properties.size()) return QModelIndex();
const Property * prop = &properties[row];
return createIndex(row, column, const_cast<Property *>(prop));
// Top level index?
if (!parent.isValid())
{
// Top level has headers only
if (row == 0 || row == 1) return createIndex(row, column, NULL);
return QModelIndex();
}
// 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)
{
if (row < 0 || row >= property_list->size()) return QModelIndex();
const Property * prop = &(*property_list)[row];
return createIndex(row, column, const_cast<Property *>(prop));
}
}
return QModelIndex();
}
QModelIndex C4ConsoleQtPropListModel::parent(const QModelIndex &index) const
{
return QModelIndex();
Property *prop = static_cast<Property *>(index.internalPointer());
if (!prop) return QModelIndex();
// Find list to use
return createIndex(prop->is_internal ? 1 : 0, 0, NULL);
}
Qt::ItemFlags C4ConsoleQtPropListModel::flags(const QModelIndex &index) const

View File

@ -32,6 +32,7 @@ class C4PropertyPath
// TODO: For now just storing the path. May want to keep the path info later to allow validation/updating of values
StdCopyStrBuf path;
public:
enum PathType
{
PPT_Root = 0,
@ -56,10 +57,10 @@ class C4PropertyDelegate : public QObject
protected:
const class C4PropertyDelegateFactory *factory;
C4RefCntPointer<C4String> set_function;
public:
C4PropertyDelegate(const class C4PropertyDelegateFactory *factory)
: factory(factory) { }
C4PropertyDelegate(const class C4PropertyDelegateFactory *factory, const C4PropList *props);
virtual ~C4PropertyDelegate() { }
virtual void SetEditorData(QWidget *editor, const C4Value &val) const = 0;
@ -67,6 +68,8 @@ public:
virtual QWidget *CreateEditor(const class C4PropertyDelegateFactory *parent_delegate, QWidget *parent, const QStyleOptionViewItem &option) const = 0;
virtual void UpdateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option) const;
const char *GetSetFunction() const { return set_function.Get() ? set_function->GetCStr() : NULL; } // get name of setter function for this property
signals:
void EditorValueChangedSignal(QWidget *editor) const;
void EditingDoneSignal(QWidget *editor) const;
@ -74,8 +77,10 @@ signals:
class C4PropertyDelegateInt : public C4PropertyDelegate
{
private:
int32_t min, max, step;
public:
C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, const C4PropList *props=NULL);
C4PropertyDelegateInt(const class C4PropertyDelegateFactory *factory, const C4PropList *props);
void SetEditorData(QWidget *editor, const C4Value &val) const override;
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
@ -105,8 +110,9 @@ class C4PropertyDelegateEnum : public C4PropertyDelegate
public:
typedef C4PropertyDelegateEnumEditor Editor; // qmake doesn't like nested classes
struct Option
class Option
{
public:
C4RefCntPointer<C4String> name; // Display name in Editor enum dropdown box
C4RefCntPointer<C4String> option_key;
C4RefCntPointer<C4String> value_key;
@ -126,11 +132,14 @@ public:
};
private:
std::vector<Option> options;
protected:
void ReserveOptions(int32_t num);
public:
C4PropertyDelegateEnum(const class C4PropertyDelegateFactory *factory, int reserve_count = 0);
C4PropertyDelegateEnum(const class C4PropertyDelegateFactory *factory, const C4ValueArray &props);
C4PropertyDelegateEnum(const class C4PropertyDelegateFactory *factory, const 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);
void SetEditorData(QWidget *editor, const C4Value &val) const override;
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
@ -144,11 +153,18 @@ public slots:
void UpdateOptionIndex(Editor *editor, int idx) const;
};
// Select a definition
class C4PropertyDelegateDef : public C4PropertyDelegateEnum
{
public:
C4PropertyDelegateDef(const C4PropertyDelegateFactory *factory, const C4PropList *props);
};
// C4Value setting using an enum
class C4PropertyDelegateC4ValueEnum : public C4PropertyDelegateEnum
{
public:
C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory);
C4PropertyDelegateC4ValueEnum(const C4PropertyDelegateFactory *factory, const C4PropList *props);
};
class C4PropertyDelegateC4ValueInputEditor : public QWidget
@ -171,7 +187,7 @@ class C4PropertyDelegateC4ValueInput : public C4PropertyDelegate
public:
typedef C4PropertyDelegateC4ValueInputEditor Editor;
C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory *factory) : C4PropertyDelegate(factory) { }
C4PropertyDelegateC4ValueInput(const C4PropertyDelegateFactory *factory, const C4PropList *props) : C4PropertyDelegate(factory, props) { }
void SetEditorData(QWidget *editor, const C4Value &val) const override;
void SetModelData(QWidget *editor, const C4PropertyPath &property_path) const override;
@ -204,6 +220,7 @@ protected:
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
// Prop list view implemented as a model view
@ -220,19 +237,23 @@ public:
C4Value delegate_info;
C4PropertyDelegate *delegate;
bool about_to_edit;
bool is_internal;
Property() : parent_proplist(NULL), delegate(NULL), about_to_edit(false) {}
Property() : parent_proplist(NULL), delegate(NULL), about_to_edit(false), is_internal(false) {}
};
private:
C4Value proplist;
std::vector< Property > properties;
std::vector< Property > published_properties; // custom properties defined by definitions
std::vector< Property > internal_properties; // proplist-properties
QFont header_font;
public:
C4ConsoleQtPropListModel();
~C4ConsoleQtPropListModel();
void SetPropList(class C4PropList *new_proplist);
class C4PropList *GetPropList() const { return proplist.getPropList(); }
protected:
public:
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

View File

@ -496,7 +496,7 @@ bool C4ConsoleGUIState::CreateConsoleWindow(C4AbstractApp *app)
// 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);
//ui.propertyTable->verticalHeader()->setDefaultSectionSize(ui.propertyTable->fontMetrics().height()+4);
// Welcome page
InitWelcomeScreen();
@ -526,7 +526,9 @@ void C4ConsoleGUIState::Execute(bool redraw_only)
if (redraw_only)
{
// process only non-critical events
application->processEvents(QEventLoop::ExcludeUserInputEvents);
// redrawing only allowed during GameTick; prevent callbacks within a Qt event triggered by windows messaging
if (::Application.IsInGameTick())
application->processEvents(QEventLoop::ExcludeUserInputEvents);
}
else
{
@ -679,15 +681,22 @@ void C4ConsoleGUIState::PropertyDlgUpdate(C4EditCursorSelection &rSelection, boo
{
// Multi object selection: Hide property view; show info label
property_model->SetPropList(NULL);
ui.propertyTable->setVisible(false);
ui.propertyTable->setEnabled(false);
ui.selectionInfoLabel->setText(rSelection.GetDataString().getData());
}
else
{
// Single object selection: Show property view + Object info in label
property_model->SetPropList(rSelection.front().getPropList());
C4PropList *prev_list = property_model->GetPropList(), *new_list = rSelection.front().getPropList();
if (prev_list != new_list)
{
property_model->SetPropList(new_list);
ui.propertyTable->setFirstColumnSpanned(0, QModelIndex(), true);
ui.propertyTable->setFirstColumnSpanned(1, QModelIndex(), true);
ui.propertyTable->expand(property_model->index(0, 0, QModelIndex()));
}
ui.selectionInfoLabel->setText(rSelection.front().GetDataString(0).getData());
ui.propertyTable->setVisible(true);
ui.propertyTable->setEnabled(true);
}
// Function update in script combo box
if (force_function_update)
@ -707,10 +716,12 @@ void C4ConsoleGUIState::OnCreatorSelectionChanged(const QItemSelection & selecte
if (is_object_selection_updating || !definition_list_model) return; // only process callbacks from users interacting with widget
// Forward to EditCursor
C4Def *def;
for (const QModelIndex &item : deselected.indexes())
auto deselected_indexes = deselected.indexes();
for (const QModelIndex &item : deselected_indexes)
if ((def = definition_list_model->GetDefByModelIndex(item)))
::Console.EditCursor.RemoveFromSelection(def);
for (const QModelIndex &item : selected.indexes())
auto selected_indexes = deselected.indexes();
for (const QModelIndex &item : selected_indexes)
if ((def = definition_list_model->GetDefByModelIndex(item)))
::Console.EditCursor.AddToSelection(def);
::Console.EditCursor.OnSelectionChanged(true);

View File

@ -51,6 +51,7 @@ C4Application::C4Application():
QuitAfterGame(false),
CheckForUpdates(false),
restartAtEnd(false),
is_in_game_tick(false),
pGamePadControl(NULL),
AppState(C4AS_None),
pGameTimer(NULL)
@ -664,6 +665,7 @@ void C4Application::QuitGame()
void C4Application::GameTick()
{
is_in_game_tick = true;
// Exec depending on game state
switch (AppState)
{
@ -737,6 +739,7 @@ void C4Application::GameTick()
if (pGamePadControl) pGamePadControl->Execute();
break;
}
is_in_game_tick = false;
}
void C4Application::Draw()

View File

@ -36,6 +36,8 @@ public:
~C4Application();
// Flag for restarting the engine at the end
bool restartAtEnd;
// Flag set during game tick
bool is_in_game_tick;
// main System.ocg in working folder
C4Group SystemGroup;
C4MusicSystem MusicSystem;
@ -50,6 +52,7 @@ public:
void ClearCommandLine();
// Tick timing
void GameTick();
bool IsInGameTick() const { return is_in_game_tick; }
void Draw();
// System.ocg helper funcs
bool OpenSystemGroup() { return SystemGroup.IsOpen() || SystemGroup.Open(C4CFN_System); }

View File

@ -287,6 +287,25 @@ C4Def* C4DefList::GetDef(int32_t iIndex)
return NULL;
}
std::vector<C4Def*> C4DefList::GetAllDefs(C4String *filter_property) const
{
// Collect vector of all definitions
// Filter for those where property evaluates to true if filter_property!=NULL
std::vector<C4Def*> result;
result.reserve(filter_property ? 32 : table.size());
C4Value prop_val;
for (C4Def *def = FirstDef; def; def = def->Next)
{
if (filter_property)
{
if (!def->GetPropertyByS(filter_property, &prop_val)) continue;
if (!prop_val) continue;
}
result.push_back(def);
}
return result;
}
C4Def *C4DefList::GetByPath(const char *szPath)
{
// search defs

View File

@ -49,6 +49,7 @@ public:
bool fOverload = false, int32_t iMinProgress=0, int32_t iMaxProgress=0);
C4Def *ID2Def(C4ID id);
C4Def *GetDef(int32_t Index);
std::vector<C4Def*> GetAllDefs(C4String *filter_property=NULL) const;
C4Def *GetByPath(const char *szPath);
C4Def *GetByName(const StdStrBuf &);
int32_t GetDefCount();

View File

@ -472,6 +472,28 @@ std::vector< C4String * > C4PropList::GetSortedLocalProperties() const
return result;
}
std::vector< C4String * > C4PropList::GetSortedProperties(const char *prefix) const
{
// Return property list with descending into prototype
// But do not include Prototype property
std::vector< C4String * > result;
const C4PropList *p = this;
do
{
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))
result.push_back(pp->Key);
p = p->GetPrototype();
} while (p);
// Sort
std::sort(result.begin(), result.end(), [](const C4String *a, const C4String *b) -> bool
{
return strcmp(a->GetCStr(), b->GetCStr()) < 0;
});
return result;
}
const char * C4PropList::GetName() const
{
C4String * s = GetPropertyStr(P_Name);

View File

@ -134,6 +134,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 * > GetSortedProperties(const char *prefix) const;
bool operator==(const C4PropList &b) const;
#ifdef _DEBUG

View File

@ -250,6 +250,10 @@ C4StringTable::C4StringTable()
P[P_ValueKey] = "ValueKey";
P[P_Value] = "Value";
P[P_Delegate] = "Delegate";
P[P_Min] = "Min";
P[P_Max] = "Max";
P[P_Set] = "Set";
P[P_Options] = "Options";
P[DFA_WALK] = "WALK";
P[DFA_FLIGHT] = "FLIGHT";
P[DFA_KNEEL] = "KNEEL";
@ -266,7 +270,11 @@ C4StringTable::C4StringTable()
P[DFA_CONNECT] = "CONNECT";
P[DFA_PULL] = "PULL";
// Prevent the individual strings from being deleted, they are not created with new
for (unsigned int i = 0; i < P_LAST; ++i) P[i].IncRef();
for (unsigned int i = 0; i < P_LAST; ++i)
{
assert(P[i].GetCStr()); // all strings should be assigned
P[i].IncRef();
}
}
C4StringTable::~C4StringTable()

View File

@ -57,10 +57,22 @@ class C4RefCntPointer
public:
C4RefCntPointer(T* p): p(p) { IncRef(); }
C4RefCntPointer(): p(0) { }
C4RefCntPointer(const C4RefCntPointer<T> & r) : p(r.p) { IncRef(); }
template <class U> C4RefCntPointer(const C4RefCntPointer<U> & r): p(r.p) { IncRef(); }
// Move constructor
C4RefCntPointer(C4RefCntPointer<T> &&r) : p(r.p) { r.p = 0; }
template <class U> C4RefCntPointer(C4RefCntPointer<U> &&r): p(r.p) { r.p = 0; }
// Move assignment
C4RefCntPointer& operator = (C4RefCntPointer<T> &&r)
{
if (p != r.p)
{
DecRef();
p = r.p;
r.p = 0;
}
return *this;
}
template <class U> C4RefCntPointer& operator = (C4RefCntPointer<U> &&r)
{
if (p != r.p)
@ -69,7 +81,6 @@ public:
p = r.p;
r.p = 0;
}
return *this;
}
~C4RefCntPointer() { DecRef(); }
@ -83,6 +94,10 @@ public:
}
return *this;
}
C4RefCntPointer& operator = (const C4RefCntPointer<T>& r)
{
return *this = r.p;
}
template <class U> C4RefCntPointer& operator = (const C4RefCntPointer<U>& r)
{
return *this = r.p;
@ -459,6 +474,10 @@ enum C4PropertyName
P_ValueKey,
P_Value,
P_Delegate,
P_Min,
P_Max,
P_Set,
P_Options,
// Default Action Procedures
DFA_WALK,
DFA_FLIGHT,