mirror of
https://github.com/status-im/dotherside.git
synced 2025-02-12 04:26:43 +00:00
Finish DQmlSlotSignalCodeGeneration
This commit is contained in:
commit
f9f42e5b6a
@ -12,5 +12,6 @@ add_library(${PROJECT_NAME} STATIC
|
||||
qapplication.d
|
||||
qmodelindex.d
|
||||
qabstractlistmodel.d
|
||||
qobjectgenerators.d
|
||||
)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
@ -8,3 +8,7 @@ public import qquickview;
|
||||
public import qmetatype;
|
||||
public import qmodelindex;
|
||||
public import qabstractlistmodel;
|
||||
public import qobjectgenerators;
|
||||
public import std.traits;
|
||||
public import std.algorithm;
|
||||
public import std.stdio;
|
||||
|
@ -19,6 +19,7 @@ class QAbstractListModel : QObject
|
||||
&roleNamesCallback,
|
||||
&flagsCallback,
|
||||
&headerDataCallback);
|
||||
qobjectInit();
|
||||
}
|
||||
|
||||
~this()
|
||||
|
@ -32,36 +32,11 @@ public enum QMetaType
|
||||
Unknown = 0,
|
||||
Bool = 1,
|
||||
Int = 2,
|
||||
Double = 6,
|
||||
String = 10,
|
||||
VoidStr = 31,
|
||||
Float = 38,
|
||||
QObject = 39,
|
||||
QVariant = 41,
|
||||
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;
|
||||
}
|
||||
|
@ -20,7 +20,10 @@ public class QObject
|
||||
{
|
||||
this.disableDosCalls = disableDosCalls;
|
||||
if (!this.disableDosCalls)
|
||||
{
|
||||
dos_qobject_create(this.vptr, cast(void*)this, &staticSlotCallback);
|
||||
qobjectInit();
|
||||
}
|
||||
}
|
||||
|
||||
~this()
|
||||
@ -37,6 +40,9 @@ public class QObject
|
||||
return this.vptr;
|
||||
}
|
||||
|
||||
protected void qobjectInit()
|
||||
{}
|
||||
|
||||
protected void onSlotCalled(QVariant slotName, QVariant[] parameters)
|
||||
{
|
||||
}
|
||||
@ -119,5 +125,5 @@ public class QObject
|
||||
}
|
||||
|
||||
protected void* vptr;
|
||||
private bool disableDosCalls;
|
||||
protected bool disableDosCalls;
|
||||
}
|
||||
|
265
D/DQml/qobjectgenerators.d
Normal file
265
D/DQml/qobjectgenerators.d
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,59 +2,43 @@ import dqml;
|
||||
import contactlist;
|
||||
import std.stdio;
|
||||
|
||||
@QtProperty(QObject.stringof, "contactList", "contactList", null, null)
|
||||
class ApplicationLogic : QObject
|
||||
{
|
||||
mixin InjectQObjectMacro;
|
||||
mixin(Q_OBJECT!(ApplicationLogic));
|
||||
|
||||
this(QApplication app)
|
||||
{
|
||||
this.m_app = app;
|
||||
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;
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void onLoadTriggered()
|
||||
{
|
||||
writefln("Load Triggered");
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void onSaveTriggered()
|
||||
{
|
||||
writefln("Save Triggered");
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void onExitTriggered()
|
||||
{
|
||||
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 ContactList m_contactList;
|
||||
}
|
||||
|
@ -1,69 +1,55 @@
|
||||
import dqml;
|
||||
|
||||
@QtProperty(string.stringof, "firstName", "firstName", "setFirstName", "firstNameChanged")
|
||||
@QtProperty(string.stringof, "lastName", "lastName", "setLastName", "lastNameChanged")
|
||||
class Contact : QObject
|
||||
{
|
||||
mixin InjectQObjectMacro;
|
||||
mixin(Q_OBJECT!(Contact));
|
||||
|
||||
this(string firstName = "", string lastName = "")
|
||||
{
|
||||
this.m_firstName = firstName;
|
||||
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()
|
||||
{
|
||||
return this.m_firstName;
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void setFirstName(string firstName)
|
||||
{
|
||||
if (this.m_firstName != firstName)
|
||||
{
|
||||
this.m_firstName = firstName;
|
||||
emit("firstNameChanged", firstName);
|
||||
firstNameChanged(firstName);
|
||||
}
|
||||
}
|
||||
|
||||
@QtSignal()
|
||||
public void firstNameChanged(string);
|
||||
|
||||
@QtSlot()
|
||||
public string lastName()
|
||||
{
|
||||
return this.m_lastName;
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void setLastName(string lastName)
|
||||
{
|
||||
if (this.m_lastName != lastName)
|
||||
{
|
||||
this.m_lastName = lastName;
|
||||
emit ("lastNameChanged", lastName);
|
||||
lastNameChanged(lastName);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void onSlotCalled(QVariant slotName, QVariant[] arguments)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@QtSignal()
|
||||
public void lastNameChanged(string);
|
||||
|
||||
private string m_firstName;
|
||||
private string m_lastName;
|
||||
|
@ -5,13 +5,14 @@ import std.algorithm;
|
||||
|
||||
class ContactList : QAbstractListModel
|
||||
{
|
||||
mixin InjectQObjectMacro;
|
||||
mixin(Q_OBJECT!(ContactList));
|
||||
|
||||
this()
|
||||
{
|
||||
this.m_contacts = [];
|
||||
this.m_roleNames[Roles.FirstName] = "firstName";
|
||||
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)
|
||||
@ -50,6 +51,7 @@ class ContactList : QAbstractListModel
|
||||
return this.m_roleNames;
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void add(string firstName, string lastName)
|
||||
{
|
||||
auto index = new QModelIndex();
|
||||
@ -60,6 +62,7 @@ class ContactList : QAbstractListModel
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void del(int pos)
|
||||
{
|
||||
if (pos < 0 || pos >= rowCount())
|
||||
@ -71,21 +74,6 @@ class ContactList : QAbstractListModel
|
||||
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 string[int] m_roleNames;
|
||||
private enum Roles : int { FirstName = 0, LastName};
|
||||
|
@ -1,23 +1,25 @@
|
||||
import dqml;
|
||||
|
||||
@QtProperty(string.stringof, "name", "getName", "setName", "nameChanged")
|
||||
@QtProperty(string.stringof, "surname", "getSurname", "setSurname", "surnameChanged")
|
||||
class Contact : QObject
|
||||
{
|
||||
mixin InjectQObjectMacro;
|
||||
mixin(Q_OBJECT!(Contact));
|
||||
|
||||
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()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void setName(string 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())
|
||||
{
|
||||
case "getName":
|
||||
arguments[0].setValue(getName());
|
||||
break;
|
||||
case "setName":
|
||||
setName(arguments[1].toString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return m_surname;
|
||||
}
|
||||
|
||||
@QtSlot()
|
||||
public void setSurname(string surname)
|
||||
{
|
||||
m_surname = surname;
|
||||
}
|
||||
|
||||
@QtSignal()
|
||||
void surnameChanged(string surname);
|
||||
|
||||
private string m_name;
|
||||
private string m_surname;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ void main()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto app = new QGuiApplication();
|
||||
auto app = new QApplication();
|
||||
scope(exit) destroy(app);
|
||||
|
||||
auto contact = new Contact();
|
||||
|
Loading…
x
Reference in New Issue
Block a user