I have one navigation pane and I disabled the back button in the main.qml, however I want to display the back button again to some part of the app. How do i implement this? here's my cpp
pane = qml->createRootObject<NavigationPane>();
// Set created root object as the application scene
app->setScene(pane);
qml->setContextProperty("cppObj", this);
}
void ApplicationUI::onLoginClicked() {
// create scene document from buttonclicked.qml asset
// set parent to created document to ensure it exists for the whole application lifetime
QmlDocument *qml = QmlDocument::create("asset:///projects.qml").parent(this);
qml->setContextProperty("cppObj", this);
Page* root = qml->createRootObject<Page>();
pane->push(root);
}
void ApplicationUI::onAddClicked() {
// create scene document from buttonclicked.qml asset
// set parent to created document to ensure it exists for the whole application lifetime
QmlDocument *qml = QmlDocument::create("asset:///addprojects.qml").parent(this);
Page* root = qml->createRootObject<Page>();
pane->push(root);
}
and here's my main where I disabled the back button
NavigationPane {
backButtonsVisible: false }
How do i make the back button active to some section of the app?
Let's say you have the following code:
StartPage
import bb.cascades 1.0
NavigationPane {
id: navPane
Page {
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
layout: DockLayout {
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "Next Page"
onClicked: {
var nextPage = page.createObject();
navPane.push(nextPage);
}
}
}
}
attachedObjects: [
ComponentDefinition {
id: page
source: "NextPage.qml"
}
]
}
NextPage:
import bb.cascades 1.0
Page {
// disables backButton (not peeking)
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
enabled: false
}
}
// [0]
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
layout: DockLayout {
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "Prev page"
onClicked: {
var page = navPane.pop();
if (page)
page.destroy();
}
}
} // [0]
// hides navigation bar
onCreationCompleted: {
navPane.backButtonsVisible = false;
}
}
Then you can completely disable backButton with this code in NextPage.qml
onCreationCompleted: {
navPane.backButtonsVisible = false;
}
Alternatively, if you want to have navigation pane visible but just Back Button disabled, use following in the same NextPage.qml file:
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
enabled: false
}
}
Also, don't forget about peekEnabled property. Using peek BB10 feature user can move between sibling pages using sliding finger movement not touching any buttons on the screen.
It could be done in similar way:
onCreationCompleted: {
navPane.peekEnabled = false;
}
Hope it helps.
Related
I have several QML files, main one is the one that opens the ApplicationWindow when ever I try to get back from child QML to main, new window is opened again!
how can I prevent of doing this?
I thought that enabling a flag in child QML, but there may be other ways!
I tried stackview in QML
Is there any way to prevent from opening new page when I get back to main QML?
create a loader in main window and call every page into that loader when ever you need to change page just change loader's source
Window {
Loader{
id:myLoader
anchors.fill: parent
source: "LoginPage.qml"
}
Connections{
target: myLoader.item
onBack_clicked:{
loginid = ""
myLoader.source = "LoginPage.qml"
}
onSetting_clicked:{
myLoader.source = "Setting.qml"
}
}
}
and for child qml files : (for me Setting.qml)
Item {
signal back_clicked()
Button {
id: button1
anchors.right: parent.right
anchors.rightMargin: 15
onClicked: {
back_clicked()
}
}
}
but if you want to not destroy old page use SwipeView Or StackView:
SwipeView {
id: swipeView
clip: true
currentIndex: 0
Item{
id:firstPage
clip:true
//your page
}
Item{
id:secondPage
clip:true
//your page
}
}
and to change pages just change currentIndex
swipeView.currentIndex = 1
UPDATE:
StackView {
id: stackView
initialItem: one
}
Component {
id: one
Item{//your first page }
}
Component {
id: two
Item{//your second page }
}
and to push your pages :
stackView.push({ item: two})
//or
stackView.push("MySecondView.qml")
to get back to old or main page just pop it :
stackView.pop()
I have a tree model derived from a QAbstractItemModel. And I can display the data in a tree like way.
What I want is to display teh data by the layers. To display only one level of a layer at a time AND put each layer on a stack and navigate backwards by poping the layer from the stack.
I guess I have to implement a custom delegate? Any advice would be highly appreciated. Thank you.
I recently implemented something similar, based on a QFileSystemModel, set as a qml contextProperty, named treeModel in the example below.
The idea was to keep track of the current QModelIndex, and to use the data() & rowCount() functions of the QAbstractItemModel to get the actual model data, and to use a recursive stack view for the navigation
General layout
ApplicationWindow {
id: main
visible: true
width: 640
height: 480
ColumnLayout
{
anchors.fill: parent
// Breadcrumb
SEE BELOW
// View
StackView
{
id: stackView
Layout.fillHeight: true
Layout.fillWidth: true
initialItem: TreeSlide {}
}
}
}
TreeSlide
The view itself is pretty simple. I didn't used anything fancy here, and it displays only one role, but you could extend it without trouble. Note that the view's model is NOT your treeModel, but instead just the rowCount for the rootIndex.
ListView
{
Layout.fillHeight: true
Layout.fillWidth: true
model: treeModel.rowCount(rootIndex)
clip: true
snapMode: ListView.SnapToItem
property var rootIndex
// I used a QFileSytemModel in my example, so I had to manually
// fetch data when the rootIndex changed. You may not need this though.
onRootIndexChanged: {
if(treeModel.canFetchMore(rootIndex))
treeModel.fetchMore(rootIndex)
}
Connections {
target: treeModel
onRowsInserted: {
rootIndexChanged()
}
}
delegate: ItemDelegate {
property var modelIndex: treeModel.index(index,0, rootIndex)
property bool hasChildren: treeModel.hasChildren(modelIndex)
width: parent.width
text: treeModel.data(modelIndex)
onClicked: {
if(hasChildren)
{
// Recursively add another TreeSlide, with a new rootIndex
stackView.push("TreeSlide.qml", {rootIndex: modelIndex})
}
}
}
}
Breadcrumb
To navigate the model, instead of a simple back button, I used a kind of dynamic breadcrumb
// Breadcrumb
RowLayout
{
Repeater
{
id: repeat
model: {
var res = []
var temp = stackView.currentItem.rootIndex
while(treeModel.data(temp) != undefined)
{
res.unshift(treeModel.data(temp))
temp = temp.parent
}
res.unshift('.')
return res
}
ItemDelegate
{
text : modelData
onClicked: {
goUp(repeat.count - index-1)
}
}
}
}
the goUp function simply goes up the stack by poping items
function goUp(n)
{
for(var i=0; i<n; i++)
stackView.pop()
}
To to do it completely by guides we should use DelegateModel and DelegateModel.rootIndex
DelegateModel {
id: delegateSupportPropConfigModel
model: supportModel
delegate: SupportPropConfigListItem {
id: currentItem
width: scrollRect2.width - 60
fieldName: model.fieldName
fieldValue: model.value
onClick:{
delegateSupportPropConfigModel.rootIndex = supportPropConfigModel.index(0, 0, supportPropConfigModel)
}
}
}
Column {
id: columnSettings
spacing: 2
Repeater {
model: delegateSupportPropConfigModel
}
}
I am trying to delete QML object and recreate object like this:
Rectangle{
property var obj
signal videoStopped(variant complete)
function recreate(url){
if(!obj){
console.log("createObject")
obj = videoComponet.createObject(root)
obj.stopped.connect(function(){
videoStopped(obj.status == MediaPlayer.EndOfMedia)
})
}
obj.source = url
obj.play()
}
function stop(){
obj.destroy() // obj.deleteLater()
}
Component{
id: videoComponet
Video {
anchors.fill: parent
visible: true
autoPlay: true; autoLoad: true
}
}
}
C++ side call recreate to generate an object and call stop to delete it.
recreate ⇒ console output createObject
↓
stop
↓
recreate ⇒ console no output
Both obj.destroy() and obj.deleteLater() not worked.
How to forcedly delete the dynamically created object just like delete in C++.
A minor change to S.M.Mousavi's answer:
I observed that setting sourceComponent to undefined does not work. I have to set it to null to unload the component. Another option is to set the source to empty string.
onClicked: {
loader.sourceComponent = null; //causes destroying loaded component
}
OR
onClicked: {
loader.source = ""; //causes destroying loaded component
}
Use Loader instead.
It loads an Item dynamically and you can destroy it by setting sourceComponent property to undefined:
Loader {
id: loader
}
Component {
id: myDynComp
Rectangle {
width: 40; height: 40
anchors.centerIn: parent
}
}
Row {
Button {
width: 120; height: 40
text: "Load"
onClicked: {
loader.sourceComponent = myDynComp; //load/create component dynamically
}
}
Button {
width: 120; height: 40
text: "UnLoad"
onClicked: {
loader.sourceComponent = undefined; //causes destoying loaded component
}
}
}
I am developing a Qt app on Win/Android. My question is very simple.
When my app starts, first a login page welcomes you.
If you want to configure server settings, ServerInfo.qml is opened in a loader. The login page and ServerInfo are loaded in the same loader.
My problem is that when I close ServerInfo.qml, then load loginpage.qml to loader, the loader creates a new instance of loginpage.qml. I don't want the page to be created again.
Here is my Qml code :
ApplicationWindow {
id:mainwindow
visible: true
width: 600
height: 800
x: Screen.width / 2 - width / 2
y: Screen.height / 2 - height / 2
menuBar:MenuBar{
Menu {
title:"Edit"
MenuItem {
text:"Sunucu Ayarları"
onTriggered: {
loader.source="ServerConfig.qml"
loader.anchors.centerIn=main
}
}
MenuItem {
text:"Çıkış"
onTriggered: {
Qt.quit();
}
}
}
}
Connections {
ignoreUnknownSignals: true
target: process
onProcessstart: {
busyrec.visible=true;
busyloader.item.busytext=text;
busyloader.item.busyrunnig=true;
}
onProcessstop: {
busyloader.item.busytext=text;
busyloader.item.busyrunnig=false;
busyloader.item.busytextcolor="blue"
}
Component.onCompleted: {
// process.onSuccesLogin();
//TaskResultm.taskresult.Malzemeler.push
console.log(TaskResultm.taskresult.serilaze());
}
}
Column {
anchors.fill: parent
Rectangle {
id:busyrec
width: parent.width
height: (parent.height/10)
visible:true
color:"green"
Loader {
id:busyloader
source:"BusyIndicator.qml"
anchors.fill: parent
}
Connections {
ignoreUnknownSignals: true
}
}
Rectangle {
id: main
// anchors.fill: parent
width: parent.width
height: (parent.height/10)*9
Loader {
id:loader
source: "LoginPage.qml"
anchors.centerIn:parent
focus:true
property bool valid: item !== null
}
Connections {
ignoreUnknownSignals: true
target: loader.valid? loader.item : null
onDirecttomainpage:{
// process.getWorkOrderList();
busyloader.item.switenabled=true;
busyloader.item.switopacity=1;
loader.anchors.fill=main;
loader.source="TaskNavigationMainScreen.qml";
}
onServerinfopageclose: {
loader.source="LoginPage.qml";
loader.anchors.centerIn=main;
}
}
}
}
onClosing: {
if(Qt.platform.os=="android") {
if(loader.item!==null)
{
if(loader.item.objectName==="tasknavigationmain")
if(loader.item.zemin===0)
close.accepted=true;
else
close.accepted=false;
}
}
else if (Qt.platform.os=="windows")
{
Qt.quit();
//if(loader.item!==null)
// if(loader.item.objectName==="tasknavigationmain")
// console.log(loader.item.stackViewItem.depth);
}
}
}
Just use a StackView instead of a Loader, it will keep previous "forms" alive as you push new ones on top, and you can always go back and forth.
A loader will load a single element, if you load another, the old one will be destroyed, there is no way around that.
I'm right now creating my app only in C++, i creating the NavigationPane and adding the container with the Views i need. It works fine, but i want to capture a Button clicked to make the NavigationPane pop the current page and push a diferent (made in runtime) Page.
How can it be achieved, i tried working with the signals, but i think i'm not getting how it works the signals and the QT_SLOTS, in the case of the NavigationPane, it doesn't have those methods as QT_SLOT.
Any advice will be appreciated.
You first need to connect the clicked() signal of your Button to the pop() slot of your NavigationPane. It should look like this:
// Connect the button's clicked() signal to the navigation pane's
// pop() slot.
bool connectResult = QObject::connect(myButton,
SIGNAL(clicked()),
myPane,
SLOT(pop()));
// Use the Q_ASSERT() function to test the return value and
// generate a warning message if the signal slot connection
// wasn’t successful.
Q_ASSERT(connectResult);
// Indicate that the variable connectResult isn't used in the
// rest of the app to prevent a compiler warning.
Q_UNUSED(connectResult);
This page about buttons might help you understand how to handle this. To better understand how to connect objects together, you might also want to have a look at a the signals and slots documentation.
You then have to create and push your new page after the pop. To do that, you simply have to connect the popTransitionEnded (bb::cascades::Page *page) slot of your NavigationPane to your custom function that will do the job.
bool connectResult = QObject::connect(myPane,
SIGNAL(popTransitionEnded(bb::cascades::Page*)),
this,
SLOT(createNewPageAndPushIt(bb::cascades::Page*)));
Q_ASSERT(connectResult);
Q_UNUSED(connectResult);
See this page for more details about the usage of NavigationPane to stack pages.
---------------------TRY THIS-------------
Get sample app from my github samples for your query....
https://github.com/svmrajesh/BB-10-Cascades/tree/master/MY%20APPS/stackNavigation
main.qml: (first page)
import bb.cascades 1.0
NavigationPane {
id: navigationPane
backButtonsVisible: false
peekEnabled: false
Page
{
id: rootPage
Container {
background: Color.LightGray
layout: DockLayout {
}
Label {
text: "First page"
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
}
}
actions: [
ActionItem {
title: "Next page"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
var page = pageDefinition.createObject();
navigationPane.push(page);
}
attachedObjects: ComponentDefinition {
id: pageDefinition
source: "PageTwo.qml"
}
}
]
}
onPopTransitionEnded: {
page.destroy();
}
}
2.second page
import bb.cascades 1.0
Page {
id: pageTwo
Container {
background: Color.Gray
layout: DockLayout {
}
Label {
text: "Second page"
horizontalAlignment: HorizontalAlignment.Center
}
Container {
layout: StackLayout {
}
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
Button {
text: qsTr("Next Page")
imageSource: "asset:///images/picture1thumb.png"
onClicked: {
// show detail page when the button is clicked
var page = getSecondPage();
console.debug("pushing detail " + page)
navigationPane.push(page);
}
property Page secondPage
function getSecondPage() {
if (! secondPage) {
secondPage = secondPageDefinition.createObject();
}
return secondPage;
}
attachedObjects: [
ComponentDefinition {
id: secondPageDefinition
source: "PageTwoOne.qml"
}
]
}
Button {
text: "Previous Page"
onClicked: {
navigationPane.pop();
}
}
}
}
/* ------------- Use this Code If back button visibility is "True"-----------------
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
title: "Back"
// imageSource: "asset:///back.png"
onTriggered: {
navigationPane.pop();
}
}
} */
}
3.last page
import bb.cascades 1.0
Page {
id: pageTwoone
Container {
background: Color.DarkGray
layout: DockLayout {}
Label {
horizontalAlignment: HorizontalAlignment.Center
text: "Last Page"
}
Container {
layout: StackLayout {}
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: qsTr("Goto Home Page")
onClicked: {
// show detail page when the button is clicked
navigationPane.navigateTo(rootPage);
}
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: qsTr("Goto Back")
onClicked: {
// show detail page when the button is clicked
navigationPane.pop();
}
}
}
}
}
------------ ADD More pages to navigate using this code----------------------------
-------------copy this code and run.. get sample app from above link if needed ------
Have you checked this?
https://developer.blackberry.com/native/reference/cascades/bb__cascades__navigationpane.html
NavigationPane* navigationPane; // Global var to change current Page with push/pop
void initializeNavigationPane()
{
ActionItem* nextAction = ActionItem::create()
.title("Next page")
.onTriggered(this, SLOT(pushPage()));
navigationPane = NavigationPane::create();
QObject::connect(navigationPane, SIGNAL(popTransitionEnded(bb::cascades::Page*)),
this, SLOT(popFinished(bb::cascades::Page*)));
// Put a new page
navigationPane->push(Page::create()
.content(Label::create("First page"))
.addAction(nextAction, ActionBarPlacement::OnBar));
Application::instance()->setScene(navigationPane);
}
void popFinished(bb::cascades::Page* page){
delete page;
}
//You have to connect this method when you want a new Page pushed.
Q_SLOT void pushPage(){
ActionItem* backAction = ActionItem::create()
.title("Previous page")
.imageSource(QUrl("asset:///back.png"))
.onTriggered(navigationPane, SLOT(pop()));
navigationPane->push(Page::create()
.content(Label::create("Second page"))
.paneProperties(NavigationPaneProperties::create()
.backButton(backAction)));
}
Explication:
An instance of the object NavigationPane allows change the current page to others with the push/pop effect (see image):
developer.blackberry.com/native/files/reference/cascades/images/navigation_pane_push_pop.png
You have to inicialice with:
navigationPane = NavigationPane::create();
And tell to the Application you will use this instance to change page:
Application::instance()->setScene(navigationPane);
Now you app got a NavigationPane, but nothing is inside, if you run it, you will get a black screen, to add a page (the principal page - page0) use push:
navigationPane->push(Page::create()
.content(Label::create("First page")));
To add a new Page that It can go back to the page0 we just push use Push again, Remeber include the back button to go back:
navigationPane->push(Page::create()
.content(Label::create("Second page"))
.paneProperties(NavigationPaneProperties::create()
.backButton(ActionItem::create()
.title("Previous page")
.imageSource(QUrl("asset:///back.png")) //You should add manually this image.
.onTriggered(navigationPane, SLOT(pop()))));
Q_INVOKABLE void insert (intindex, bb::cascades::Page *page )
https://developer.blackberry.com/native/reference/cascades/bb__cascades__NavigationPane.html#function-insert-index-page
Inserts a page at a specified index in the NavigationPane.
The page that is passed must not be 0 or it will be ignored. If the
page is already present in the navigation stack, the operation will
fail. This operation will not trigger a transition effect, even if the
page is added to the top of the stack. If a transition effect is
desired, use push() instead. The topChanged() signal will be emitted
if the operation affects the top node.
Parameters
1- index
The index where the page will be placed. If the index < 0 the the page is inserted in the bottom. If the index > the number of pages in the navigation stack, it is added on top of the stack.
2- page
The page to be inserted, must not be 0.
Since: BlackBerry 10.0.0
An idea is
You could use:
navigationPane.count() To get the current pages in the nagationPane stack, and use:
navigationPane.insert(navigationPane.count()-1, myPageToBeBack); To push a page between the current page and
the previous one