Finish DQmlSlotSignalCodeGeneration

This commit is contained in:
Filippo Cucchetto 2015-05-10 15:49:04 +02:00
commit f9f42e5b6a
11 changed files with 339 additions and 123 deletions

View File

@ -12,5 +12,6 @@ add_library(${PROJECT_NAME} STATIC
qapplication.d qapplication.d
qmodelindex.d qmodelindex.d
qabstractlistmodel.d qabstractlistmodel.d
qobjectgenerators.d
) )
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -8,3 +8,7 @@ public import qquickview;
public import qmetatype; public import qmetatype;
public import qmodelindex; public import qmodelindex;
public import qabstractlistmodel; public import qabstractlistmodel;
public import qobjectgenerators;
public import std.traits;
public import std.algorithm;
public import std.stdio;

View File

@ -19,6 +19,7 @@ class QAbstractListModel : QObject
&roleNamesCallback, &roleNamesCallback,
&flagsCallback, &flagsCallback,
&headerDataCallback); &headerDataCallback);
qobjectInit();
} }
~this() ~this()

View File

@ -32,36 +32,11 @@ public enum QMetaType
Unknown = 0, Unknown = 0,
Bool = 1, Bool = 1,
Int = 2, Int = 2,
Double = 6,
String = 10, String = 10,
VoidStr = 31, VoidStr = 31,
Float = 38,
QObject = 39, QObject = 39,
QVariant = 41, QVariant = 41,
Void = 43 Void = 43
} }
public QMetaType GetMetaType(T)()
if (is (T == int)
|| is (T == bool)
|| is (T == string)
|| is (T == void)
|| is (T == QObject)
|| is (T == QVariant)
|| is (T == void*))
{
static if (is (T == bool))
return QMetaType.Bool;
else if (is (T == int))
return QMetaType.Int;
else if (is( T == void))
return QMetaType.Void;
else if (is (T == string))
return QMetaType.String;
else if (is (T == QObject))
return QMetaType.QObject;
else if (is (T == QVariant))
return QMetaType.QVariant;
else if (is (T == void*))
return QMetaType.VoidStar;
else
return QMetaType.Unknown;
}

View File

@ -20,7 +20,10 @@ public class QObject
{ {
this.disableDosCalls = disableDosCalls; this.disableDosCalls = disableDosCalls;
if (!this.disableDosCalls) if (!this.disableDosCalls)
{
dos_qobject_create(this.vptr, cast(void*)this, &staticSlotCallback); dos_qobject_create(this.vptr, cast(void*)this, &staticSlotCallback);
qobjectInit();
}
} }
~this() ~this()
@ -37,6 +40,9 @@ public class QObject
return this.vptr; return this.vptr;
} }
protected void qobjectInit()
{}
protected void onSlotCalled(QVariant slotName, QVariant[] parameters) protected void onSlotCalled(QVariant slotName, QVariant[] parameters)
{ {
} }
@ -119,5 +125,5 @@ public class QObject
} }
protected void* vptr; protected void* vptr;
private bool disableDosCalls; protected bool disableDosCalls;
} }

265
D/DQml/qobjectgenerators.d Normal file
View File

@ -0,0 +1,265 @@
import std.traits;
import std.algorithm;
import std.string;
import std.stdio;
struct QtProperty
{
public string type;
public string name;
public string read;
public string write;
public string notify;
this(string type, string name, string read, string write, string notify)
{
this.type = type;
this.name = name;
this.read = read;
this.write = write;
this.notify = notify;
}
}
struct QtSlot {};
struct QtSignal {};
string GenerateVariantConversionCall(string typeName)
{
switch (typeName)
{
case "string":
return ".toString()";
case "int":
return ".toInt()";
case "bool":
return ".toBool()";
case "float":
return ".toFloat()";
case "double":
return ".toDouble()";
case "QVariant":
return "";
default:
throw new Exception("Unknown conversion function from Qvariant to " ~ typeName);
}
}
string GenerateArgumentList(string[] typeNames)
{
string result = "";
for (int i = 0; i < typeNames.length; ++i)
{
auto typeName = typeNames[i];
auto variantCall = GenerateVariantConversionCall(typeName);
result ~= i > 0 ? "," : "";
result ~= format("arguments[%d]%s", i+1, variantCall);
}
return result;
}
string GenerateSlotCall(FunctionInfo info)
{
auto args = GenerateArgumentList(info.parameterTypes);
auto call = format("%s(%s)", info.name, args);
auto formatStr = info.returnType != "void" ? "arguments[0].setValue(%s)" : "%s";
return format(formatStr, call);
}
string GenerateCaseBlock(FunctionInfo info)
{
string result = "";
result ~= format("case \"%s\":\n", info.name);
result ~= format("%s;\n", GenerateSlotCall(info));
result ~= "break;\n";
return result;
}
string GenerateOnSlotCalled(QtInfo info)
{
string result = "protected override void onSlotCalled(QVariant slotName, QVariant[] arguments)\n";
result ~= "{\n";
result ~= "switch(slotName.toString())\n";
result ~= "{\n";
foreach (slot; info.slots)
result ~= GenerateCaseBlock(slot);
result ~= "default: super.onSlotCalled(slotName, arguments);\n";
result ~= "}\n"; //
result ~= "}";
return result;
}
string GenerateSignalCall(FunctionInfo info)
{
string args = "";
string vars = "";
for (int i = 0; i < info.parameterTypes.length; ++i) {
if (i > 0) {
args ~= ",";
vars ~= ",";
}
args ~= format("%s val%d", info.parameterTypes[i], i);
vars ~= format("val%d", i);
}
auto result = format("public %s %s(%s) { emit(\"%s\", %s); }", info.returnType, info.name, args, info.name, vars);
return result;
}
string GenerateQtSignals(QtInfo info)
{
string result = "";
foreach (signal; info.signals)
result ~= GenerateSignalCall(signal) ~ "\n";
return result;
}
string GenerateMetaType(string typeName)
{
switch(typeName)
{
case "void":
return "QMetaType.Void";
case "int":
return "QMetaType.Int";
case "string":
return "QMetaType.String";
case "QObject":
return "QMetaType.QObject";
case "QVariant":
return "QMetaType.QVariant";
case "bool":
return "QMetaType.Bool";
case "float":
return "QMetaType.Float";
case "double":
return "QMetaType.Double";
default:
throw new Exception(format("Unknown conversion from %s to QMetaType", typeName));
}
}
string GenerateMetaTypesListForSlot(FunctionInfo info)
{
string result = GenerateMetaType(info.returnType);
result ~= ", ";
result ~= GenerateMetaTypesListForSignal(info);
return result;
}
string GenerateMetaTypesListForSignal(FunctionInfo info)
{
string result = "";
for (int i = 0; i < info.parameterTypes.length; ++i)
{
if (i > 0)
result ~= ", ";
result ~= GenerateMetaType(info.parameterTypes[i]);
}
return result;
}
string GenerateQObjectInit(QtInfo info)
{
string result = "";
result ~= "protected override void qobjectInit()\n";
result ~= "{\n";
foreach (slot; info.slots)
{
auto metaTypes = GenerateMetaTypesListForSlot(slot);
result ~= format("registerSlot(\"%s\", [%s]);\n", slot.name, metaTypes);
}
foreach (signal; info.signals)
{
auto metaTypes = GenerateMetaTypesListForSignal(signal);
result ~= format("registerSignal(\"%s\", [%s]);\n", signal.name, metaTypes);
}
foreach (property; info.properties)
{
result ~= format("registerProperty(\"%s\", %s, \"%s\", \"%s\", \"%s\");\n", property.name, GenerateMetaType(property.type), property.read, property.write, property.notify);
}
result ~= "super.qobjectInit();\n";
result ~= "}";
return result;
}
struct FunctionInfo
{
string name;
string returnType;
string[] parameterTypes;
}
struct QtInfo
{
FunctionInfo[] slots;
FunctionInfo[] signals;
QtProperty[] properties;
}
mixin template InjectQObjectMacro()
{
private static QtInfo GetQtUDA(T)()
{
QtInfo result;
foreach (attribute; __traits(getAttributes, T)) {
static if (is (typeof(attribute) == QtProperty)) {
result.properties ~= attribute;
}
}
foreach (member; __traits(allMembers, T))
{
static if (__traits(compiles, __traits(getMember, T, member))
&& isSomeFunction!(__traits(getMember, T, member)))
{
// Retrieve the UDA
auto attributes = __traits(getAttributes, __traits(getMember, T, member));
// Turn the tuple in an array of strings
string[] attributeNames;
foreach (attribute; attributes)
attributeNames ~= typeof(attribute).stringof;
bool isSlot = attributeNames.canFind("QtSlot");
bool isSignal = attributeNames.canFind("QtSignal");
// Extract the Function Return Type and Arguments
if (isSlot || isSignal)
{
FunctionInfo info;
info.name = member;
info.returnType = ReturnType!(__traits(getMember, T, member)).stringof;
foreach (param; ParameterTypeTuple!(__traits(getMember, T, member)))
info.parameterTypes ~= param.stringof;
if (isSlot)
result.slots ~= info;
if (isSignal)
result.signals ~= info;
}
}
}
return result;
}
private static string Q_OBJECT(T)()
{
string result = "";
auto info = T.GetQtUDA!(T);
result ~= GenerateOnSlotCalled(info) ~ "\n";
result ~= GenerateQObjectInit(info) ~ "\n";
result ~= GenerateQtSignals(info) ~ "\n";
return result;
}
}

View File

@ -2,59 +2,43 @@ import dqml;
import contactlist; import contactlist;
import std.stdio; import std.stdio;
@QtProperty(QObject.stringof, "contactList", "contactList", null, null)
class ApplicationLogic : QObject class ApplicationLogic : QObject
{ {
mixin InjectQObjectMacro;
mixin(Q_OBJECT!(ApplicationLogic));
this(QApplication app) this(QApplication app)
{ {
this.m_app = app; this.m_app = app;
this.m_contactList = new ContactList(); this.m_contactList = new ContactList();
this.registerSlot("contactList", [QMetaType.QObject]);
this.registerSlot("onLoadTriggered", [QMetaType.Void]);
this.registerSlot("onSaveTriggered", [QMetaType.Void]);
this.registerSlot("onExitTriggered", [QMetaType.Void]);
this.registerProperty("contactList", QMetaType.QObject, "contactList", null, null);
} }
public ContactList contactList()
@QtSlot()
public QObject contactList()
{ {
return this.m_contactList; return this.m_contactList;
} }
@QtSlot()
public void onLoadTriggered() public void onLoadTriggered()
{ {
writefln("Load Triggered"); writefln("Load Triggered");
} }
@QtSlot()
public void onSaveTriggered() public void onSaveTriggered()
{ {
writefln("Save Triggered"); writefln("Save Triggered");
} }
@QtSlot()
public void onExitTriggered() public void onExitTriggered()
{ {
this.m_app.quit(); this.m_app.quit();
} }
protected override void onSlotCalled(QVariant slotName, QVariant[] arguments)
{
switch(slotName.toString())
{
case "contactList":
return arguments[0].setValue(contactList());
case "onExitTriggered":
onExitTriggered();
break;
case "onSaveTriggered":
onSaveTriggered();
break;
case "onLoadTriggered":
onLoadTriggered();
break;
default:
break;
}
}
private QApplication m_app; private QApplication m_app;
private ContactList m_contactList; private ContactList m_contactList;
} }

View File

@ -1,69 +1,55 @@
import dqml; import dqml;
@QtProperty(string.stringof, "firstName", "firstName", "setFirstName", "firstNameChanged")
@QtProperty(string.stringof, "lastName", "lastName", "setLastName", "lastNameChanged")
class Contact : QObject class Contact : QObject
{ {
mixin InjectQObjectMacro;
mixin(Q_OBJECT!(Contact));
this(string firstName = "", string lastName = "") this(string firstName = "", string lastName = "")
{ {
this.m_firstName = firstName; this.m_firstName = firstName;
this.m_lastName = lastName; this.m_lastName = lastName;
this.registerSlot("firstName", [QMetaType.String]);
this.registerSlot("setFirstName", [QMetaType.Void, QMetaType.String]);
this.registerSignal("firstNameChanged", [QMetaType.String]);
this.registerSlot("lastName", [QMetaType.String]);
this.registerSlot("setLastName", [QMetaType.Void, QMetaType.String]);
this.registerSignal("lastNameChanged", [QMetaType.String]);
this.registerProperty("firstName", QMetaType.String, "firstName", "setFirstName", "firstNameChanged");
this.registerProperty("lastName", QMetaType.String, "lastName", "setLastName", "lastNameChanged");
} }
@QtSlot()
public string firstName() public string firstName()
{ {
return this.m_firstName; return this.m_firstName;
} }
@QtSlot()
public void setFirstName(string firstName) public void setFirstName(string firstName)
{ {
if (this.m_firstName != firstName) if (this.m_firstName != firstName)
{ {
this.m_firstName = firstName; this.m_firstName = firstName;
emit("firstNameChanged", firstName); firstNameChanged(firstName);
} }
} }
@QtSignal()
public void firstNameChanged(string);
@QtSlot()
public string lastName() public string lastName()
{ {
return this.m_lastName; return this.m_lastName;
} }
@QtSlot()
public void setLastName(string lastName) public void setLastName(string lastName)
{ {
if (this.m_lastName != lastName) if (this.m_lastName != lastName)
{ {
this.m_lastName = lastName; this.m_lastName = lastName;
emit ("lastNameChanged", lastName); lastNameChanged(lastName);
} }
} }
protected override void onSlotCalled(QVariant slotName, QVariant[] arguments) @QtSignal()
{ public void lastNameChanged(string);
switch (slotName.toString())
{
case "firstName":
arguments[0].setValue(firstName());
break;
case "setFirstName":
setFirstName(arguments[1].toString());
break;
case "lastName":
arguments[0].setValue(lastName());
break;
case "setLastName":
setLastName(arguments[1].toString());
break;
default:
break;
}
}
private string m_firstName; private string m_firstName;
private string m_lastName; private string m_lastName;

View File

@ -5,13 +5,14 @@ import std.algorithm;
class ContactList : QAbstractListModel class ContactList : QAbstractListModel
{ {
mixin InjectQObjectMacro;
mixin(Q_OBJECT!(ContactList));
this() this()
{ {
this.m_contacts = []; this.m_contacts = [];
this.m_roleNames[Roles.FirstName] = "firstName"; this.m_roleNames[Roles.FirstName] = "firstName";
this.m_roleNames[Roles.LastName] = "lastName"; this.m_roleNames[Roles.LastName] = "lastName";
this.registerSlot("add", [QMetaType.Void, QMetaType.String, QMetaType.String]);
this.registerSlot("del", [QMetaType.Void, QMetaType.Int]);
} }
public override int rowCount(QModelIndex parent = null) public override int rowCount(QModelIndex parent = null)
@ -50,6 +51,7 @@ class ContactList : QAbstractListModel
return this.m_roleNames; return this.m_roleNames;
} }
@QtSlot()
public void add(string firstName, string lastName) public void add(string firstName, string lastName)
{ {
auto index = new QModelIndex(); auto index = new QModelIndex();
@ -60,6 +62,7 @@ class ContactList : QAbstractListModel
endInsertRows(); endInsertRows();
} }
@QtSlot()
public void del(int pos) public void del(int pos)
{ {
if (pos < 0 || pos >= rowCount()) if (pos < 0 || pos >= rowCount())
@ -71,21 +74,6 @@ class ContactList : QAbstractListModel
endRemoveRows(); endRemoveRows();
} }
protected override void onSlotCalled(QVariant slotName, QVariant[] arguments)
{
switch (slotName.toString())
{
case "add":
add(arguments[1].toString(), arguments[2].toString());
break;
case "del":
del(arguments[1].toInt());
break;
default:
break;
}
}
private Contact[] m_contacts; private Contact[] m_contacts;
private string[int] m_roleNames; private string[int] m_roleNames;
private enum Roles : int { FirstName = 0, LastName}; private enum Roles : int { FirstName = 0, LastName};

View File

@ -1,23 +1,25 @@
import dqml; import dqml;
@QtProperty(string.stringof, "name", "getName", "setName", "nameChanged")
@QtProperty(string.stringof, "surname", "getSurname", "setSurname", "surnameChanged")
class Contact : QObject class Contact : QObject
{ {
mixin InjectQObjectMacro;
mixin(Q_OBJECT!(Contact));
this() this()
{ {}
registerSlot("getName", [QMetaType.String]);
registerSlot("setName", [QMetaType.Void, QMetaType.String]);
registerSignal("nameChanged", [QMetaType.String]);
registerProperty("name", QMetaType.String, "getName", "setName", "nameChanged");
}
~this() {}
~this()
{}
@QtSlot()
public string getName() public string getName()
{ {
return m_name; return m_name;
} }
@QtSlot()
public void setName(string name) public void setName(string name)
{ {
if (m_name != name) if (m_name != name)
@ -27,20 +29,24 @@ class Contact : QObject
} }
} }
protected override void onSlotCalled(QVariant slotName, QVariant[] arguments) @QtSignal()
public void nameChanged(string name);
@QtSlot()
public string getSurname()
{ {
switch (slotName.toString()) return m_surname;
{
case "getName":
arguments[0].setValue(getName());
break;
case "setName":
setName(arguments[1].toString());
break;
default:
break;
}
} }
@QtSlot()
public void setSurname(string surname)
{
m_surname = surname;
}
@QtSignal()
void surnameChanged(string surname);
private string m_name; private string m_name;
private string m_surname;
} }

View File

@ -6,7 +6,7 @@ void main()
{ {
try try
{ {
auto app = new QGuiApplication(); auto app = new QApplication();
scope(exit) destroy(app); scope(exit) destroy(app);
auto contact = new Contact(); auto contact = new Contact();