Coherent UI  2.5.3
A modern user interface library for games
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
Binding for C++

Coherent UI allows easy communication between your application and the UI via several mechanisms.

Events

C++ triggering JavaScript

To trigger an event for the view use

// set player score to 10000
view->TriggerEvent('PlayerScoreChanged', 10000);

For the view to be able to catch this event it must have executed:

engine.on('PlayerScoreChanged', function (new_score) {
var scoreDisplay = document.getElementById('scoreDisplay');
scoreDisplay.innerHTML = new_score;
});
See Also
For more information about engine, see JavaScript.

JavaScript triggering C++

Registering C++ functions for events triggered by JavaScript should happend the in the handler of Coherent::UI::ViewListener::OnReadyForBindings event.

Event Handlers

Event handlers are registered with the Coherent::UI::View::RegisterForEvent method. They cannot return any value to JavaScript, but may trigger an event for JavaScript. There may be more than one C++ handler for a given event. There also may be both C++ and JavaScript handlers for the same event.

Warning
JavaScript handlers for an event are called synchronously, but C++ handlers for an event are called asynchronously. There is no guarantee for the order of execution of C++ and JavaScript event handlers.
There is no guarantee for the order of execution of C++ event handlers.
class Game
{
public:
void Quit()
{
}
} g_Game;
class GameViewListener : public Coherent::UI::ViewListener
{
public:
virtual void OnReadyForBindings(int frameId, const wchar_t* url, bool isMainFrame)
{
m_View->RegisterForEvent("OnQuitClicked", Coherent::UI::MakeHandler(&g_Game, &Game::Quit));
}
};

Triggering the event from JavaScript looks like:

engine.on('OnQuitClicked', function () {
ShowMessage('Bye');
});
// using jQuery to simplify the sample
$('#QuitButton').click(function () {
// this will execute both Game::Quit in C++
// and ShowMessage('Bye') in JavaScript
engine.trigger('OnQuitClicked');
});

Coherent::UI::MakeHandler uses template argument deduction to guess the signature of the handler. This requires several specializations of Coherent::UI::FunctorTraits and overloads of Coherent::UI::EventHandler::InvokeStub. To extend the number of arguments for event handlers supported by Coherent UI, you have to add additional specializations and overloads.

Call Handlers

Call handlers are registered with the Coherent::UI::View::BindCall method. There may be only one handler for a given call and the handler may return a result.

class Game
{
public:
std::string GetPlayerName()
{
return m_PlayerName;
}
} g_Game;
class GameViewListener : public Coherent::UI::ViewListener
{
public:
virtual void OnReadyForBindings(int frameId, const wchar_t* url, bool isMainFrame)
{
m_View->BindCall("getPlayerName", Coherent::UI::MakeHandler(&g_Game, &Game::GetPlayerName));
}
};

To get the player name in the view is:

// call 'Game::GetPlayerName' with callback for the result
engine.call('getPlayerName').then(function (name) {
var playerName = document.getElementById('playerName');
playerName.innerHTML = name;
});

Context Events

There are two context events:

  • Ready - triggered when the page loading has finished and Coherent::UI::ViewListener::OnReadyForBindings as been executed. Triggering events for C++ before Ready will result in strange 'disappearance' of events.
  • Error - triggered when there was an error converting arguments from JavaScript to C++ (i.e. the C++ function expects a number, but JavaScript gives a string)

Unregistering Handlers

You can remove a particular event or call hander using Coherent::UI::View::UnregisterFromEvent and Coherent::UI::View::UnbindCall using the Coherent::UI::BoundEventHandle returned from Coherent::UI::View::RegisterForEvent and Coherent::UI::View::BindCall. You can also remove all handlers that are methods of a particular object at once using Coherent::UI::View::UnbindObject.

Warning
Unregistering or unbinding a handler inside the callback of the handler is not supported and will result in undefined behavior. If you want to remove a handler, schedule the removal to happen outside of the callback.

Exposing C++ Types

To be able to use your C++ types as arguments or results for events and call handlers, the C++ type must be exposed to Coherent UI. Once exposed, Coherent UI will treat them just like any builtin type. To expose a C++ type to Coherent UI use the following pattern:

class Player
{
public:
std::string Name;
double GetScore() const;
void SetScore(double);
};
// called by Coherent UI when an instance of Player goes through
// the C++ / JavaScript boundary
void CoherentBind(Coherent::UI::Binder* binder, Player* player)
{
if (auto type = binder->RegisterType("Player", player))
{
type.Property("name", &Player::Name)
.Property("score", &Player::GetScore, &Player::SetScore)
;
}
}

Note: If you want to use a user-defined type T, you must provide an overload of

void CoherentBind(Coherent::UI::Binder* binder, T* player)

otherwise you'll receive a compile-time error complaining about a missing overload of CoherentBind for the type T.

STL and container types

Coherent UI has built-in support for most STL containers and std:: classes.

C++ Type JavaScript Type Header
std::string String <Coherent\UI\Binding\String.h>
std::vector Array <Coherent\UI\Binding\Vector.h>
std::map Object <Coherent\UI\Binding\Map.h>
std::pair Object <Coherent\UI\Binding\Pair.h>
C style array Array <Coherent\UI\Binding\Array.h>

Support for additional containers can be added in a similar way.

C++ and JavaScript Communication

Registering type Player triggering events in both directions with instances of Player as argument and as a result of a call handler.

class Game
{
public:
void Start()
{
m_View->TriggerEvent("StartWithPlayer", m_Player);
}
private:
Player m_Player;
} g_Game;

Then in JavaScript, we receive an object with the specified properties. The value of each property is the same as in the moment of triggering the event. We may store a reference to this object, but its properties WILL NOT be synchronised with g_Game.m_Player.

engine.on('StartWithPlayer', function (player) {
var playerName = document.getElementById('playerName');
playerName.innerHTML = player.name;
var scoreDisplay = document.getElementById('scoreDisplay');
scoreDisplay.innerHTML = player.score;
});

If you want to call C++ handler with an instance of Player created in JavaScript there is one important detail - the object must have a property __Type with value Player (the same name of the type we gave to Coherent::UI::Binder::RegisterType in CoherentBind for Player. Otherwise Coherent UI cannot treat the object as an instance of Player.

$('#doCreatePlayer').click(function () {
var player = {
__Type: 'Player', // set the type name
name: $('#playerName').val(),
score: 0
};
engine.call('CreatePlayer', player).then(function (success) {
if (success) {
ShowMessage('Welcome ' + player.name);
} else {
ShowMessage('Sorry, try another name');
}
});
});
bool CreatePlayer(const Player& player)
{
if (player.Name.find(' ') == std::string::npos)
{
// create the player
return true;
}
else
{
// sorry, no spaces in player name
return false;
}
}

For some calls it's possible that there is no meaningful value to return. For example -

Item Player::GetItem(int slot)
{
if (HasItemAt(slot))
{
return GetItemAt(slot);
}
else
{
// notify the view that there is not item at this slot
m_View->SetScriptError(Coherent::UI::SCE_NoResult, "no item at slot");
return Item();
}
}
engine.call('GetItem', slot).then(function (item) {
ShowMessage('Item at slot ' + slot + ' costs ' + item.Price);
},
// called when there is no item at this slot
function (errorType, message) {
console.log('could not get item at slot ' + slot);
}
});