Component Showcase Demo App
import Felgo 3.0
import QtQuick 2.5
import QtLocation 5.5
import QtPositioning 5.5
import QtQuick.Controls 2.0 as Quick2
import QtMultimedia 5.5
import QtGraphicalEffects 1.0
import "../controls"
import "../common"
FlickablePage {
id: componentsPage
property var video: null
property var alphaVideo: null
flickable.contentHeight: content.height
property real yBeginning: 0
property real yDelta: flickable.contentY - yBeginning
property real videoContentY: 0
Component.onCompleted: yBeginning = flickable.contentY
onHeightChanged: videoContentY = video !== null ? video.mapToItem(flickable.contentItem, 0 , 0).y : 0
MouseArea {
anchors.fill: parent
onClicked: flickable.forceActiveFocus()
}
Column {
id: content
width: parent.width
anchors.horizontalCenter: parent.horizontalCenter
SectionHeader { icon: IconType.paintbrush; text: "User Interface Styling" }
SectionDescription { text: "You can customize the styling of all UI components and controls, or use the default native look & feel of your platform.\n
Change the tint color or switch the platform theme below. All UI elements will then update their look." }
SectionContent { contentItem: Column {
width: parent.width
spacing: dp(Theme.navigationBar.defaultBarItemPadding)
Row {
id: tintColorRow
anchors.horizontalCenter: parent.horizontalCenter
spacing: parent.spacing
property color defaultColor: Theme.isIos ? "#007aff" : (Theme.isAndroid ? "#3f51b5" : "#01a9e2")
property int currentIndex: 0
Connections {
target: Theme
onPlatformChanged: if(tintColorRow.currentIndex === 0) Theme.colors.tintColor = tintColorRow.defaultColor
}
AppText {
text: "Tint:"
anchors.verticalCenter: parent.verticalCenter
}
Repeater {
model: [tintColorRow.defaultColor, "#FF3B30", "#4CD964", "#FF9500"]
Rectangle {
color: modelData
width: dp(48)
height: dp(48)
MouseArea {
anchors.fill: parent
onClicked: { Theme.colors.tintColor = color; tintColorRow.currentIndex = index }
}
}
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
AppText {
text: "Theme: "
anchors.verticalCenter: parent.verticalCenter
}
IconButton {
anchors.verticalCenter: parent.verticalCenter
icon: IconType.apple
size: dp(24)
color: Theme.isIos ? Theme.tintColor : Theme.secondaryTextColor
onClicked: Theme.platform = "ios"
}
IconButton {
id: androidIcon
anchors.verticalCenter: parent.verticalCenter
icon: IconType.android
color: Theme.isAndroid ? Theme.tintColor : Theme.secondaryTextColor
size: dp(24)
visible: !system.isPlatform(1)
onClicked: Theme.platform = "android"
}
AppButton {
text: "Custom"
anchors.top: parent.top
anchors.topMargin: Theme.isAndroid ? dp(2) : dp(1)
flat: true
verticalPadding: 0
borderColor: Theme.isIos ? Theme.secondaryTextColor : Theme.tintColor
textColor: Theme.isIos ? borderColor : Theme.appButton.flatTextColor
visible: !androidIcon.visible
onClicked: Theme.platform = "android"
}
}
}
}
SectionSpacer { height: dp(5) }
SectionHeader { icon: IconType.mobile; text: "Native Features" }
SectionDescription { text: "Use native features like opening dialogs or access device information with a single code base & cross-platform." }
SectionContent { contentItem: Column {
width: parent.width
spacing: dp(10)
Row {
anchors.horizontalCenter: parent.horizontalCenter
AppButton { text: "Alert Dialog"; onClicked: nativeUtils.displayAlertDialog("Alert", "Be careful!", "I will", "Don't worry!"); verticalMargin: 0; flat: false }
AppButton { text: "Input Dialog"; onClicked:NativeDialog.inputText("Input", "This is a native input dialog."); verticalMargin: 0; flat: false }
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
AppButton { text: "Alert Sheet"; onClicked: nativeUtils.displayAlertSheet("Choose", ["Pizza", "Pasta", "Steak"], true); verticalMargin: 0; flat: false }
AppButton { text: "Share Dialog"; onClicked: nativeUtils.share("Sharing is caring!", "https://www.felgo.com"); verticalMargin: 0; flat: false }
}
}
}
SectionSpacer {}
SectionHeader { text: "Open Apps, Send E-Mail and More" }
SectionDescription { text: "Start other apps, open an url in the browser or let the user send emails. Take a photo from camera or pick an image from gallery." }
SectionContent { contentItem: Column {
width: parent.width
spacing: dp(16)
Row {
anchors.horizontalCenter: parent.horizontalCenter
CustomComboBox {
id: comboBox2
anchors.verticalCenter: parent.verticalCenter
implicitWidth: dp(120) + 30
model: ["Open App", "Open Url", "Send Mail"]
}
AppButton {
text: "Open"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
switch(comboBox2.currentIndex) {
case 0:
NativeDialog.confirm(
"Open Facebook",
"The Facebook App will be opened. Proceed?",
function(proceed) {
if(!proceed) return
var launchParam = Theme.isIos ? "fb://profile" : "com.facebook.katana"
if(!nativeUtils.openApp(launchParam)) {
NativeDialog.confirm("Facebook could not be launched.","", function(){}, false)
}
})
break
case 1:
NativeDialog.confirm(
"Open Url",
"The Felgo website will be opened. Proceed?",
function(proceed) {
if(!proceed) return
nativeUtils.openUrl("https://www.felgo.com")
})
break
case 2:
NativeDialog.confirm(
"Send Mail",
"The mail app will be opened with a new draft. Proceed?",
function(proceed) {
if(!proceed) return
nativeUtils.sendEmail("", "I just wanted to ...", "wish you a cool day! ;)")
})
break
}
}
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
CustomComboBox {
id: comboBox3
anchors.verticalCenter: parent.verticalCenter
implicitWidth: dp(120) + 30
model: [ "Store Contact", "Vibrate" ]
}
AppButton {
text: "Do It"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
switch(comboBox3.currentIndex) {
case 0:
var vCard = "BEGIN:VCARD
VERSION:2.1
N:Felgo;Office;;;
FN:Felgo
TEL;WORK:0123456789
EMAIL;WORK:help@felgo.com
ADR;WORK:;;Kolonitzgasse;Wien;;1030;Austria
ORG:Felgo
URL:https://www.felgo.com
END:VCARD";
var success = nativeUtils.storeContacts(vCard)
if(Theme.isIos) {
if(success)
NativeDialog.confirm("Contact stored.", "", function() {}, false)
else
NativeDialog.confirm("Could not store contact",
"The app does not have permission to access the AddressBook, please allow access in the device settings.",
function() {}, false)
}
else if (Theme.isAndroid) {
if(!success) {
NativeDialog.confirm("Could not store contact",
"Error when trying to store the vCard to the user documents folder.",
function() {}, false)
}
}
else {
NativeDialog.confirm("Could not store contact", "Storing contacts is only possible on iOS and Android.", function() {}, false)
}
break
case 1:
nativeUtils.vibrate(); break
}
}
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: dp(10)
AppText {
anchors.verticalCenter: parent.verticalCenter
text: "Statusbar visible:"
}
AppSwitch {
anchors.verticalCenter: parent.verticalCenter
checked: Theme.statusBarStyle === Theme.colors.statusBarStyleBlack
updateChecked: false
onToggled: Theme.colors.statusBarStyle = checked ? Theme.colors.statusBarStyleHidden : Theme.colors.statusBarStyleBlack
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
height: userImage.height
spacing: dp(10)
width: userImage.width + spacing + choosePhotoText.width
UserImage {
id: userImage
property string iconFontName: Theme.iconFont.name
width: dp(72)
height: width
placeholderImage: "\uf007"
source: ""
editable: true
editBackgroundColor: Theme.tintColor
property bool shownEditPhotoDialog: false
onEditClicked: {
if (system.desktopPlatform) {
nativeUtils.displayImagePicker(qsTr("Choose Image"))
}
else {
shownEditPhotoDialog = true
nativeUtils.displayAlertSheet("", ["Choose Photo", "Take Photo", "Reset Photo"], true)
}
}
Connections {
target: nativeUtils
onAlertSheetFinished: {
if (userImage.shownEditPhotoDialog) {
if (index == 0)
nativeUtils.displayImagePicker(qsTr("Choose Image"))
else if (index == 1)
nativeUtils.displayCameraPicker("Take Photo")
else if (index == 2)
userImage.source = ""
userImage.shownEditPhotoDialog = false
}
}
onImagePickerFinished: {
console.debug("Image picker finished with path:", path)
if(accepted)
userImage.source = Qt.resolvedUrl(path)
}
onCameraPickerFinished: {
console.debug("Camera picker finished with path:", path)
if(accepted)
userImage.source = Qt.resolvedUrl(path)
}
}
}
AppText {
id: choosePhotoText
width: dp(130)
text: "click to choose image or take photo"
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: dp(12)
wrapMode: Text.WordWrap
}
}
}
}
SectionSpacer { }
SectionHeader { text: "Device Info" }
SectionContent { contentItem: Row {
width: parent.width
Column {
id: info1
spacing: dp(5)
width: (parent.width - parent.spacing) * 0.5
AppText { text: "<b>OS Type:</b> "+system.osType; font.pixelSize: sp(11) }
AppText { text: "<b>OS Version:</b> "+system.osVersion; font.pixelSize: sp(11) }
AppText { text: "<b>Device Model:</b> "+nativeUtils.deviceModel(); font.pixelSize: sp(11) }
AppText { text: "<b>Device Online:</b> "+system.isOnline; font.pixelSize: sp(11) }
AppText { text: "<b>MAC Address:</b> "+system.macAddress; font.pixelSize: sp(11)
width: parent.width; wrapMode: Text.WrapAtWordBoundaryOrAnywhere }
AppText { text: "<b>Unique Device Id:</b> "+system.UDID; font.pixelSize: sp(11);
width: parent.width; wrapMode: Text.WrapAtWordBoundaryOrAnywhere }
}
Column {
id: info2
spacing: dp(5)
width: info1.width
AppText { text: "<b>Orientation:</b> "+(Theme.isPortrait ? "portrait" : "landscape"); font.pixelSize: sp(11) }
AppText { text: "<b>Statusbar Height:</b> "+Theme.statusBarHeight; font.pixelSize: sp(11) }
AppText { text: "<b>App Identifier:</b> "+system.appIdentifier; font.pixelSize: sp(11)
width: parent.width; wrapMode: Text.WrapAtWordBoundaryOrAnywhere }
AppText { text: "<b>App Version:</b> "+system.appVersionCode; font.pixelSize: sp(11) }
AppText { text: "<b>Qt Version:</b> "+system.qtVersion; font.pixelSize: sp(11) }
AppText { text: "<b>Felgo Version:</b> "+system.vplayVersion; font.pixelSize: sp(11) }
}
}
}
SectionSpacer {}
SectionHeader { icon: IconType.mapmarker; text: "Maps and Positioning" }
SectionDescription { text: "Embed maps with custom markers, routing and retrieve the user position." }
Item {
width: parent.width
height: map.height + positionRow.height
AppMap {
id: map
width: parent.width
height: dp(200)
showUserPosition: true
plugin: Plugin {
name: "mapbox"
parameters: [ PluginParameter {
name: "mapbox.mapping.map_id"
value: "mapbox.streets"
},
PluginParameter {
name: "mapbox.access_token"
value: "pk.eyJ1IjoiZ3R2cGxheSIsImEiOiJjaWZ0Y2pkM2cwMXZqdWVsenJhcGZ3ZDl5In0.6xMVtyc0CkYNYup76iMVNQ"
},
PluginParameter {
name: "mapbox.mapping.highdpi_tiles"
value: true
}]
}
center: userPositionAvailable ? userPosition.coordinate : QtPositioning.coordinate(48.208417, 16.372472)
zoomLevel: 13
Component.onCompleted: {
map.center = userPositionAvailable ? userPosition.coordinate : QtPositioning.coordinate(48.208417, 16.372472)
map.zoomLevel = 13
}
onUserPositionAvailableChanged: {
if(userPositionAvailable && userPosition.horizontalAccuracyValid && map.center !== userPosition.coordinate) {
zoomToUserPosition()
}
}
MapQuickItem {
coordinate: parent.userPositionAvailable ? parent.userPosition.coordinate : parent.center
anchorPoint: Qt.point(sourceItem.width/2, -dp(35))
z: 3
sourceItem: Rectangle {
width: dp(150)
height: dp(50)
color: "white"
radius: width * 0.05
border.color: Theme.tintColor
border.width: dp(1)
layer.effect: DropShadow { horizontalOffset: 2; verticalOffset: 2
radius: 16.0; samples: 16; color: "#80000000" }
layer.enabled: true
AppText {
text: "This is a custom marker!"
anchors.centerIn: parent
font.pixelSize: sp(10)
}
}
}
}
Row {
id: positionRow
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
spacing: dp(10)
width: posButton.width + spacing + posText.contentWidth
AppButton { id: posButton; text: "Zoom to Position"; onClicked: map.zoomToUserPosition() }
AppText {
id: posText
text: "<b>Lat:</b> "+(map.userPositionAvailable ? map.userPosition.coordinate.latitude.toFixed(8) : "Not available")
+ "<br/><b>Lng:</b> "+(map.userPositionAvailable ? map.userPosition.coordinate.longitude.toFixed(8) : "Not available")
font.pixelSize: sp(11)
anchors.verticalCenter: parent.verticalCenter
}
}
}
SectionSpacer {}
SectionHeader { icon: IconType.videocamera; text: "Multimedia Components" }
SectionDescription { text: "Show videos or play sound anywhere in your app. You can even play a video with alpha channel for advanced visual effects." }
Rectangle {
id: embeddedVideoContainer
width: parent.width
height: width * 0.75
color: componentsPage.alphaVideo !== null ? "white" : "black"
Component.onCompleted: playEmbeddedVideoTimer.start()
}
SectionContent { contentItem: Column {
width: parent.width
spacing: dp(10)
Row {
anchors.horizontalCenter: parent.horizontalCenter
CustomComboBox {
id: comboBox4
implicitWidth: dp(150) + 30
model: [ "Transparent Video", "Music", "Sound" ]
}
AppButton {
id: playButton
text: {
if((comboBox4.currentIndex === 1 && music.playbackState === Audio.PlayingState)
|| (comboBox4.currentIndex === 2 && sound.playing))
return "Stop"
else
return "Play"
}
onClicked: {
switch(comboBox4.currentIndex) {
case 0:
if(componentsPage.video !== null) {
componentsPage.video.pause()
componentsPage.video.visible = false
}
if(componentsPage.alphaVideo === null)
componentsPage.alphaVideo = alphaVideoComponent.createObject(componentsPage)
break
case 1:
if(playButton.text !== "Stop")
music.play()
else
music.stop()
break
case 2:
if(playButton.text !== "Stop")
sound.play()
else
sound.stop()
break
}
}
}
}
}
}
SectionSpacer { }
SectionHeader { text: "Convenience Controls" }
SectionDescription { text: "Use a PageControl and a SwipeView for horizontal scrolling of content." }
SectionSpacer { }
Item {
width: parent.width
height: imgSwipeView.height + pageControl.height
Quick2.SwipeView {
id: imgSwipeView
width: parent.width
height: width / 1.5
anchors.bottom: parent.bottom
Item { AppImage { source: "../../assets/devices.png"; anchors.fill: parent; fillMode: Image.PreserveAspectFit } }
Item { AppImage { source: "../../assets/onu.png"; anchors.fill: parent; fillMode: Image.PreserveAspectFit } }
Item { AppImage { source: "../../assets/squaby.png"; anchors.fill: parent; fillMode: Image.PreserveAspectFit } }
Item { AppImage { source: "../../assets/stack.png"; anchors.fill: parent; fillMode: Image.PreserveAspectFit } }
}
PageControl {
id: pageControl
height: implicitHeight + dp(5)
pages: 4
currentPage: imgSwipeView.currentIndex
clickableIndicator: true
spacing: dp(10)
onPageSelected: imgSwipeView.currentIndex = index
}
}
SectionDescription { text: "The PictureViewer shows an image in fullscreen." }
SectionContent { contentItem: Column {
spacing: dp(10)
anchors.horizontalCenter: parent.horizontalCenter
AppText { text: "click to open PictureViewer: "; font.pixelSize: dp(12); anchors.horizontalCenter: parent.horizontalCenter }
AppImage {
anchors.horizontalCenter: parent.horizontalCenter
width: parent.parent.width * 0.35
fillMode: AppImage.PreserveAspectFit
source: "../../assets/felgo-logo.png"
MouseArea {
anchors.fill: parent
onClicked: PictureViewer.show(getApplication(), parent.source)
}
}
}
}
Loader {
id: webViewLoader
width: parent.width
height: webViewLoader.item ? webViewLoader.item.implicitHeight : 0
source: system.desktopPlatform ? "" : Qt.resolvedUrl("../common/WebViewSection.qml")
}
}
Audio {
id: music
source: "../../assets/multimedia/coconut-land.wav"
loops: 0
autoLoad: true
}
SoundEffect {
id: sound
source: "../../assets/multimedia/collected.wav"
loops: 0
}
Component {
id: videoComponent
Video {
anchors.centerIn: parent
anchors.fill: parent
source: isWindows ? "../../assets/multimedia/FelgoAnimation.wmv" : "../../assets/multimedia/FelgoAnimation.mp4"
autoPlay: false
property bool allowPlayback: !flickable.dragging && !flickable.flicking && inVisibleArea
property bool inVisibleArea: (yDelta + flickable.height >= videoContentY) && (yDelta <= videoContentY + height)
onAllowPlaybackChanged: if(allowPlayback) playTimer.start(); else playTimer.stop()
visible: playbackState === MediaPlayer.PlayingState
Timer {
id: playTimer
interval: 250
onTriggered: parent.play()
}
Component.onCompleted: {
videoContentY = embeddedVideoContainer.mapToItem(flickable.contentItem, 0 , 0).y
if(!allowPlayback) stop(); else playTimer.start();
stopped.connect(function() { if(allowPlayback) playTimer.start() })
}
}
}
Timer {
id: playEmbeddedVideoTimer
interval: 2000
onTriggered: componentsPage.video = videoComponent.createObject(embeddedVideoContainer)
}
Component {
id: alphaVideoComponent
Rectangle {
id: alphaVideoItem
anchors.fill: parent
color: Qt.rgba(0,0,0,0.5)
AlphaVideo {
anchors.centerIn: parent
anchors.fill: parent
source: isWindows ? "../../assets/multimedia/AlphaAnimation.wmv" : "../../assets/multimedia/AlphaAnimation.mp4"
autoPlay: true
property bool destroyWhenVisible: false
onStatusChanged: {
if(status === MediaPlayer.EndOfMedia) {
if(!visible)
destroyWhenVisible = true
else
destroyAlphaVideo()
}
}
onVisibleChanged: if(visible && destroyWhenVisible) destroyAlphaVideo()
function destroyAlphaVideo() {
alphaVideoItem.destroy()
componentsPage.alphaVideo = null
if(componentsPage.video !== null) {
componentsPage.video.destroy()
playEmbeddedVideoTimer.start()
}
}
}