next weeks and months I will blog about many different use cases from ‘Mobile Computing’
2nd Screen
It’s easy to connect to a 2nd (bigger) Screen from your BlackBerry 10 device: Z10, Z30, Q10 have a HDMI Micro Connector and you can easy plug in via HDMI cable to a bigger screen. The Z30 also supports Miracast to connect wireless (HDMI).
By default the BlackBerry 10 Device – Screen is mirrored to the big screen and in many cases this is what you want, per ex. while watching a Movie.
Some Applications like Docs2Go – Powerpoint display different content: the actual slide on the big screen and actual plus next slide or notes on the device screen. If you want to do this from Cascades, you’ll find a notice in the documentation of SDK 10.2, that this is currently not possible – you have to use OpenGL ES.
I’m not familar with OpenGL ES and I don’t want to output my content this way. So I have to wait for a future Cascades relase supporting 2nd screen – Design.
But I’m already developing business apps using scenarios from Mobile Computing (per ex. SerCar10) – so I tried to make it something better with the tools available NOW. To let you know what I found out, I created a sample app and pushed to a public Github repository:
There’s also a Thread in the forums, if you want to discuss this topic.
Here’s an example of a ScoreList running on Z30:
There are 5 items displayed. Connecting the device to HDMI you’ll see the same 5 items, but much larger.
Would it be cool to get more on your big screen from same ListView and same QML file ?
Here it is:
The text would be too small on the device – screen, but looks good on the big screen.
Scaling the ListView
The easiest way to scale a ListView is
var scaleFactor = 0.5 listView.scaleX = scaleFactor listView.scaleY = scaleFactor
but this doesn’t really help you, because now the ListView is scaled down by 0.5 and positioned in the middle of your screen surrounded by white or dark space denpending on your Theme:
OK – seems we have to enhance the width and height to make the ListView as large as the Screen. If we scaled down to 0.5, we have to use the reciprocal value – in this case 1 / 0.5 = 2. For a screen of 1280 x 720:
var extendFactor = 1 / scaleFactor listView.minWidth = 1280 * extendFactor listView.minHeight = 720 * extendFactor
Now we get this:
Seems there’s something outside the visible screen. You probably know, that you can ‘move’ the positioning of a UI Control using translateX and translateY.
To calculate the offset we have to subtract the origin width (or height) from the new (enlarged) width (or height) and divide by -2.
Our solution should work on all devices, where Z10 has 1280 x 768 and Z30 has 1280 x 720 and who knows about upcoming devices, so we need the actual sizes and no constants.
Also our simplified sample above ignores that there could be a Title Bar and an Action Bar and we know: Cascades is intelligent and uses different sizes for TitleBar and ActionBar on Z10 and Z30.
We have to find a way to get the size of the ListView and exactly for this Cascades gives us the LayoutUpdateHandler we can attach to UI Controls. For a ListView we have to attach the handler to the surrounding Container.
The LayoutUpdateHandler gives us:
layoutFrame.width layoutFrame.height layoutFrame.X layoutFrame.Y
The values are always the origin values ignoring scaling or translations. This is great: we get the old values and know the scaleFactor to calculate the new ones.
Where the LayoutUpdateHandler is connected to the Container, we have to do the scaling on the ListView. To handle different situations, we use a function to do the scaling. To make it easy for you to test, there’s an ActionItem to switch between unscaled Device screen and scaled screen.
To persist the state we use a property on the NavigationPane. Also we only want to scale if the device is in Landscape orientation.
// properties set for NavigationPane property variant scaleFactor: 0.666 property bool use2ndScreen: false onUse2ndScreenChanged: { // current orientation is Landscape ? scale immediately if (OrientationSupport.orientation == UIOrientation.Landscape) { scaled = use2ndScreen } } onScaleFactorChanged: { // current orientation is Landscape AND already scaled ? re-scale immediately if (scaled && OrientationSupport.orientation == UIOrientation.Landscape) { scaleListView() } } // this is the trigger to scale the ListView for HDMI or Device property bool scaled: false onScaledChanged: { scaleListView() } // ActionItem to swap scaled/unscaled ActionItem { title: use2ndScreen ? "On Device" : "On 2nd Screen" imageSource: use2ndScreen ? "asset:///images/device.png" : "asset:///images/ic_hdmi.png" ActionBar.placement: ActionBarPlacement.OnBar onTriggered: { use2ndScreen = ! use2ndScreen } } // Function to scale the ListView function scaleListView() { var extendFactor = 1 / scaleFactor if (scaled) { listView.scaleX = scaleFactor listView.scaleY = scaleFactor listView.minWidth = listViewLayoutHandler.layoutFrame.width * extendFactor listView.minHeight = listViewLayoutHandler.layoutFrame.height * extendFactor listView.translationX = (listView.minWidth - listViewLayoutHandler.layoutFrame.width) / -2 listView.translationY = (listView.minHeight - listViewLayoutHandler.layoutFrame.height) / -2 } else { listView.scaleX = 1 listView.scaleY = 1 listView.minWidth = 0 listView.minHeight = 0 listView.translationX = 0 listView.translationY = 0 } }
As you see: to reset the scaling simply set scaleX and scaleY to 1 and the other values to zero.
If you take a deeper look at the app you’ll see that there are some Actions with predefined scaling factors, so you can easy test it out with your hardware.
There’s even a Slider to try your own values. You know: “peeking-back” is a fundamental UI feature of BlackBerry 10, but have you ever used it to display something different while peeking back ? Here’s your chance to learn about: while changing the Slider from a Page pushed on top of the List you can peek back to see the scaled ListView. (If in Landscape and switch is set to ‘HDMI’)
onPeekStarted: { scaleFactor = scaleSliderPage.scaling }
From my experiences a scaling factor of 0.666 looks great and also up to 0.40 the text was readable on my HD Monitor and also using HD Beamer in my HomeCinema. BTW: 0.666 means that you’re enlarging from 1280px to 1920px, which probably fits best on your FullHD Monitor.
Scale automagically if 2nd Screen attached
To test it out, ActionItems are are great, but for a real – life Application scaling should automatically be done if a 2nd Screen was attached – doesn’t matter if via HDMI cable or wireless using Miracast.
Thanks to Signals / Slots – concept from Qt this is easy to solve.
From C++
// monitor attached 2nd screens int secondaryDisplayId = bb::device::DisplayInfo::secondaryDisplayId(); mSecondaryDisplayInfo = new bb::device::DisplayInfo(secondaryDisplayId, this); // to be notified when the attached state changes. bool ok = QObject::connect(mSecondaryDisplayInfo, SIGNAL(attachedChanged(bool)), this, SLOT(secondaryDisplayAttachedChanged(bool))); void ApplicationUI::secondaryDisplayAttachedChanged(bool connected) { emit secondScreenAttached(connected); }
From QML connect to the signal:
function onSecondScreenAttached(connected) { if (connected) { use2ndScreen = true // support only Landscape orientation OrientationSupport.supportedDisplayOrientation = SupportedDisplayOrientation.DisplayLandscape; } else { use2ndScreen = false // support all orientations OrientationSupport.supportedDisplayOrientation = SupportedDisplayOrientation.All; } } onCreationCompleted: { // connect to signal if 2nd screen is connected app.secondScreenAttached.connect(onSecondScreenAttached) } }
Now if you connect to the 2nd Screen your APP automatically switches to Landscape and scales the ListView.
Have Fun with your HDMI Screen and scaling
The sample application is well documentated, so you should understand easy. I hope this gives you some ideas HowTo deal with external screens using 10.2.0 SDK. You can use the technic also for ScrollViews or Pages where you enter data. It’s up to you not only to scale but also to add fields, change Layouts, rearrange content and more.
For me it’s now easier to design my SerCar10 App to be used On-The-Go while out there with your smartphone or In-The-Office. But that’s another story to tewll you later …
Download from GitHub cascades_lists_on_hdmi and if you have questions, please discuss in the forum thread here.