diff --git a/website/docs/development/getting-started.md b/website/docs/development/getting-started.md index fbe60e723..a7de3d28d 100644 --- a/website/docs/development/getting-started.md +++ b/website/docs/development/getting-started.md @@ -85,7 +85,7 @@ The idea is : 2. Then we will use NLabel and wrap it using NAPI and export it to JS side. This is what qlabel_wrap does. **NLabel**: Since NLabel has inherited from QLabel we can treat is as QLabel with extra methods and properties. Primary reason to extend QLabel to create NLabel is to add support for Event listeners and CSS styling using Flex. -So if you take a look at NLabel you will see, it inherits from QLabel and NodeWidget. NodeWidget inturn inherits from YogaWidget and EventWidget. Event widget adds event handling support. YogaWidget is a class that contains the magic that enables a regular Qt Widget to have Yoga node. A Yoga node is an instance used by yoga library to calculate a widgets position on the screen. Yoga is a library that will layout the widget on the screen. To do so we will specify the flex properties like alignitems, justify content, margin, paddings etc on the Yoga node of the widget. Apart from adding yoga node, YogaWidget adds support for specifying those yoga properties via Qt's stylesheet. (This is done by using Q_PROPERTY). To make this work we need to use something called as Q_OBJECT inside the class which is a C++ macro. Q_OBJECT will be expanded to relevant code by the compiler. In Qt whenever we add Q_OBJECT to a header file, we need to use a pre compiler called Qt MOC (Meta Object Compiler). The way we use it is +So if you take a look at NLabel you will see, it inherits from QLabel and QWidget. QWidget in turn inherits from YogaWidget and EventWidget. Event widget adds event handling support. YogaWidget is a class that contains the magic that enables a regular Qt Widget to have Yoga node. A Yoga node is an instance used by yoga library to calculate a widgets position on the screen. Yoga is a library that will layout the widget on the screen. To do so we will specify the flex properties like alignitems, justify content, margin, paddings etc on the Yoga node of the widget. Apart from adding yoga node, YogaWidget adds support for specifying those yoga properties via Qt's stylesheet. (This is done by using Q_PROPERTY). To make this work we need to use something called as Q_OBJECT inside the class which is a C++ macro. Q_OBJECT will be expanded to relevant code by the compiler. In Qt whenever we add Q_OBJECT to a header file, we need to use a pre compiler called Qt MOC (Meta Object Compiler). The way we use it is ``` moc headername.h -o headername_moc.cpp --include // example : ../../core/YogaWidget/yogawidget.h diff --git a/website/docs/development/signal_and_event_handling.md b/website/docs/development/signal_and_event_handling.md index 2cca815a9..363b00b16 100644 --- a/website/docs/development/signal_and_event_handling.md +++ b/website/docs/development/signal_and_event_handling.md @@ -52,7 +52,7 @@ Example: ```js import addon from '../../core/addon'; -import { NodeWidget } from '../../QtGui/QWidget'; +import { QWidget } from '../../QtGui/QWidget'; import { BaseWidgetEvents } from '../../core/EventWidget'; export const QPushButtonEvents = Object.freeze({ @@ -63,8 +63,8 @@ export const QPushButtonEvents = Object.freeze({ toggled: 'toggled', }); -export class QPushButton extends NodeWidget { - constructor(parent?: NodeWidget) { +export class QPushButton extends QWidget { + constructor(parent?: QWidget) { let native: NativeElement; if (parent) { native = new addon.QPushButton(parent.native); diff --git a/website/docs/guides/understanding-memory.md b/website/docs/guides/understanding-memory.md new file mode 100644 index 000000000..a78344410 --- /dev/null +++ b/website/docs/guides/understanding-memory.md @@ -0,0 +1,81 @@ +--- +sidebar_label: Understanding Memory +title: Understanding Memory +--- + +NodeGui allows you to use a library designed for C++ (Qt) from the JavaScript language. C++ and JavaScript have very different approaches for managing the memory of objects. C++ expects the programmer to manually create objects and later destroy them to free up their memory. It is the job of the programmer to destroy objects at the right time and when it is safe. JavaScript uses a Garbage Collector which detects when an object is longer being used and then automatically destroys it, freeing up its memory. Most of the time NodeGui can coordinate these two systems in a way which is mostly transparent to the JavaScript developer. However, there are situations where it is necessary to have some understanding of what is happening being in the scenes. + +## Qt's QObject Memory Management + +Much of Qt is based around the `QObject` class. It is the base class of many Qt classes including most of the widgets exposed by NodeGui. `QObject` provides many useful features on top of C++ for working with objects. It has support for easier memory management. This system is built around the idea of object ownership and trees of objects which can be freed as a group. Each `QObject` can participate in a tree of objects. Each `QObject` has a list of child objects, and may also have a parent `QObject`. When a `QObject` is destroyed, all of its attached child objects are destroyed with it. + +Trees of objects is a good model for user interfaces which are also structured in hierarchies. Qt's widgets are built on top of `QObject` and its object trees. The same object tree is used by widgets for memory management and UI layout / structuring. + +`QObject` contains a number of methods for examining this tree of objects: + +* `parent()` - Get the parent of an object, or `null`. +* `setParent()` - Set the parent of an object. +* `children()` - Get a list of the child objects belonging to this object. + + +## JavaScript Wrappers + +Node runs on the V8 JavaScript engine. It can't directly work with C++ object like those from Qt. To bridge the gap between V8 and Qt, NodeGui creates "wrapper" C++ and JavaScript objects which can translate between the two worlds. A JavaScript object "wrapper" will wrap a corresponding Qt object in C++. As a JavaScript developer you don't have to pay much attention to this. It happens automatically in the background. + +## QObject End of Life + +There are two ways in which a `QObject` can be destroyed, starting from either the C++ side or JavaScript side. + +The JavaScript side is the most common. If the Garbage Collector discovers that a wrapper is no longer being referenced, then it will destroy the wrapper. When the C++ side of the wrapper is destroyed, it will also destroy its corresponding Qt object *if that object is not part of an object tree*. In other words, if the `QObject` has a parent object set, then it will not be destroyed and NodeGui assumes that the responsibility for destroying and cleaning up that object belongs to the parent object and the Qt `QObject` memory system. + +When a `QObject` is destroyed from the C++ side, then NodeGui detects this and sets the reference to the object itside the JavaScript wrapper object, to `null`. If any attempt is made to use the JS wrapper object, then it will hit the `null` and throw an exception. + +## Problems with Unexpected Object Destruction + +A NodeGui object suddenly throws an exception regarding a `null` if you try to use a method on it. This situation can happen if underlying C++ Qt object is destroyed and afterwards you still try to use it via its wrapper. + +The most common way this can happen is if a `QObject` is part of an object tree and one of its parent objects is destroyed which then also destroys the rest of the tree. If you don't want an object to be destroyed with its parent object, then you need to remove it from the tree before the parent is destroyed. Calling `setParent(null)` on the object is enough to disconnect it from its parent. + +## Debugging + +NodeGui provides some tools to help track the lifecycle of objects and figure out why your application isn't running the way you expect. + +Each `QObject` instance, and subclass also, has an ID to identify it. The method `QObject._id()` can be used to fetch the ID for an object. The ID is valid for the lifetime of the object, although it could be reused once this object is destroyed. + +NodeGui manages the lifecycle of objects and it is possible to turn on logging regarding the creation and destruction of objects. The following functions take a boolean to turn this logging on or off: + +* `setLogDestroyQObject()` - Turn logging of object destruction on/off. +* `setLogCreateQObject()` - Turn logging of object creation on/off. + +These log messages contain the ID of the object as returned from `QObject._id()`. + +A typical usage of this logging would be to narrow down the exact place where an object is being unexpectedly destroyed: + +```js +const { setLogDestroyQObject, setLogCreateQObject } = require('@nodegui/nodegui'); + +setLogCreateQObject(true); +setLogDestroyQObject(true); + +const myWidget = new QWidget(); +console.log(`myWidget has ID: ${myWidget._id()}.`); + +// ... +// ... Code which somehow triggers the destruction of myWidget. +// ... +console.log(`Reached crash point.`); +const size = myWidget.size(); +// ^ line which triggers an exception because the object is destroyed. +``` + +The logging output from the application might be: + +``` +NodeGui: Created C++ object with ID: 17533348349032. +myWidget has ID: 17533348349032. +NodeGui: Destroyed C++ object with ID: 17533348349032. +Reached crash point. +Uncaught TypeError: Cannot read property 'size' of null +``` + +More logging could be added to narrow down which section of code triggers the destruction of the object.