Finish 0.2.0
This commit is contained in:
commit
b19d3510ff
|
@ -0,0 +1,249 @@
|
||||||
|
:Authors:
|
||||||
|
Filippo Cucchetto <filippocucchetto@gmail.com>
|
||||||
|
|
||||||
|
Will Szumski <will@cowboycoders.org>
|
||||||
|
:Version: 0.2.0
|
||||||
|
:Date: 2015/01/02
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
-----------
|
||||||
|
The NimQml module adds Qt Qml bindings to the Nim programming language
|
||||||
|
allowing you to create new modern UI by mixing the Qml declarative syntax
|
||||||
|
and the Nim imperative language.
|
||||||
|
|
||||||
|
The NimQml is made by two components:
|
||||||
|
* The DOtherSide C++ shared library
|
||||||
|
* The NimQml Nim module
|
||||||
|
|
||||||
|
This first component implements the glue code necessary for
|
||||||
|
communicating with the Qt C++ library, the latter module wraps
|
||||||
|
the libDOtherSide exported symbols in Nim
|
||||||
|
|
||||||
|
Building
|
||||||
|
--------
|
||||||
|
At the time of writing the DOtherSide C++ library must be compiled
|
||||||
|
and installed manually from source.
|
||||||
|
|
||||||
|
First clone the DOtherSide git repo
|
||||||
|
::
|
||||||
|
git clone https://github.com/filcuc/DOtherSide
|
||||||
|
|
||||||
|
than you can proceed with the common CMake build steps
|
||||||
|
|
||||||
|
::
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
|
||||||
|
If everything goes correctly, you'll have built both
|
||||||
|
the DOtherSide C++ library and the Nim examples
|
||||||
|
|
||||||
|
Installation
|
||||||
|
----------
|
||||||
|
The installation is not mandatory, in fact you could try
|
||||||
|
the built Nim example in the following way
|
||||||
|
::
|
||||||
|
cd path/to/build/dir
|
||||||
|
cd Nim/Examples/HelloWorld
|
||||||
|
export LD_LIBRARY_PATH=path/to/libDOtherSide.so
|
||||||
|
./HelloWorld
|
||||||
|
|
||||||
|
The DOtherSide project is made of two components
|
||||||
|
1. The DOtherSide C++ lib
|
||||||
|
2. The NimQml module
|
||||||
|
|
||||||
|
You can procede with the installation of the C++ library
|
||||||
|
in the following way
|
||||||
|
::
|
||||||
|
cd to/build/dir
|
||||||
|
make install
|
||||||
|
or by manually copying the library in your system lib directory
|
||||||
|
::
|
||||||
|
sudo cp build/dir/path/DOtherSide/libDOtherSide.so /usr/lib
|
||||||
|
|
||||||
|
For the NimQml module you can use the ``nimble`` package manager
|
||||||
|
::
|
||||||
|
nimble install NimQml
|
||||||
|
|
||||||
|
or
|
||||||
|
::
|
||||||
|
cd to/build/dir/Nim/NimQml
|
||||||
|
nimble install
|
||||||
|
|
||||||
|
|
||||||
|
Example 1: HelloWorld
|
||||||
|
----------
|
||||||
|
As usual lets start with an HelloWorld example.
|
||||||
|
Most of the NimQml projects are made by one or more nim and qml
|
||||||
|
files. Usually the .nim files contains your app logic and data
|
||||||
|
layer. The qml files contain the presentation layer and expose
|
||||||
|
the data in your nim files.
|
||||||
|
|
||||||
|
``Examples/HelloWorld/main.nim``
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:file: ../Examples/HelloWorld/main.nim
|
||||||
|
|
||||||
|
``Examples/HelloWorld/main.qml``
|
||||||
|
|
||||||
|
.. code-block:: qml
|
||||||
|
:file: ../Examples/HelloWorld/main.qml
|
||||||
|
|
||||||
|
The following example shows the basic steps of each NimQml app
|
||||||
|
1. Create the QApplication for initializing the Qt runtime
|
||||||
|
2. Create the QQmlApplicationEngine and load your main .qml file
|
||||||
|
3. Call the ``exec`` proc of the QApplication instance for starting
|
||||||
|
the Qt event loop
|
||||||
|
|
||||||
|
Example 2: exposing data to Qml
|
||||||
|
------------------------------------
|
||||||
|
The previous example shown you how to create a simple application
|
||||||
|
window and how to startup the Qt event loop.
|
||||||
|
|
||||||
|
It's time to explore how to pass data to Qml, but lets see the
|
||||||
|
example code first:
|
||||||
|
|
||||||
|
``Examples/SimpleData/main.nim``
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:file: ../Examples/SimpleData/main.nim
|
||||||
|
|
||||||
|
``Examples/SimpleData/main.qml``
|
||||||
|
|
||||||
|
.. code-block:: qml
|
||||||
|
:file: ../Examples/SimpleData/main.qml
|
||||||
|
|
||||||
|
The following example shows how to expose simple data types to Qml:
|
||||||
|
1. Create a QVariant and set its internal value.
|
||||||
|
2. Create a property in the Qml root context with a given name.
|
||||||
|
|
||||||
|
Once a property is set through the ``setContextProperty`` proc, it's available
|
||||||
|
globally in all the Qml script loaded by the current engine (see the official Qt
|
||||||
|
documentation for more details about the engine and context objects)
|
||||||
|
|
||||||
|
At the time of writing the QVariant class support the following types:
|
||||||
|
* int
|
||||||
|
* string
|
||||||
|
* bool
|
||||||
|
* QObject derived classes
|
||||||
|
|
||||||
|
Example 3: exposing complex data and procedures to Qml
|
||||||
|
----------------------------------------------------------
|
||||||
|
As seen by the second example, simple data is fine. However most
|
||||||
|
applications need to expose complex data, functions and
|
||||||
|
update the view when something changes in the data layer.
|
||||||
|
This is achieved by creating an object that derives from QObject.
|
||||||
|
|
||||||
|
A QObject is made of :
|
||||||
|
1. ``Slots``: slots are functions that could be called from the qml engine and/or connected to Qt signals
|
||||||
|
2. ``Signals``: signals allow the sending of events and be connected to slots
|
||||||
|
3. ``Properties``: properties allow the passing of data to
|
||||||
|
the Qml view and make it aware of changes in the data layer
|
||||||
|
|
||||||
|
A QObject property is made of three things:
|
||||||
|
* a read slot: a method that returns the current value of the property
|
||||||
|
* a write slot: a method that sets the value of the property
|
||||||
|
* a notify signal: emitted when the current value of the property is changed
|
||||||
|
|
||||||
|
We'll start by looking at the main.nim file
|
||||||
|
|
||||||
|
``Examples/SlotsAndProperties/main.nim``
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:file: ../Examples/SlotsAndProperties/main.nim
|
||||||
|
|
||||||
|
Here, nothing special happens except:
|
||||||
|
1. The creation of Contact object
|
||||||
|
2. The injection of the Contact object to the Qml root context
|
||||||
|
using the ``setContextProperty`` as seen in the previous
|
||||||
|
example
|
||||||
|
|
||||||
|
The Qml file is as follows:
|
||||||
|
|
||||||
|
``Examples/SlotsAndProperties/main.qml``
|
||||||
|
|
||||||
|
.. code-block:: qml
|
||||||
|
:file: ../Examples/SlotsAndProperties/main.qml
|
||||||
|
|
||||||
|
The qml is made up of: a Label, a TextInput widget, and a button.
|
||||||
|
The label displays the contact name - this automatically updates when
|
||||||
|
the contact name changes.
|
||||||
|
|
||||||
|
When clicked, the button updates the contact name with the text from
|
||||||
|
the TextInput widget.
|
||||||
|
|
||||||
|
So where's the magic?
|
||||||
|
|
||||||
|
The magic is in the Contact.nim file
|
||||||
|
|
||||||
|
``Examples/SlotsAndProperties/Contact.nim``
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:file: ../Examples/SlotsAndProperties/Contact.nim
|
||||||
|
|
||||||
|
First we declare a QObject subclass and provide a simple
|
||||||
|
new method where we:
|
||||||
|
1. invoke the ``create()`` procedure. This invoke the C++ bridge and allocate
|
||||||
|
a QObject instance
|
||||||
|
2. register a slot ``getName`` for reading the Contact name field
|
||||||
|
3. register a slot ``setName`` for writing the Contact name
|
||||||
|
4. register a signal ``nameChanged`` for notify the contact name changes
|
||||||
|
5. register a property called ``name`` of type ``QString`` with the given
|
||||||
|
read, write slots and notify signal
|
||||||
|
|
||||||
|
Looking at the ``getName`` and ``setName`` methods, you can see that slots, as defined in Nim,
|
||||||
|
are nothing more than standard methods. The method corresponding to the ``setName`` slot
|
||||||
|
demonstrates how to use the ``emit`` method to emit a signal.
|
||||||
|
|
||||||
|
The last thing to consider is the override of the ``onSlotCalled`` method.
|
||||||
|
This method is called by the NimQml library when an invocation occurs from
|
||||||
|
the Qml side for one of the slots belonging to the QObject.
|
||||||
|
The usual implementation for the onSlotCalled method consists of a
|
||||||
|
switch statement that forwards the arguments to the correct slot.
|
||||||
|
If the invoked slot has a return value, this is always in the index position
|
||||||
|
0 of the args array.
|
||||||
|
|
||||||
|
|
||||||
|
Example 4: QtObject macro
|
||||||
|
-------------------------
|
||||||
|
The previous example shows how to create a simple QObject, however writing
|
||||||
|
all those ``register`` procs and writing the ``onSlotCalled`` method
|
||||||
|
becomes boring pretty soon.
|
||||||
|
|
||||||
|
Furthermore all this information can be automatically generated.
|
||||||
|
For this purpose you can import the NimQmlMacros module that provides
|
||||||
|
the QtObject macro.
|
||||||
|
|
||||||
|
Let's begin as usual with both the main.nim and main.qml files
|
||||||
|
|
||||||
|
``Examples/QtObjectMacro/main.nim``
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:file: ../Examples/QtObjectMacro/main.nim
|
||||||
|
|
||||||
|
|
||||||
|
``Examples/QtObjectMacro/main.qml``
|
||||||
|
|
||||||
|
.. code-block:: qml
|
||||||
|
:file: ../Examples/QtObjectMacro/main.qml
|
||||||
|
|
||||||
|
Nothing is new in both the ``main.nim`` and ``main.qml`` with respect to
|
||||||
|
the previous example. What changed is the Contact object:
|
||||||
|
|
||||||
|
``Examples/QtObjectMacro/Contact.nim``
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
:file: ../Examples/QtObjectMacro/Contact.nim
|
||||||
|
|
||||||
|
In details:
|
||||||
|
1. Each QObject is defined inside the QtObject macro
|
||||||
|
2. Each slot is annotated with the ``{.slot.}`` pragma
|
||||||
|
3. Each signal is annotated with the ``{.signal.}`` pragma
|
||||||
|
4. Each property is created with the ``QtProperty`` macro
|
||||||
|
|
||||||
|
The ``QtProperty`` macro has the following syntax
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
QtProperty[typeOfProperty] nameOfProperty
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
add_subdirectory(Simple)
|
add_subdirectory(HelloWorld)
|
||||||
|
add_subdirectory(SimpleData)
|
||||||
|
add_subdirectory(SlotsAndProperties)
|
||||||
|
add_subdirectory(QtObjectMacro)
|
|
@ -1,2 +1,2 @@
|
||||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
add_nim_executable(TARGET Main SOURCES main.nim PATHS ../../NimQml)
|
add_nim_executable(TARGET HelloWorld SOURCES main.nim PATHS ../../NimQml)
|
|
@ -0,0 +1,19 @@
|
||||||
|
import NimQml
|
||||||
|
import macros
|
||||||
|
import typeinfo
|
||||||
|
|
||||||
|
proc mainProc() =
|
||||||
|
var app: QApplication
|
||||||
|
app.create()
|
||||||
|
defer: app.delete()
|
||||||
|
|
||||||
|
var engine: QQmlApplicationEngine
|
||||||
|
engine.create()
|
||||||
|
defer: engine.delete()
|
||||||
|
|
||||||
|
engine.load("main.qml")
|
||||||
|
app.exec()
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
mainProc()
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 1.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.1
|
||||||
|
|
||||||
|
ApplicationWindow
|
||||||
|
{
|
||||||
|
width: 400
|
||||||
|
height: 300
|
||||||
|
title: "Hello World"
|
||||||
|
Component.onCompleted: visible = true
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
add_nim_executable(TARGET QtObjectMacro SOURCES main.nim PATHS ../../NimQml)
|
|
@ -0,0 +1,27 @@
|
||||||
|
## Please note we are using templates where ordinarily we would like to use procedures
|
||||||
|
## due to bug: https://github.com/Araq/Nim/issues/1821
|
||||||
|
import NimQml, NimQmlMacros
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type Contact* = ref object of QObject
|
||||||
|
m_name: string
|
||||||
|
|
||||||
|
template newContact*(): Contact =
|
||||||
|
var result = Contact(m_name: "initialName")
|
||||||
|
result.create
|
||||||
|
result
|
||||||
|
|
||||||
|
method getName*(contact: Contact): string {.slot.} =
|
||||||
|
result = contact.m_name
|
||||||
|
|
||||||
|
method nameChanged*(contact: Contact) {.signal.}
|
||||||
|
|
||||||
|
method setName*(contact: Contact, name: string) {.slot.} =
|
||||||
|
if contact.m_name != name:
|
||||||
|
contact.m_name = name
|
||||||
|
contact.nameChanged()
|
||||||
|
|
||||||
|
QtProperty[string] name:
|
||||||
|
read = getName
|
||||||
|
write = setName
|
||||||
|
notify = nameChanged
|
|
@ -0,0 +1,27 @@
|
||||||
|
import NimQml
|
||||||
|
import Contact
|
||||||
|
|
||||||
|
proc mainProc() =
|
||||||
|
var app: QApplication
|
||||||
|
app.create()
|
||||||
|
defer: app.delete()
|
||||||
|
|
||||||
|
var contact = newContact()
|
||||||
|
defer: contact.delete()
|
||||||
|
|
||||||
|
var engine: QQmlApplicationEngine
|
||||||
|
engine.create()
|
||||||
|
defer: engine.delete()
|
||||||
|
|
||||||
|
var variant: QVariant
|
||||||
|
variant.create(contact)
|
||||||
|
defer: variant.delete()
|
||||||
|
|
||||||
|
var rootContext: QQmlContext = engine.rootContext()
|
||||||
|
rootContext.setContextProperty("contact", variant)
|
||||||
|
engine.load("main.qml")
|
||||||
|
app.exec()
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
mainProc()
|
||||||
|
|
|
@ -16,7 +16,7 @@ ApplicationWindow
|
||||||
|
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
text: "Current name is:" + myQObject.name
|
text: "Current name is:" + contact.name
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField
|
TextField
|
||||||
|
@ -27,10 +27,7 @@ ApplicationWindow
|
||||||
Button
|
Button
|
||||||
{
|
{
|
||||||
text: "Change Name"
|
text: "Change Name"
|
||||||
onClicked: {
|
onClicked: contact.name = textField.text
|
||||||
console.log("QML:", textField.text)
|
|
||||||
myQObject.name = textField.text
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
import NimQml
|
|
||||||
import macros
|
|
||||||
import typeinfo
|
|
||||||
|
|
||||||
type MyQObject = ref object of QObject
|
|
||||||
m_name: string
|
|
||||||
|
|
||||||
method getName(myQObject: MyQObject): string =
|
|
||||||
result = myQObject.m_name
|
|
||||||
|
|
||||||
method setName(myQObject: MyQObject, name: string) =
|
|
||||||
if myQObject.m_name != name:
|
|
||||||
myQObject.m_name = name
|
|
||||||
myQObject.emit("nameChanged")
|
|
||||||
|
|
||||||
method onSlotCalled(myQObject: MyQObject, slotName: string, args: openarray[QVariant]) =
|
|
||||||
case slotName:
|
|
||||||
of "getName":
|
|
||||||
args[0].stringVal = myQObject.getName()
|
|
||||||
of "setName":
|
|
||||||
myQObject.setName(args[1].stringVal)
|
|
||||||
else:
|
|
||||||
discard()
|
|
||||||
|
|
||||||
proc mainProc() =
|
|
||||||
var app: QApplication
|
|
||||||
app.create()
|
|
||||||
defer: app.delete()
|
|
||||||
|
|
||||||
var myQObject = MyQObject()
|
|
||||||
myQObject.create()
|
|
||||||
defer: myQObject.delete()
|
|
||||||
myQObject.m_name = "InitialName"
|
|
||||||
myQObject.registerSlot("getName", [QMetaType.QString])
|
|
||||||
myQObject.registerSlot("setName", [QMetaType.Void, QMetaType.QString])
|
|
||||||
myQObject.registerSignal("nameChanged", [QMetaType.Void])
|
|
||||||
myQObject.registerProperty("name", QMetaType.QString, "getName", "setName", "nameChanged")
|
|
||||||
|
|
||||||
var engine: QQmlApplicationEngine
|
|
||||||
engine.create()
|
|
||||||
defer: engine.delete()
|
|
||||||
|
|
||||||
var variant: QVariant
|
|
||||||
variant.create(myQObject)
|
|
||||||
defer: variant.delete()
|
|
||||||
|
|
||||||
var rootContext: QQmlContext = engine.rootContext()
|
|
||||||
rootContext.setContextProperty("myQObject", variant)
|
|
||||||
engine.load("main.qml")
|
|
||||||
app.exec()
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
mainProc()
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
add_nim_executable(TARGET SimpleData SOURCES main.nim PATHS ../../NimQml)
|
|
@ -0,0 +1,38 @@
|
||||||
|
import NimQml
|
||||||
|
import macros
|
||||||
|
import typeinfo
|
||||||
|
|
||||||
|
proc mainProc() =
|
||||||
|
var app: QApplication
|
||||||
|
app.create()
|
||||||
|
defer: app.delete()
|
||||||
|
|
||||||
|
var engine: QQmlApplicationEngine
|
||||||
|
engine.create()
|
||||||
|
defer: engine.delete()
|
||||||
|
|
||||||
|
var qVar1: QVariant
|
||||||
|
qVar1.create()
|
||||||
|
defer: qVar1.delete()
|
||||||
|
qVar1.intVal = 10
|
||||||
|
|
||||||
|
var qVar2: QVariant
|
||||||
|
qVar2.create()
|
||||||
|
defer: qVar2.delete()
|
||||||
|
qVar2.stringVal = "Hello World"
|
||||||
|
|
||||||
|
var qVar3: QVariant
|
||||||
|
qVar3.create()
|
||||||
|
defer: qVar3.delete()
|
||||||
|
qVar3.boolVal = false
|
||||||
|
|
||||||
|
engine.rootContext.setContextProperty("qVar1", qVar1)
|
||||||
|
engine.rootContext.setContextProperty("qVar2", qVar2)
|
||||||
|
engine.rootContext.setContextProperty("qVar3", qVar2)
|
||||||
|
|
||||||
|
engine.load("main.qml")
|
||||||
|
app.exec()
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
mainProc()
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 1.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.1
|
||||||
|
|
||||||
|
ApplicationWindow
|
||||||
|
{
|
||||||
|
width: 400
|
||||||
|
height: 300
|
||||||
|
title: "SimpleData"
|
||||||
|
Component.onCompleted: visible = true
|
||||||
|
|
||||||
|
ColumnLayout
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
SpinBox { value: qVar1}
|
||||||
|
TextField { text: qVar2}
|
||||||
|
CheckBox { checked: qVar3}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
add_nim_executable(TARGET SlotsAndProperties SOURCES main.nim PATHS ../../NimQml)
|
|
@ -0,0 +1,33 @@
|
||||||
|
## Please note we are using templates where ordinarily we would like to use procedures
|
||||||
|
## due to bug: https://github.com/Araq/Nim/issues/1821
|
||||||
|
import NimQml
|
||||||
|
|
||||||
|
type Contact = ref object of QObject
|
||||||
|
m_name: string
|
||||||
|
|
||||||
|
template newContact*(): Contact =
|
||||||
|
var result = Contact(m_name: "initialName")
|
||||||
|
result.create()
|
||||||
|
result.m_name = "InitialName"
|
||||||
|
result.registerSlot("getName", [QMetaType.QString])
|
||||||
|
result.registerSlot("setName", [QMetaType.Void, QMetaType.QString])
|
||||||
|
result.registerSignal("nameChanged", [QMetaType.Void])
|
||||||
|
result.registerProperty("name", QMetaType.QString, "getName", "setName", "nameChanged")
|
||||||
|
result
|
||||||
|
|
||||||
|
method getName*(self: Contact): string =
|
||||||
|
result = self.m_name
|
||||||
|
|
||||||
|
method setName*(self: Contact, name: string) =
|
||||||
|
if self.m_name != name:
|
||||||
|
self.m_name = name
|
||||||
|
self.emit("nameChanged")
|
||||||
|
|
||||||
|
method onSlotCalled(self: Contact, slotName: string, args: openarray[QVariant]) =
|
||||||
|
case slotName:
|
||||||
|
of "getName":
|
||||||
|
args[0].stringVal = self.getName()
|
||||||
|
of "setName":
|
||||||
|
self.setName(args[1].stringVal)
|
||||||
|
else:
|
||||||
|
discard()
|
|
@ -0,0 +1,27 @@
|
||||||
|
import NimQml
|
||||||
|
import Contact
|
||||||
|
|
||||||
|
proc mainProc() =
|
||||||
|
var app: QApplication
|
||||||
|
app.create()
|
||||||
|
defer: app.delete()
|
||||||
|
|
||||||
|
var contact = newContact()
|
||||||
|
defer: contact.delete()
|
||||||
|
|
||||||
|
var engine: QQmlApplicationEngine
|
||||||
|
engine.create()
|
||||||
|
defer: engine.delete()
|
||||||
|
|
||||||
|
var variant: QVariant
|
||||||
|
variant.create(contact)
|
||||||
|
defer: variant.delete()
|
||||||
|
|
||||||
|
var rootContext: QQmlContext = engine.rootContext()
|
||||||
|
rootContext.setContextProperty("contact", variant)
|
||||||
|
engine.load("main.qml")
|
||||||
|
app.exec()
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
mainProc()
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 1.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.1
|
||||||
|
|
||||||
|
ApplicationWindow
|
||||||
|
{
|
||||||
|
width: 400
|
||||||
|
height: 300
|
||||||
|
|
||||||
|
Component.onCompleted: visible = true
|
||||||
|
|
||||||
|
ColumnLayout
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: "Current name is:" + contact.name
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField
|
||||||
|
{
|
||||||
|
id: textField
|
||||||
|
}
|
||||||
|
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
text: "Change Name"
|
||||||
|
onClicked: contact.name = textField.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,19 @@
|
||||||
import NimQmlTypes
|
import NimQmlTypes
|
||||||
import tables
|
import tables
|
||||||
|
|
||||||
|
## NimQml aims to provide binding to the QML for the Nim programming language
|
||||||
|
|
||||||
export QObject
|
export QObject
|
||||||
export QApplication
|
export QApplication
|
||||||
export QVariant
|
export QVariant
|
||||||
export QQmlApplicationEngine
|
export QQmlApplicationEngine
|
||||||
export QQmlContext
|
export QQmlContext
|
||||||
|
|
||||||
type QMetaType* {.pure.} = enum
|
type QMetaType* {.pure.} = enum ## \
|
||||||
|
## Qt metatypes values used for specifing the
|
||||||
|
## signals and slots argument and return types.
|
||||||
|
##
|
||||||
|
## This enum mimic the QMetaType::Type C++ enum
|
||||||
UnknownType = cint(0),
|
UnknownType = cint(0),
|
||||||
Bool = cint(1),
|
Bool = cint(1),
|
||||||
Int = cint(2),
|
Int = cint(2),
|
||||||
|
@ -50,53 +56,65 @@ proc dos_qvariant_setString(variant: pointer, value: cstring) {.cdecl, dynlib:"l
|
||||||
proc dos_chararray_delete(rawCString: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_chararray_delete(rawCString: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
|
|
||||||
proc create*(variant: var QVariant) =
|
proc create*(variant: var QVariant) =
|
||||||
|
## Create a new QVariant
|
||||||
var data: pointer
|
var data: pointer
|
||||||
dos_qvariant_create(data)
|
dos_qvariant_create(data)
|
||||||
variant = QVariant(data)
|
variant = QVariant(data)
|
||||||
|
|
||||||
proc create*(variant: var QVariant, value: cint) =
|
proc create*(variant: var QVariant, value: cint) =
|
||||||
|
## Create a new QVariant given a cint value
|
||||||
var data: pointer
|
var data: pointer
|
||||||
dos_qvariant_create_int(data, value)
|
dos_qvariant_create_int(data, value)
|
||||||
variant = QVariant(data)
|
variant = QVariant(data)
|
||||||
|
|
||||||
proc create*(variant: var QVariant, value: bool) =
|
proc create*(variant: var QVariant, value: bool) =
|
||||||
|
## Create a new QVariant given a bool value
|
||||||
var data: pointer
|
var data: pointer
|
||||||
dos_qvariant_create_bool(data, value)
|
dos_qvariant_create_bool(data, value)
|
||||||
variant = QVariant(data)
|
variant = QVariant(data)
|
||||||
|
|
||||||
proc create*(variant: var QVariant, value: string) =
|
proc create*(variant: var QVariant, value: string) =
|
||||||
|
## Create a new QVariant given a string value
|
||||||
var data: pointer
|
var data: pointer
|
||||||
dos_qvariant_create_string(data, value)
|
dos_qvariant_create_string(data, value)
|
||||||
variant = QVariant(data)
|
variant = QVariant(data)
|
||||||
|
|
||||||
proc create*(variant: var QVariant, value: QObject) =
|
proc create*(variant: var QVariant, value: QObject) =
|
||||||
|
## Create a new QVariant given a QObject
|
||||||
var data: pointer
|
var data: pointer
|
||||||
dos_qvariant_create_qobject(data, value.data)
|
dos_qvariant_create_qobject(data, value.data)
|
||||||
variant = QVariant(data)
|
variant = QVariant(data)
|
||||||
|
|
||||||
proc delete*(variant: QVariant) =
|
proc delete*(variant: QVariant) =
|
||||||
|
## Delete a QVariant
|
||||||
debugMsg("QVariant", "delete")
|
debugMsg("QVariant", "delete")
|
||||||
dos_qvariant_delete(pointer(variant))
|
dos_qvariant_delete(pointer(variant))
|
||||||
|
|
||||||
proc isNull*(variant: QVariant): bool =
|
proc isNull*(variant: QVariant): bool =
|
||||||
|
## Return true if the QVariant value is null, false otherwise
|
||||||
dos_qvariant_isnull(pointer(variant), result)
|
dos_qvariant_isnull(pointer(variant), result)
|
||||||
|
|
||||||
proc intVal*(variant: QVariant): int =
|
proc intVal*(variant: QVariant): int =
|
||||||
|
## Return the QVariant value as int
|
||||||
var rawValue: cint
|
var rawValue: cint
|
||||||
dos_qvariant_toInt(pointer(variant), rawValue)
|
dos_qvariant_toInt(pointer(variant), rawValue)
|
||||||
result = cast[int](rawValue)
|
result = cast[int](rawValue)
|
||||||
|
|
||||||
proc `intVal=`*(variant: QVariant, value: int) =
|
proc `intVal=`*(variant: QVariant, value: int) =
|
||||||
|
## Sets the QVariant value int value
|
||||||
var rawValue = cast[cint](value)
|
var rawValue = cast[cint](value)
|
||||||
dos_qvariant_setInt(pointer(variant), rawValue)
|
dos_qvariant_setInt(pointer(variant), rawValue)
|
||||||
|
|
||||||
proc boolVal*(variant: QVariant): bool =
|
proc boolVal*(variant: QVariant): bool =
|
||||||
|
## Return the QVariant value as bool
|
||||||
dos_qvariant_toBool(pointer(variant), result)
|
dos_qvariant_toBool(pointer(variant), result)
|
||||||
|
|
||||||
proc `boolVal=`*(variant: QVariant, value: bool) =
|
proc `boolVal=`*(variant: QVariant, value: bool) =
|
||||||
|
## Sets the QVariant bool value
|
||||||
dos_qvariant_setBool(pointer(variant), value)
|
dos_qvariant_setBool(pointer(variant), value)
|
||||||
|
|
||||||
proc stringVal*(variant: QVariant): string =
|
proc stringVal*(variant: QVariant): string =
|
||||||
|
## Return the QVariant value as string
|
||||||
var rawCString: cstring
|
var rawCString: cstring
|
||||||
var rawCStringLength: cint
|
var rawCStringLength: cint
|
||||||
dos_qvariant_toString(pointer(variant), rawCString, rawCStringLength)
|
dos_qvariant_toString(pointer(variant), rawCString, rawCStringLength)
|
||||||
|
@ -104,6 +122,7 @@ proc stringVal*(variant: QVariant): string =
|
||||||
dos_chararray_delete(rawCString)
|
dos_chararray_delete(rawCString)
|
||||||
|
|
||||||
proc `stringVal=`*(variant: QVariant, value: string) =
|
proc `stringVal=`*(variant: QVariant, value: string) =
|
||||||
|
## Sets the QVariant string value
|
||||||
dos_qvariant_setString(pointer(variant), value)
|
dos_qvariant_setString(pointer(variant), value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,19 +133,23 @@ proc dos_qqmlapplicationengine_context(engine: pointer, context: var pointer) {.
|
||||||
proc dos_qqmlapplicationengine_delete(engine: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qqmlapplicationengine_delete(engine: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
|
|
||||||
proc create*(engine: var QQmlApplicationEngine) =
|
proc create*(engine: var QQmlApplicationEngine) =
|
||||||
|
## Create an new QQmlApplicationEngine
|
||||||
var temp: pointer
|
var temp: pointer
|
||||||
dos_qqmlapplicationengine_create(temp)
|
dos_qqmlapplicationengine_create(temp)
|
||||||
engine = QQmlApplicationEngine(temp)
|
engine = QQmlApplicationEngine(temp)
|
||||||
|
|
||||||
proc load*(engine: QQmlApplicationEngine, filename: cstring) =
|
proc load*(engine: QQmlApplicationEngine, filename: cstring) =
|
||||||
|
## Load the given Qml file
|
||||||
dos_qqmlapplicationengine_load(pointer(engine), filename)
|
dos_qqmlapplicationengine_load(pointer(engine), filename)
|
||||||
|
|
||||||
proc rootContext*(engine: QQmlApplicationEngine): QQmlContext =
|
proc rootContext*(engine: QQmlApplicationEngine): QQmlContext =
|
||||||
|
## Return the engine root context
|
||||||
var context: pointer
|
var context: pointer
|
||||||
dos_qqmlapplicationengine_context(pointer(engine), context)
|
dos_qqmlapplicationengine_context(pointer(engine), context)
|
||||||
result = cast[QQmlContext](context)
|
result = cast[QQmlContext](context)
|
||||||
|
|
||||||
proc delete*(engine: QQmlApplicationEngine) =
|
proc delete*(engine: QQmlApplicationEngine) =
|
||||||
|
## Delete the given QQmlApplicationEngine
|
||||||
debugMsg("QQmlApplicationEngine", "delete")
|
debugMsg("QQmlApplicationEngine", "delete")
|
||||||
dos_qqmlapplicationengine_delete(pointer(engine))
|
dos_qqmlapplicationengine_delete(pointer(engine))
|
||||||
|
|
||||||
|
@ -134,6 +157,7 @@ proc delete*(engine: QQmlApplicationEngine) =
|
||||||
proc dos_qqmlcontext_setcontextproperty(context: pointer, propertyName: cstring, propertyValue: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qqmlcontext_setcontextproperty(context: pointer, propertyName: cstring, propertyValue: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
|
|
||||||
proc setContextProperty*(context: QQmlContext, propertyName: string, propertyValue: QVariant) =
|
proc setContextProperty*(context: QQmlContext, propertyName: string, propertyValue: QVariant) =
|
||||||
|
## Sets a new property with the given value
|
||||||
dos_qqmlcontext_setcontextproperty(pointer(context), propertyName, pointer(propertyValue))
|
dos_qqmlcontext_setcontextproperty(pointer(context), propertyName, pointer(propertyValue))
|
||||||
|
|
||||||
# QApplication
|
# QApplication
|
||||||
|
@ -142,21 +166,31 @@ proc dos_qguiapplication_exec() {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
proc dos_qguiapplication_delete() {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qguiapplication_delete() {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
|
|
||||||
proc create*(application: QApplication) =
|
proc create*(application: QApplication) =
|
||||||
debugMsg("QApplication", "create")
|
## Create a new QApplication
|
||||||
dos_qguiapplication_create()
|
dos_qguiapplication_create()
|
||||||
|
|
||||||
proc exec*(application: QApplication) =
|
proc exec*(application: QApplication) =
|
||||||
debugMsg("QApplication", "exec")
|
## Start the Qt event loop
|
||||||
dos_qguiapplication_exec()
|
dos_qguiapplication_exec()
|
||||||
|
|
||||||
proc delete*(application: QApplication) =
|
proc delete*(application: QApplication) =
|
||||||
debugMsg("QApplication", "delete")
|
## Delete the given QApplication
|
||||||
dos_qguiapplication_delete()
|
dos_qguiapplication_delete()
|
||||||
|
|
||||||
# QObject
|
# QObject
|
||||||
type QVariantArray {.unchecked.} = array[0..0, QVariant]
|
type QVariantArray {.unchecked.} = array[0..0, QVariant]
|
||||||
type QVariantArrayPtr = ptr QVariantArray
|
type QVariantArrayPtr = ptr QVariantArray
|
||||||
|
|
||||||
|
proc toVariantSeq(args: QVariantArrayPtr, numArgs: cint): seq[QVariant] =
|
||||||
|
result = @[]
|
||||||
|
for i in 0..numArgs-1:
|
||||||
|
result.add(args[i])
|
||||||
|
|
||||||
|
proc toCIntSeq(metaTypes: openarray[QMetaType]): seq[cint] =
|
||||||
|
result = @[]
|
||||||
|
for metaType in metaTypes:
|
||||||
|
result.add(cint(metaType))
|
||||||
|
|
||||||
proc dos_qobject_create(qobject: var pointer, nimobject: pointer, qobjectCallback: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qobject_create(qobject: var pointer, nimobject: pointer, qobjectCallback: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
proc dos_qobject_delete(qobject: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qobject_delete(qobject: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
proc dos_qobject_slot_create(qobject: pointer, slotName: cstring, argumentsCount: cint, argumentsMetaTypes: ptr cint, slotIndex: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qobject_slot_create(qobject: pointer, slotName: cstring, argumentsCount: cint, argumentsMetaTypes: ptr cint, slotIndex: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
|
@ -164,40 +198,31 @@ proc dos_qobject_signal_create(qobject: pointer, signalName: cstring, argumentsC
|
||||||
proc dos_qobject_signal_emit(qobject: pointer, signalName: cstring, argumentsCount: cint, arguments: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qobject_signal_emit(qobject: pointer, signalName: cstring, argumentsCount: cint, arguments: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
proc dos_qobject_property_create(qobject: pointer, propertyName: cstring, propertyType: cint, readSlot: cstring, writeSlot: cstring, notifySignal: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qobject_property_create(qobject: pointer, propertyName: cstring, propertyType: cint, readSlot: cstring, writeSlot: cstring, notifySignal: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
|
|
||||||
method onSlotCalled*(nimobject: QObject, slotName: string, args: openarray[QVariant]) =
|
method onSlotCalled*(nimobject: QObject, slotName: string, args: openarray[QVariant]) =
|
||||||
debugMsg("QObject", "onSlotCalled", "begin")
|
## Called from the NimQml bridge when a slot is called from Qml.
|
||||||
debugMsg("QObject", "onSlotCalled", "end")
|
## Subclasses can override the given method for handling the slot call
|
||||||
|
discard()
|
||||||
|
|
||||||
proc toVariantSeq(args: QVariantArrayPtr, numArgs: cint): seq[QVariant] =
|
proc qobjectCallback(nimobject: pointer, slotName: QVariant, numArguments: cint, arguments: QVariantArrayPtr) {.exportc.} =
|
||||||
result = @[]
|
|
||||||
for i in 0..numArgs-1:
|
|
||||||
result.add(args[i])
|
|
||||||
|
|
||||||
proc qobjectCallback(nimobject: pointer, slotName: QVariant, numArguments: cint, arguments: QVariantArrayPtr) {.exportc.} =
|
|
||||||
debugMsg("QObject", "qobjectCallback", "begin")
|
|
||||||
var nimQObjectCasted = cast[ptr QObject](nimobject)
|
var nimQObjectCasted = cast[ptr QObject](nimobject)
|
||||||
# forward to the QObject subtype instance
|
# forward to the QObject subtype instance
|
||||||
nimQObjectCasted[].onSlotCalled(slotName.stringVal, arguments.toVariantSeq(numArguments))
|
nimQObjectCasted[].onSlotCalled(slotName.stringVal, arguments.toVariantSeq(numArguments))
|
||||||
debugMsg("QObject", "qobjectCallback", "end")
|
|
||||||
|
|
||||||
proc toCIntSeq(metaTypes: openarray[QMetaType]): seq[cint] =
|
|
||||||
result = @[]
|
|
||||||
for metaType in metaTypes:
|
|
||||||
result.add(cint(metaType))
|
|
||||||
|
|
||||||
proc create*(qobject: var QObject) =
|
proc create*(qobject: var QObject) =
|
||||||
|
## Create a new QObject
|
||||||
qobject.name = "QObject"
|
qobject.name = "QObject"
|
||||||
qobject.slots = initTable[string,cint]()
|
qobject.slots = initTable[string,cint]()
|
||||||
qobject.signals = initTable[string, cint]()
|
qobject.signals = initTable[string, cint]()
|
||||||
dos_qobject_create(qobject.data, addr(qobject), qobjectCallback)
|
dos_qobject_create(qobject.data, addr(qobject), qobjectCallback)
|
||||||
|
|
||||||
proc delete*(qobject: QObject) =
|
proc delete*(qobject: QObject) =
|
||||||
debugMsg("QObject", "delete")
|
## Delete the given QObject
|
||||||
dos_qobject_delete(qobject.data)
|
dos_qobject_delete(qobject.data)
|
||||||
|
|
||||||
proc registerSlot*(qobject: var QObject,
|
proc registerSlot*(qobject: var QObject,
|
||||||
slotName: string,
|
slotName: string,
|
||||||
metaTypes: openarray[QMetaType]) =
|
metaTypes: openarray[QMetaType]) =
|
||||||
|
## Register a slot in the QObject with the given name and signature
|
||||||
# Copy the metatypes array
|
# Copy the metatypes array
|
||||||
var copy = toCIntSeq(metatypes)
|
var copy = toCIntSeq(metatypes)
|
||||||
var index: cint
|
var index: cint
|
||||||
|
@ -207,6 +232,7 @@ proc registerSlot*(qobject: var QObject,
|
||||||
proc registerSignal*(qobject: var QObject,
|
proc registerSignal*(qobject: var QObject,
|
||||||
signalName: string,
|
signalName: string,
|
||||||
metatypes: openarray[QMetaType]) =
|
metatypes: openarray[QMetaType]) =
|
||||||
|
## Register a signal in the QObject with the given name and signature
|
||||||
var index: cint
|
var index: cint
|
||||||
if metatypes.len > 0:
|
if metatypes.len > 0:
|
||||||
var copy = toCIntSeq(metatypes)
|
var copy = toCIntSeq(metatypes)
|
||||||
|
@ -220,10 +246,12 @@ proc registerProperty*(qobject: var QObject,
|
||||||
propertyType: QMetaType,
|
propertyType: QMetaType,
|
||||||
readSlot: string,
|
readSlot: string,
|
||||||
writeSlot: string,
|
writeSlot: string,
|
||||||
notifySignal: string) =
|
notifySignal: string) =
|
||||||
|
## Register a property in the QObject with the given name and type.
|
||||||
dos_qobject_property_create(qobject.data, propertyName, cast[cint](propertyType), readSlot, writeSlot, notifySignal)
|
dos_qobject_property_create(qobject.data, propertyName, cast[cint](propertyType), readSlot, writeSlot, notifySignal)
|
||||||
|
|
||||||
proc emit*(qobject: QObject, signalName: string, args: openarray[QVariant] = []) =
|
proc emit*(qobject: QObject, signalName: string, args: openarray[QVariant] = []) =
|
||||||
|
## Emit the signal with the given name and values
|
||||||
if args.len > 0:
|
if args.len > 0:
|
||||||
var copy: seq[QVariant]
|
var copy: seq[QVariant]
|
||||||
for i in 0..args.len-1:
|
for i in 0..args.len-1:
|
||||||
|
@ -239,23 +267,27 @@ proc dos_qquickview_show(view: pointer) {.cdecl, dynlib:"libDOtherSide.so", impo
|
||||||
proc dos_qquickview_source(view: pointer, filename: var cstring, length: var int) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qquickview_source(view: pointer, filename: var cstring, length: var int) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
proc dos_qquickview_set_source(view: pointer, filename: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
proc dos_qquickview_set_source(view: pointer, filename: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
|
||||||
|
|
||||||
proc create(view: var QQuickView) =
|
proc create(view: var QQuickView) =
|
||||||
|
## Create a new QQuickView
|
||||||
var temp: pointer
|
var temp: pointer
|
||||||
dos_qquickview_create(temp)
|
dos_qquickview_create(temp)
|
||||||
view = QQuickView(temp)
|
view = QQuickView(temp)
|
||||||
|
|
||||||
proc source(view: QQuickView): cstring =
|
proc source(view: QQuickView): cstring =
|
||||||
|
## Return the source Qml file loaded by the view
|
||||||
var length: int
|
var length: int
|
||||||
dos_qquickview_source(pointer(view), result, length)
|
dos_qquickview_source(pointer(view), result, length)
|
||||||
|
|
||||||
proc `source=`(view: QQuickView, filename: cstring) =
|
proc `source=`(view: QQuickView, filename: cstring) =
|
||||||
|
## Sets the source Qml file laoded by the view
|
||||||
dos_qquickview_set_source(pointer(view), filename)
|
dos_qquickview_set_source(pointer(view), filename)
|
||||||
|
|
||||||
proc show(view: QQuickView) =
|
proc show(view: QQuickView) =
|
||||||
|
## Sets the view visible
|
||||||
dos_qquickview_show(pointer(view))
|
dos_qquickview_show(pointer(view))
|
||||||
|
|
||||||
proc delete(view: QQuickView) =
|
proc delete(view: QQuickView) =
|
||||||
debugMsg("QQuickView", "delete")
|
## Delete the given QQuickView
|
||||||
dos_qquickview_delete(pointer(view))
|
dos_qquickview_delete(pointer(view))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[Package]
|
[Package]
|
||||||
name = "NimQml"
|
name = "NimQml"
|
||||||
version = "0.01"
|
version = "0.2.0"
|
||||||
author = "Filippo Cucchetto"
|
author = "Filippo Cucchetto, Will Szumski"
|
||||||
description = "QML bindings for Nimrod"
|
description = "QML bindings for Nimrod"
|
||||||
license = "GPLv3"
|
license = "GPLv3"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,460 @@
|
||||||
|
## Contains helper macros for NimQml
|
||||||
|
|
||||||
|
import macros
|
||||||
|
import strutils
|
||||||
|
import typetraits
|
||||||
|
import tables
|
||||||
|
|
||||||
|
template debug(body: stmt): stmt =
|
||||||
|
{.push warning[user]: off.}
|
||||||
|
when defined(debug):
|
||||||
|
{.pop.}
|
||||||
|
body
|
||||||
|
else:
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
let nimFromQtVariant {.compileTime.} = {
|
||||||
|
"int" : "intVal",
|
||||||
|
"string" : "stringVal",
|
||||||
|
"bool" : "boolVal",
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
let nim2QtMeta {.compileTime.} = {
|
||||||
|
"bool": "Bool",
|
||||||
|
"int" : "Int",
|
||||||
|
"string" : "QString",
|
||||||
|
"pointer" : "VoidStar",
|
||||||
|
"QVariant": "QVariant",
|
||||||
|
"" : "Void", # no return, which is represented by an nnkEmpty node
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
proc getNodeOf*(tree: PNimrodNode, kind: TNimrodNodeKind): PNimrodNode {.compileTime.} =
|
||||||
|
## recursively looks for a node of kind, ``kind``, in the tree provided as ``tree``
|
||||||
|
## Returnsthe first node that satisfies this condition
|
||||||
|
for i in 0.. <tree.len:
|
||||||
|
var child = tree[i]
|
||||||
|
if child.kind == kind:
|
||||||
|
return child
|
||||||
|
var candidate = getNodeOf(child, kind)
|
||||||
|
if not candidate.isNil:
|
||||||
|
return candidate
|
||||||
|
|
||||||
|
static:
|
||||||
|
type Context* = ref object of RootObj
|
||||||
|
type NullContext* = ref object of Context
|
||||||
|
|
||||||
|
type NodeModifier*[T] = proc(context: T, a: var PNimrodNode): PNimrodNode
|
||||||
|
|
||||||
|
# had to remove type bound on hook due to recent regression with generics
|
||||||
|
proc hookOnNode*[T](context: T, code: PNimrodNode, hook: NodeModifier,
|
||||||
|
recursive: bool = false): PNimrodNode {.compileTime.} =
|
||||||
|
## Iterates over the tree, ``code``, calling ``hook`` on each ``PNimrodNode``
|
||||||
|
## encountered. If ``recursive`` is true, it will recurse over the tree, otherwise
|
||||||
|
## it will only visit ``code``'s children. ``hook`` should return a replacement for
|
||||||
|
## the node that was passed in via it's return value. `hook` may return nil to remove
|
||||||
|
## the node from the tree.
|
||||||
|
if code.len == 0:
|
||||||
|
return code
|
||||||
|
var newCode = newNimNode(code.kind)
|
||||||
|
for i in 0.. <code.len:
|
||||||
|
var child = code[i].copy()
|
||||||
|
child = hook(context, child)
|
||||||
|
if recursive:
|
||||||
|
child = hookOnNode(context,child,hook,true)
|
||||||
|
if child != nil:
|
||||||
|
newCode.add child
|
||||||
|
return newCode
|
||||||
|
|
||||||
|
proc removeOpenSym*(context: NullContext,
|
||||||
|
a: var PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## replaces: ``nnkOpenSymChoice`` and ``nnkSym`` nodes with idents
|
||||||
|
## corresponding to the symbols string representation.
|
||||||
|
if a.kind == nnkOpenSymChoice:
|
||||||
|
return ident($a[0].symbol)
|
||||||
|
elif a.kind == nnkSym:
|
||||||
|
return ident($a.symbol)
|
||||||
|
return a
|
||||||
|
|
||||||
|
proc newTemplate*(name = newEmptyNode();
|
||||||
|
params: openArray[PNimrodNode] = [newEmptyNode()];
|
||||||
|
body: PNimrodNode = newStmtList()): PNimrodNode {.compileTime.} =
|
||||||
|
## shortcut for creating a new template
|
||||||
|
##
|
||||||
|
## The ``params`` array must start with the return type of the template,
|
||||||
|
## followed by a list of IdentDefs which specify the params.
|
||||||
|
result = newNimNode(nnkTemplateDef).add(
|
||||||
|
name,
|
||||||
|
newEmptyNode(),
|
||||||
|
newEmptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).add(params), ##params
|
||||||
|
newEmptyNode(), ## pragmas
|
||||||
|
newEmptyNode(),
|
||||||
|
body)
|
||||||
|
|
||||||
|
#FIXME: changed parent, typ from typedesc to expr to workaround Nim issue #1874
|
||||||
|
template declareSuperTemplate*(parent: expr, typ: expr): stmt =
|
||||||
|
template superType*(ofType: typedesc[typ]): typedesc[parent] =
|
||||||
|
parent
|
||||||
|
|
||||||
|
proc getTypeName*(a: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## returns the node containing the name of an object in a
|
||||||
|
## given type definition block
|
||||||
|
expectMinLen a, 1
|
||||||
|
expectKind a, nnkTypeDef
|
||||||
|
var testee = a
|
||||||
|
if testee[0].kind == nnkPragmaExpr:
|
||||||
|
testee = testee[0]
|
||||||
|
if testee[0].kind in {nnkIdent}:
|
||||||
|
return testee[0]
|
||||||
|
elif testee[0].kind in {nnkPostfix}:
|
||||||
|
return testee[0][1]
|
||||||
|
|
||||||
|
proc isExported(def: PNimrodNode): bool {.compileTime.} =
|
||||||
|
## given a type definition, ``typedef``, determines whether or
|
||||||
|
## not the type is exported with a '*'
|
||||||
|
assert def.kind in {nnkTypeDef, nnkProcDef, nnkMethodDef, nnkTemplateDef},
|
||||||
|
"unsupported type: " & $def.kind
|
||||||
|
if def[0].kind == nnkPostfix:
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc exportDef(def: PNimrodNode) {.compileTime.} =
|
||||||
|
## Exports exportable definitions. Currently only supports
|
||||||
|
## templates, methods and procedures and types.
|
||||||
|
if def.kind in {nnkProcDef, nnkMethodDef, nnkTemplateDef, nnkTypeDef}:
|
||||||
|
if def.isExported:
|
||||||
|
return
|
||||||
|
def[0] = postfix(def[0], "*")
|
||||||
|
else:
|
||||||
|
error("node: " & $def.kind & " not supported")
|
||||||
|
|
||||||
|
proc unexportDef(def: PNimrodNode) {.compileTime.} =
|
||||||
|
## unexports exportable definitions. Currently only supports
|
||||||
|
## templates, methods and procedures and types.
|
||||||
|
if def.kind in {nnkProcDef, nnkMethodDef, nnkTemplateDef, nnkTypeDef}:
|
||||||
|
if not def.isExported:
|
||||||
|
return
|
||||||
|
def[0] = ident unpackPostfix(def[0])[1]
|
||||||
|
else:
|
||||||
|
error("node: " & $def.kind & " not supported")
|
||||||
|
|
||||||
|
proc genSuperTemplate*(typeDecl: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## generates a template, with name: superType, that returns the super type
|
||||||
|
## of the object defined in the type defintion, ``typeDecl``. ``typeDecl``
|
||||||
|
## must contain an object inheriting from a base type.
|
||||||
|
expectKind typeDecl, nnkTypeDef
|
||||||
|
let inheritStmt = typeDecl.getNodeOf(nnkOfInherit)
|
||||||
|
let typeName = getTypeName(typeDecl)
|
||||||
|
if inheritStmt == nil: error("you must declare a super type for " & $typeName)
|
||||||
|
# ident of superType (have to deal with generics)
|
||||||
|
let superType = if inheritStmt[0].kind == nnkIdent: inheritStmt[0]
|
||||||
|
else: inheritStmt[0].getNodeOf(nnkIdent)
|
||||||
|
let superTemplate = getAst declareSuperTemplate(superType, typeName)
|
||||||
|
result = superTemplate[0]
|
||||||
|
if typeDecl.isExported():
|
||||||
|
result.exportDef()
|
||||||
|
else:
|
||||||
|
result.unexportDef()
|
||||||
|
|
||||||
|
proc getSuperType*(typeDecl: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## returns ast containing superType info, may not be an ident if generic
|
||||||
|
let inheritStmt = typeDecl.getNodeOf(nnkOfInherit)
|
||||||
|
if inheritStmt.isNil: return newEmptyNode()
|
||||||
|
return inheritStmt[0]
|
||||||
|
|
||||||
|
proc getPragmaName*(child: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## name of child in a nnkPragma section
|
||||||
|
if child.kind == nnkIdent:
|
||||||
|
return child
|
||||||
|
# assumes first ident is name of pragma
|
||||||
|
let ident = child.getNodeOf(nnkIdent)
|
||||||
|
result = ident
|
||||||
|
|
||||||
|
proc removePragma*(pragma: PNimrodNode, toRemove: string): PNimrodNode {.compileTime.} =
|
||||||
|
## removes a pragma from pragma definition, `pragma`, with name `toRemove`
|
||||||
|
expectKind pragma, nnkPragma
|
||||||
|
result = newNimNode(nnkPragma)
|
||||||
|
for i in 0.. <pragma.len:
|
||||||
|
let child = pragma[i]
|
||||||
|
if $child.getPragmaName == toRemove:
|
||||||
|
continue
|
||||||
|
result.add child
|
||||||
|
if result.len == 0:
|
||||||
|
return newEmptyNode()
|
||||||
|
|
||||||
|
proc hasPragma*(node: PNimrodNode, pragmaName: string): bool {.compileTime.} =
|
||||||
|
## Returns ``true`` iff the method, or proc definition: ``node``, has a pragma
|
||||||
|
## ``pragmaName``
|
||||||
|
doAssert node.kind in {nnkMethodDef, nnkProcDef}
|
||||||
|
result = false
|
||||||
|
let pragma = node.pragma
|
||||||
|
if pragma.kind == nnkEmpty:
|
||||||
|
# denotes no pragma set
|
||||||
|
return false
|
||||||
|
for child in pragma.children():
|
||||||
|
if $child.getPragmaName() == pragmaName:
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc getArgType*(arg: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## returns the ``PNimrodNode`` representing a parameters type
|
||||||
|
if arg[1].kind == nnkIdent:
|
||||||
|
arg[1]
|
||||||
|
else:
|
||||||
|
arg[1].getNodeOf(nnkIdent)
|
||||||
|
|
||||||
|
proc getArgName*(arg: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## returns the ``PNimrodNode`` representing a parameters name
|
||||||
|
if arg[0].kind == nnkIdent:
|
||||||
|
arg[0]
|
||||||
|
else:
|
||||||
|
arg[0].getNodeOf(nnkIdent)
|
||||||
|
|
||||||
|
proc addSignalBody(signal: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
# e.g: produces: emit(MyQObject, "nameChanged")
|
||||||
|
expectKind signal, nnkMethodDef
|
||||||
|
result = newStmtList()
|
||||||
|
# if exported, will use postfix
|
||||||
|
let name = if signal.name.kind == nnkIdent: signal.name else: signal.name[1]
|
||||||
|
let params = signal.params
|
||||||
|
# type signal defined on is the 1st arg
|
||||||
|
let self = getArgName(params[1])
|
||||||
|
var args = newSeq[PNimrodNode]()
|
||||||
|
args.add(self)
|
||||||
|
args.add newLit($name)
|
||||||
|
if params.len > 2: # more args than just type
|
||||||
|
for i in 2.. <params.len:
|
||||||
|
args.add getArgName params[i]
|
||||||
|
result.add newCall("emit", args)
|
||||||
|
|
||||||
|
#FIXME: changed typ from typedesc to expr to workaround Nim issue #1874
|
||||||
|
template declareOnSlotCalled(typ: expr): stmt =
|
||||||
|
method onSlotCalled(myQObject: typ, slotName: string, args: openarray[QVariant]) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
#FIXME: changed parent, typ from typedesc to expr to workaround Nim issue #1874
|
||||||
|
template prototypeCreate(typ: expr): stmt =
|
||||||
|
template create*(myQObject: var typ) =
|
||||||
|
var super = (typ.superType())(myQObject)
|
||||||
|
procCall create(super)
|
||||||
|
|
||||||
|
proc doRemoveOpenSym(a: var PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
hookOnNode(NullContext(),a, removeOpenSym, true)
|
||||||
|
|
||||||
|
proc templateBody*(a: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
expectKind a, nnkTemplateDef
|
||||||
|
result = a[6]
|
||||||
|
|
||||||
|
proc genArgTypeArray(params: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
expectKind params, nnkFormalParams
|
||||||
|
result = newNimNode(nnkBracket)
|
||||||
|
for i in 0 .. <params.len:
|
||||||
|
if i == 1:
|
||||||
|
# skip "self" param eg: myQObject: MyQObject
|
||||||
|
continue
|
||||||
|
let pType = if i != 0: getArgType params[i] else: params[i]
|
||||||
|
let pTypeString = if pType.kind == nnkEmpty: "" else: $pType
|
||||||
|
# function that maps Qvariant type to nim type
|
||||||
|
let qtMeta = nim2QtMeta[pTypeString]
|
||||||
|
if qtMeta == nil: error(pTypeString & " not supported yet")
|
||||||
|
let metaDot = newDotExpr(ident "QMetaType", ident qtMeta)
|
||||||
|
result.add metaDot
|
||||||
|
|
||||||
|
proc getIdentDefName*(a: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## returns object field name from ident def
|
||||||
|
expectKind a, nnkIdentDefs
|
||||||
|
if a[0].kind == nnkIdent:
|
||||||
|
return a[0]
|
||||||
|
elif a[0].kind == nnkPostFix:
|
||||||
|
return a[0][1]
|
||||||
|
|
||||||
|
macro QtObject*(qtDecl: stmt): stmt {.immediate.} =
|
||||||
|
## Generates boiler plate code for registering signals, slots
|
||||||
|
## and properties.
|
||||||
|
##
|
||||||
|
## Currently generates:
|
||||||
|
## - create: a method to register signal, slots and properties
|
||||||
|
## - superType: a template that returns the super type of the
|
||||||
|
## object defined within the macro body
|
||||||
|
## - onSlotCalled: a method to dispatch an on slot call to the
|
||||||
|
## appropiate method.
|
||||||
|
##
|
||||||
|
## Current limitations:
|
||||||
|
## - only one type can be defined within the body of code sent to the
|
||||||
|
## the macro. It is assumed, but not checked, that somewhere in the
|
||||||
|
## inheritance hierarchy this object derives from ``QObject``.
|
||||||
|
## - generics are not currently supported
|
||||||
|
expectKind(qtDecl, nnkStmtList)
|
||||||
|
#echo treeRepr qtDecl
|
||||||
|
result = newStmtList()
|
||||||
|
var slots = newSeq[PNimrodNode]()
|
||||||
|
var properties = newSeq[PNimrodNode]()
|
||||||
|
var signals = newSeq[PNimrodNode]()
|
||||||
|
# holds all user defined procedures so we can add them after create
|
||||||
|
var userDefined = newSeq[PNimrodNode]()
|
||||||
|
# assume only one type per section for now
|
||||||
|
var typ: PNimrodNode
|
||||||
|
for it in qtDecl.children():
|
||||||
|
if it.kind == nnkTypeSection:
|
||||||
|
let typeDecl = it.findChild(it.kind == nnkTypeDef)
|
||||||
|
let superType = typeDecl.getSuperType()
|
||||||
|
if superType.kind == nnkEmpty:
|
||||||
|
# allow simple types and type aliases
|
||||||
|
result.add it
|
||||||
|
else:
|
||||||
|
# may come in useful if we want to check objects inherit from QObject
|
||||||
|
#let superName = if superType.kind == nnkIdent: superType
|
||||||
|
# else: superType.getNodeOf(nnkIdent)
|
||||||
|
if typ != nil:
|
||||||
|
error("you may not define more than one type " &
|
||||||
|
"within the code block passed to this macro")
|
||||||
|
else: # without this else, it fails to compile
|
||||||
|
typ = typeDecl
|
||||||
|
result.add it
|
||||||
|
result.add genSuperTemplate(typeDecl)
|
||||||
|
elif it.kind == nnkMethodDef:
|
||||||
|
if it.hasPragma("slot"):
|
||||||
|
let pragma = it.pragma()
|
||||||
|
it.pragma = pragma.removePragma("slot")
|
||||||
|
slots.add it # we need to gensome code later
|
||||||
|
result.add it
|
||||||
|
elif it.hasPragma("signal"):
|
||||||
|
let pragma = it.pragma()
|
||||||
|
it.pragma = pragma.removePragma("signal")
|
||||||
|
it.body = addSignalBody(it)
|
||||||
|
result.add it
|
||||||
|
signals.add it
|
||||||
|
else:
|
||||||
|
userDefined.add it
|
||||||
|
elif it.kind == nnkProcDef:
|
||||||
|
userDefined.add it
|
||||||
|
elif it.kind == nnkCommand:
|
||||||
|
let bracket = it[0]
|
||||||
|
if bracket.kind != nnkBracketExpr:
|
||||||
|
error("do not know how to handle: \n" & repr(it))
|
||||||
|
# BracketExpr
|
||||||
|
# Ident !"QtProperty"
|
||||||
|
# Ident !"string"
|
||||||
|
let cmdIdent = bracket[0]
|
||||||
|
if cmdIdent == nil or cmdIdent.kind != nnkIdent or
|
||||||
|
($cmdIdent).toLower() != "qtproperty":
|
||||||
|
error("do not know how to handle: \n" & repr(it))
|
||||||
|
properties.add it
|
||||||
|
else:
|
||||||
|
# everything else should pass through unchanged
|
||||||
|
result.add it
|
||||||
|
if typ == nil:
|
||||||
|
error("you must declare an object that inherits from QObject")
|
||||||
|
let typeName = typ.getTypeName()
|
||||||
|
|
||||||
|
## define onSlotCalled
|
||||||
|
var slotProto = (getAst declareOnSlotCalled(typeName))[0]
|
||||||
|
var caseStmt = newNimNode(nnkCaseStmt)
|
||||||
|
caseStmt.add ident("slotName")
|
||||||
|
for slot in slots:
|
||||||
|
var ofBranch = newNimNode(nnkOfBranch)
|
||||||
|
# for exported procedures - strip * marker
|
||||||
|
let slotName = ($slot.name).replace("*","")
|
||||||
|
ofBranch.add newLit slotName
|
||||||
|
let params = slot.params
|
||||||
|
let hasReturn = not (params[0].kind == nnkEmpty)
|
||||||
|
var branchStmts = newStmtList()
|
||||||
|
var args = newSeq[PNimrodNode]()
|
||||||
|
# first params always the object
|
||||||
|
args.add ident "myQObject"
|
||||||
|
for i in 2.. <params.len:
|
||||||
|
let pType = getArgType params[i]
|
||||||
|
# function that maps Qvariant type to nim type
|
||||||
|
let mapper = nimFromQtVariant[$pType]
|
||||||
|
let argAccess = newNimNode(nnkBracketExpr)
|
||||||
|
.add (ident "args")
|
||||||
|
.add newIntLitNode(i-1)
|
||||||
|
let dot = newDotExpr(argAccess, ident mapper)
|
||||||
|
args.add dot
|
||||||
|
var call = newCall(ident slotName, args)
|
||||||
|
if hasReturn:
|
||||||
|
# eg: args[0].strVal = getName(myQObject)
|
||||||
|
let retType = params[0]
|
||||||
|
let mapper = nimFromQtVariant[$retType]
|
||||||
|
let argAccess = newNimNode(nnkBracketExpr)
|
||||||
|
.add (ident "args")
|
||||||
|
.add newIntLitNode(0)
|
||||||
|
let dot = newDotExpr(argAccess, ident mapper)
|
||||||
|
call = newAssignment(dot, call)
|
||||||
|
branchStmts.add call
|
||||||
|
ofBranch.add branchStmts
|
||||||
|
caseStmt.add ofBranch
|
||||||
|
# add else: discard
|
||||||
|
caseStmt.add newNimNode(nnkElse)
|
||||||
|
.add newStmtList().add newNimNode(nnkDiscardStmt).add newNimNode(nnkEmpty)
|
||||||
|
slotProto.body = newStmtList().add caseStmt
|
||||||
|
result.add slotProto
|
||||||
|
|
||||||
|
# generate create method
|
||||||
|
var createProto = (getAst prototypeCreate(typeName))[0]
|
||||||
|
# the template creates loads of openSyms - replace these with idents
|
||||||
|
createProto = doRemoveOpenSym(createProto)
|
||||||
|
if typ.isExported:
|
||||||
|
createProto.exportDef()
|
||||||
|
else:
|
||||||
|
createProto.unexportDef()
|
||||||
|
var createBody = createProto.templateBody
|
||||||
|
for slot in slots:
|
||||||
|
let params = slot.params
|
||||||
|
let regSlotDot = newDotExpr(ident "myQObject", ident "registerSlot")
|
||||||
|
let name = ($slot.name).replace("*","")
|
||||||
|
let argTypesArray = genArgTypeArray(params)
|
||||||
|
let call = newCall(regSlotDot, newLit name, argTypesArray)
|
||||||
|
createBody.add call
|
||||||
|
for signal in signals:
|
||||||
|
let params = signal.params
|
||||||
|
let regSigDot = newDotExpr(ident "myQObject", ident "registerSignal")
|
||||||
|
let name = ($signal.name).replace("*","")
|
||||||
|
let argTypesArray = genArgTypeArray(params)
|
||||||
|
let call = newCall(regSigDot, newLit name, argTypesArray)
|
||||||
|
createBody.add call
|
||||||
|
for property in properties:
|
||||||
|
let bracket = property[0]
|
||||||
|
expectKind bracket, nnkBracketExpr
|
||||||
|
#Command
|
||||||
|
# BracketExpr
|
||||||
|
# Ident !"QtProperty"
|
||||||
|
# Ident !"string"
|
||||||
|
# Ident !"name"
|
||||||
|
# StmtList
|
||||||
|
let nimPropType = bracket[1]
|
||||||
|
let qtPropMeta = nim2QtMeta[$nimPropType]
|
||||||
|
if qtPropMeta == nil: error($nimPropType & " not supported")
|
||||||
|
let metaDot = newDotExpr(ident "QMetaType", ident qtPropMeta)
|
||||||
|
let propertyName = property[1]
|
||||||
|
var read, write, notify: PNimrodNode
|
||||||
|
let stmtList = property[2]
|
||||||
|
# fields
|
||||||
|
# StmtList
|
||||||
|
# Asgn
|
||||||
|
# Ident !"read"
|
||||||
|
# Ident !"getName
|
||||||
|
for asgn in stmtList.children:
|
||||||
|
let name = asgn[0]
|
||||||
|
case $name
|
||||||
|
of "read":
|
||||||
|
read = asgn[1]
|
||||||
|
of "write":
|
||||||
|
write = asgn[1]
|
||||||
|
of "notify":
|
||||||
|
notify = asgn[1]
|
||||||
|
else:
|
||||||
|
error("unknown property field: " & $name)
|
||||||
|
let regPropDot = newDotExpr(ident "myQObject", ident "registerProperty")
|
||||||
|
let readArg = if read == nil: newNilLit() else: newLit($read)
|
||||||
|
let writeArg = if write == nil: newNilLit() else: newLit($write)
|
||||||
|
let notifyArg = if notify == nil: newNilLit() else: newLit($notify)
|
||||||
|
let call = newCall(regPropDot, newLit($propertyName), metaDot, readArg, writeArg, notifyArg)
|
||||||
|
createBody.add call
|
||||||
|
|
||||||
|
#echo repr createProto
|
||||||
|
result.add createProto
|
||||||
|
|
||||||
|
for fn in userDefined:
|
||||||
|
result.add fn
|
||||||
|
|
||||||
|
debug:
|
||||||
|
echo repr result
|
|
@ -1,14 +1,14 @@
|
||||||
import tables
|
import tables
|
||||||
|
|
||||||
type
|
type
|
||||||
QVariant* = distinct pointer
|
QVariant* = distinct pointer ## A QVariant
|
||||||
QQmlApplicationEngine* = distinct pointer
|
QQmlApplicationEngine* = distinct pointer ## A QQmlApplicationEngine
|
||||||
QQmlContext* = distinct pointer
|
QQmlContext* = distinct pointer ## A QQmlContext
|
||||||
QApplication* = distinct pointer
|
QApplication* = distinct pointer ## A QApplication
|
||||||
QObject* {.inheritable.} = ref object of RootObj
|
QObject* {.inheritable.} = ref object of RootObj ## A QObject
|
||||||
name*: string
|
name*: string
|
||||||
data*: pointer
|
data*: pointer
|
||||||
slots*: Table[string, cint]
|
slots*: Table[string, cint]
|
||||||
signals*: Table[string, cint]
|
signals*: Table[string, cint]
|
||||||
properties*: Table[string, cint]
|
properties*: Table[string, cint]
|
||||||
QQuickView* = distinct pointer
|
QQuickView* = distinct pointer ## A QQuickView
|
||||||
|
|
|
@ -23,7 +23,7 @@ function(add_nim_executable )
|
||||||
|
|
||||||
# add target to trigger the nimrod compiler
|
# add target to trigger the nimrod compiler
|
||||||
add_custom_target(
|
add_custom_target(
|
||||||
nim ALL
|
${ARGS_TARGET} ALL
|
||||||
COMMAND
|
COMMAND
|
||||||
${NIM_EXECUTABLE} "c" ${in_paths} "--nimcache:" ${DIRECTORY} "--out:" ${nim_target} ${in_files}
|
${NIM_EXECUTABLE} "c" ${in_paths} "--nimcache:" ${DIRECTORY} "--out:" ${nim_target} ${in_files}
|
||||||
DEPENDS
|
DEPENDS
|
||||||
|
|
|
@ -4,6 +4,13 @@ THIS IS UNSTABLE AND ALPHA SOFTWARE
|
||||||
## Description
|
## Description
|
||||||
Qml bindings for both D and Nim programming languages
|
Qml bindings for both D and Nim programming languages
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
The documentation for the Nim programming language can be
|
||||||
|
read [here](http://filcuc.github.io/DOtherSide/ "").
|
||||||
|
|
||||||
|
For the D programming language is an on going project
|
||||||
|
and pull request are accepted.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
You need the following software:
|
You need the following software:
|
||||||
* Qt 5.3
|
* Qt 5.3
|
||||||
|
|
Loading…
Reference in New Issue