What I'll be showing in this article is how you can wrap this event-providing BPS API for use from within a Cascades UI. In order to do this, we'll have to do several things:
To do this, we'll be doing the following:
- Implement AbstractBpsEventHandler in a class that:
- Subscribes to BPS virtual keyboard events
- Handles those events to monitor virtual keyboard changes
- Keeps track of the last known virtual keyboard visibility and height
- Implement a QObject subclass that:
- Provides Qt properties for current virtual keyboard state
- Provides Qt signals for changes to virtual keyboard state
- Is registered for creation from within QML
In a simple case, we could implement both of these within the same class. However, there's a problem with this. BPS event subscriptions are thread-global, which means having two simultaneous instances of this class could become problematic. Specifically, this means one instance could inadvertently unsubscribe the other.
One solution to this problem is to do a variation on the C++ pImpl pattern. Specifically, what we'll do is:
- Create a "private" class that implements AbstractBpsEventHandler
- Register this class as a singleton using Q_GLOBAL_STATIC
- Create a "public" class that subclasses QObject
- Have this class interact with the single private class for virtual keyboard state and events
With this approach, we can create as many instances of the public class as we want, while only having a single instance of the class that actually handles the BPS events.
Public declaration
class VirtualKeyboard : public QObject { Q_OBJECT Q_PROPERTY(bool visible READ visible NOTIFY visibleChanged FINAL) Q_PROPERTY(int height READ height NOTIFY heightChanged FINAL) public: explicit VirtualKeyboard(QObject *parent=0); virtual ~VirtualKeyboard(); bool visible() const; int height() const; signals: void visibleChanged(); void heightChanged(); private: Q_DISABLE_COPY(VirtualKeyboard) };
Registering with Qt
qmlRegisterType<VirtualKeyboard>("ex.controls", 1, 0, "VirtualKeyboard");
Using from QML
import bb.cascades 1.0 import ex.controls 1.0 Page { Container { TextArea { } Label { text: "Keyboard visible: " + virtualKeyboard.visible } Label { text: "Keyboard height: " + virtualKeyboard.height } } attachedObjects: [ VirtualKeyboard { id: virtualKeyboard onVisibleChanged: { // do stuff } onHeightChanged: { // do stuff } } ] }
Private declaration
class VirtualKeyboardPrivate : public bb::AbstractBpsEventHandler { public: VirtualKeyboardPrivate(); virtual ~VirtualKeyboardPrivate(); void addInstance(VirtualKeyboard *instance); void removeInstance(VirtualKeyboard *instance); virtual void event(bps_event_t *event); bool started_; bool visible_; int height_; private: void start(); void stop(); QSet<VirtualKeyboard *> instances_; }; Q_GLOBAL_STATIC(VirtualKeyboardPrivate, globalVirtualKeyboardPrivate)
Private implementation
VirtualKeyboardPrivate::VirtualKeyboardPrivate() : started_(false), visible_(false), height_(0) { subscribe(virtualkeyboard_get_domain()); bps_initialize(); } VirtualKeyboardPrivate::~VirtualKeyboardPrivate() { stop(); bps_shutdown(); } void VirtualKeyboardPrivate::addInstance(VirtualKeyboard *instance) { instances_.insert(instance); start(); } void VirtualKeyboardPrivate::removeInstance(VirtualKeyboard *instance) { instances_.remove(instance); if(instances_.isEmpty()) { stop(); } } void VirtualKeyboardPrivate::start() { if(!started_) { virtualkeyboard_request_events(0); virtualkeyboard_get_height(&height_); started_ = true; } } void VirtualKeyboardPrivate::stop() { if(started_) { virtualkeyboard_stop_events(0); visible_ = false; height_ = 0; started_ = false; } } void VirtualKeyboardPrivate::event(bps_event_t *event) { if(bps_event_get_domain(event) == virtualkeyboard_get_domain()) { unsigned int eventCode = bps_event_get_code(event); if(eventCode == VIRTUALKEYBOARD_EVENT_VISIBLE) { if(!visible_) { visible_ = true; foreach(VirtualKeyboard *instance, instances_) { QMetaObject::invokeMethod(instance, "visibleChanged"); } } } else if(eventCode == VIRTUALKEYBOARD_EVENT_HIDDEN) { if(visible_) { visible_ = false; foreach(VirtualKeyboard *instance, instances_) { QMetaObject::invokeMethod(instance, "visibleChanged"); } } } else if(eventCode == VIRTUALKEYBOARD_EVENT_INFO) { int height = virtualkeyboard_event_get_height(event); if(height_ != height) { height_ = height; foreach(VirtualKeyboard *instance, instances_) { QMetaObject::invokeMethod(instance, "heightChanged"); } } } } }
Public implementation
VirtualKeyboard::VirtualKeyboard(QObject *parent) : QObject(parent) { VirtualKeyboardPrivate *d = globalVirtualKeyboardPrivate(); d->addInstance(this); } VirtualKeyboard::~VirtualKeyboard() { VirtualKeyboardPrivate *d = globalVirtualKeyboardPrivate(); d->removeInstance(this); } bool VirtualKeyboard::visible() const { VirtualKeyboardPrivate *d = globalVirtualKeyboardPrivate(); return d->visible_; } int VirtualKeyboard::height() const { VirtualKeyboardPrivate *d = globalVirtualKeyboardPrivate(); return d->height_; }
No comments:
Post a Comment