Creating a PhotonUI widget is not difficult, but you will need to learn a few things about how PhotonUI is built and works behind the scene, especially the Class system, the widgets hierarchy and the automagical functionality of the Base PhotonUI class (photonui.Base
).
Also, I recommend you take a look at the class reference documentation and the existing widgets to have a better understanding of how widgets work.
Abitbol: The Heart of The PhotonUI Class System
Abitbol is a tiny javascript library that provides an handful (es5) class système.
Dealing With Classes
Declaring a class using Abitbol is very simple:
1 | var MyClass = photonui.lib.Class.$extend({ |
… and using it is straightforward:
1 | var foo = new MyClass(param1, param2); |
Extending an existing class is also simple:
1 | var MySecondClass = MyClass.$extend({ |
Automagical Properties
Abitbol automatically creates properties from available getters and setters. To understand the principle, let’s study a simple example:
1 | var Person = photonui.lib.Class.$extend({ |
There are many ways to get and set the firstName
of the Person
class:
1 | // When instantiating the class |
NOTE¹: Pay attention to the underscore when we defined the _firstName
attribute. Since the firstName
property will be automatically created by the abitbol
class, you will create an infinite loop if you name your private property the same way.
Going Deeper With Abitbol
Abitbol provides a lot of other interesting functionalities like annotations, “mixin” and class vars… For more information read the Abitbol documentation.
The “photonui.Base” Class
In PhotonUI, no widget directly extends the Abitbol’s Class
. All widgets extend at least the photonui.Base
class, and most often, the photonui.Widget
class.
About The Constructor Method
The constructor method of all PhotonUI widgets always calls the constructor method of the photonui.Base
class, generally using the this.$super(params)
. The constructor method of each widgets also always takes one argument: a parameter object that should be passed to the photonui.Base
class constructor.
A simple example to understand:
1 | var MyWidget = photonui.Widget.$extend({ |
Choosing The Right Base Class
When you create a PhotonUI widget, you will extend different classes depending on what kind of widget you want to create.
There are 5 main types of widgets in PhotonUI:
For Interactive widgets (like
photonui.Button
,photonui.TextField
,…) or Visual-Only widgets: (likephotonui.FAIcon
,photonui.Label
orphotonui.Separator
), you will generally extend thephotonui.Widget
class,for Non-Visual widgets (like
photonui.Translation
,photonui.FileManager
orphotonui.MouseManager
), you will extend thephotonui.Base
class,for Container widgets (like
photonui.Window
orphotonui.MenuItem
), you will probably extend thephotonui.Container
class,and for Layout widgets (like
photonui.BoxLayout
orphotonui.GridLayout
) you should extend thephotonui.Layout
class.
Of course, if you want to build a more specific widget, you can extend more specialized classes.
Minimal Widget Templates
Depending on what kind of widget you want to create, you will inherit from different classes, and you will have to define different methods to make things work.
Template for “Non-Visual” Widgets
Non-visual widgets have no specific method to override to make things work:
1 | var MyWidget = photonui.Base.$extend({ |
You can however override the destroy
method if it is relevant:
1 | destroy: function() { |
Template for “Interactive” and “Visual-Only” Widgets
For interactive and visual widgets, you should at least implement the getHtml
and the _buildHtml
methods:
1 | var MyWidget = photonui.Widget.$extend({ |
Template for “Container” Widgets
The container widgets have the same methods as the interactive and visual widgets plus getContainerNode
:
1 | var MyWidget = photonui.Container.$extend({ |
Template for “Layout” Widgets
The layout widgets are the most difficult to build: in addition to getHtml
and _buildHtml
methods, you have to implement the _updateLayout
method which will have to build the HTML that glues children widgets together in the layout.
You can look at the photonui.BoxLayout
code if you want a simple example of a layout widget.
1 | var MyWidget = photonui.Layout.$extend({ |
Example: Creating a Simple Button Widget
No more theory, let’s build a real widget: a simple button with a text
property to define… the button’s text, a click
wEvent, and even the internationalisation support.
HTML and Basic Code
Here we just apply everything we saw before to create the visual part of the button. You can also add a bit of CSS to make it look better, but that’s not the topic.
1 | var SimpleButton = photonui.Widget.$extend({ |
The “text” Property
Then we add our text
property that allows to set the button’s text:
1 | _text: "Button", |
NOTE: The code as we wrote it here works, but if the user never sets the text
property, the widget will not display the default text defined in the class (in the _text
attribute) since the setter is never called. To force properties to be refreshed even when not set, you must use the @photonui-update
annotation:
1 | getText: function() { |
This abitbol’s annotation can go either in the getter function or in the setter function. It must be at the top of the function code.
Adding Interactivity (wEvent)
There are two kinds of events to deal with when you build a PhotonUI widget:
The native javascript events, that will be used only behind the scene by the widget,
and the wEvents, that are exposed through the widget API
So the widget binds native js event to its DOM elements and can expose a corresponding wEvent if required.
First, we have to declare the available wEvents of the widget. This can be done with the _registerWEvents
:
1 | __init__: function(params) { |
Then, we have to bind the native javascript click
event to the button. To bind native js events, PhotonUI provides an event manager that will automatically unbind the events when the widget is destroyed; you can use it through two methods: _bindEvent
and _unbindEvent
.
1 | __init__: function(params) { |
Finally we create our internal callback that will be in charge of calling the callbacks of our wEvent using the _callCallbacks
method:
1 | __onButtonClicked: function(event) { |
NOTE: The first argument of all the wEvent callbacks is always the widget that called it:
1 | function myWEventCallback( widget [, additionalParam1 [, additionalParam2 [, ...]]] ) { |
The Final Code
1 | var SimpleButton = photonui.Widget.$extend({ |
Here We Are
Voilà, you created your first PhotonUI widget. Not too difficult right ? ;)