Finish 0.4.1

This commit is contained in:
Filippo Cucchetto 2015-05-10 15:57:06 +02:00
commit 4a1f7d6948
13 changed files with 353 additions and 131 deletions

View File

@ -1,4 +1,10 @@
# Version 4.0
# Version 0.4.1
* [DQml] Added support for code generation of slots, signals and properties by using custom UDAs
* [DQml] Updated the examples with the new attributes for code generation
* [NimQml] Little fix for adding fixing compilation with Nim 0.11.0
# Version 0.4.0
* [DQml] Inheritance of slots and signals is now supported
* [DQml] Added support for QAbstractListModel subclasses
* [DQml] Put on par the examples for matching those in NimQml
@ -7,19 +13,19 @@
* [DOtherSide] Initial support for windows build
* [DOtherSide] Removed most warnings and code cleanup
# Version 3.0
* [NimQml] Added support for QAbstractListModel subclasses
# Version 0.3.0
* [NimQml] Added support for QAbstractListModel subclasses
* [NimQml] Fixed QtObject macro wrong reorder of the methods and proc declaration (thanks to Will)
* [NimQml] Added new ContactApp example
* [NimQml] Added new ContactApp example
* [NimQml] Added optional support for finalizers
* [DOtherSide] Added support for injecting the DynamicQObject as behaviour to QObject subclasses
* [DotherSide] Added support for QAbstractListModel subclasses
# Version 2.0
# Version 0.2.0
* [DQml] Initial support for properties creation
* [NimQml] Added new macro syntax for creating QObject derived object (thanks to Will)
# Version 1.0
# Version 0.1.0
* [DOtherSide] Initial version with support for QObject Slot, Signal and Properties creation
* [DQml] Initial support for Slot and Signal creation
* [NimQml] Initial support for Slot, Signal and Properties creation

View File

@ -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})

View File

@ -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;

View File

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

View File

@ -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;
}

View File

@ -20,7 +20,10 @@ public class QObject
{
this.disableDosCalls = disableDosCalls;
if (!this.disableDosCalls)
{
dos_qobject_create(this.vptr, cast(void*)this, &staticSlotCallback);
qobjectInit();
}
}
~this()
@ -36,6 +39,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
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 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;
}

View File

@ -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;

View File

@ -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};

View File

@ -1,23 +1,25 @@
import dqml;
@QtProperty(string.stringof, "name", "getName", "setName", "nameChanged")
@QtProperty(string.stringof, "surname", "getSurname", "setSurname", "surnameChanged")
class Contact : QObject
{
this()
{
registerSlot("getName", [QMetaType.String]);
registerSlot("setName", [QMetaType.Void, QMetaType.String]);
registerSignal("nameChanged", [QMetaType.String]);
registerProperty("name", QMetaType.String, "getName", "setName", "nameChanged");
}
mixin InjectQObjectMacro;
mixin(Q_OBJECT!(Contact));
~this() {}
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;
}

View File

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

View File

@ -710,8 +710,8 @@ proc roleNamesCallback(modelObject: ptr QAbstractListModelObj, hash: RawQHashInt
debugMsg("QAbstractListModel", "roleNamesCallback")
let model = cast[QAbstractListModel](modelObject)
let table = model.roleNames()
for pair in table.pairs:
dos_qhash_int_qbytearray_insert(hash, pair.key, pair.val)
for key, val in table.pairs:
dos_qhash_int_qbytearray_insert(hash, key, val)
method flags*(model: QAbstractListModel, index: QModelIndex): QtItemFlag =
## Return the item flags and the given index