diff options
Diffstat (limited to 'doc/src/tutorials')
-rw-r--r-- | doc/src/tutorials/declarative.qdoc | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/doc/src/tutorials/declarative.qdoc b/doc/src/tutorials/declarative.qdoc new file mode 100644 index 0000000..3fad672 --- /dev/null +++ b/doc/src/tutorials/declarative.qdoc @@ -0,0 +1,674 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page tutorials-declarative-contacts.html + \startpage {index.html}{Qt Reference Documentation} + \nextpage {tutorials/declarative/contacts/part1}{Chapter 1} + + \title Declarative UI Tutorial + \ingroup howto + \ingroup tutorials + \brief An introduction to using Qt Declarative UI to put together a + simple animated application. + + \omit + At the time of writing the tutorial Declarative UI was still under + development. It is extremely likely that an update will be required + prior to 4.6 release. + \endomit + + This tutorial gives an introduction to using the Qt Declarative UI + animation framework. + + In this process we will learn about some of the basics of using + Declarative UI, such as + + \list + \o Basic drawing + \o States and Transitions + \o Reuse of components + \o Models and Views + \endlist + + An existing knowledge of Qt is not required. + + The tutorial's source code is located in Qt's + \c examples/declarative/tutorials/contacts directory. + It is split up into a number of sub directories, and within each + sub directory the files are numbered in an order of increasing features. + + The code in this example is not compiled, but interpreted at run time. + This means you should use the qmlviewer application provided with + Qt to run the examples. + + \list + \o \l{tutorials/declarative/contacts/part1}{Drawing and Animation} + \o \l{tutorials/declarative/contacts/part2}{Reusing QML Components} + \o \l{tutorials/declarative/contacts/part3}{Models, Views and Delegates} + \endlist +*/ + +/*! + \page tutorials-declarative-contacts-part1.html + \contentspage {Declarative UI Tutorial}{Contents} + \nextpage {tutorials/declarative/contacts/part2}{Chapter 2} + \example tutorials/declarative/contacts/part1 + \title Drawing and Animation + \tableofcontents + + The first part of this tutorial covers basic drawing of elements on the + screen and causing them to animate. + + \section1 Drawing + + In this first chapter we will build a button that indicates something + can be removed and asks for confirmation. When clicked it will expand + from a small button with a trash can icon, to a wide button with a + confirm icon on the left, the text "Remove" in the middle, and a + cancel icon on the right. + + \image declarative-removebutton.gif + + Because Declarative UI is declarative, you don't pass instructions on + what to paint in a sequential manner as you may be used to. Instead + elements and how they appear on the screen are declared in much the + same was as elements on a web page are declared. This is done using + the Qt Markup Language which we will refer to by the abbreviation QML + for the remainder of the tutorial. + + We will start by drawing a simple red rectangle with rounded corners. + + \image declarative-roundrect.png + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/1/RemoveButton.qml 0 + + This is one of the simplest of QML components. It describes a rectangle with + some simple properties. In QML all components start with a capital + letter, and their properties with lower case letters. + + Apart from the properties all QML components share, the \l{Rect}{Rect} component has the properties + + \list + \o color - The background color of the rectangle + \o tintColor - The overlay color of the rectangle + \o gradientColor - The color at the base of the rectangle to blend upwards + \o pen - The description of how to draw the border of the rectangle + \o radius - The corner radius used to draw rounded rectangles. + \endlist + + There are also a number of properties all QML components shares, described + in the \l{Item}{Item} element reference documentation. The rectangle drawn in the + above code uses the properties; + + \list + \o id - An identifier of the component + \o width - the width of the component when drawn + \o height - the height of the component when drawn + \endlist + + Currently we have described a rectangle with a width and height of 30 pixels, filled in with + the color red and with rounded corners using a radius of 5. + + \section1 Layout + + The next step of the tutorial adds an image over the rectangle. We + will do this by adding an \l{Image}{Image} component as a child of the + \l{Rect}{Rect} component. All QML components have a list of children which + are drawn in order after the parent component has been drawn. + By having the \l{Image}{Image} + component as a child of the \l{Rect}{Rect} component we ensure it is drawn + over the \l{Rect}{Rect} component. Children also are affected by the opacity + of the parent component and calculate their position in within the bounds of + the parent component. + + \image declarative-removebutton-close.png + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/2/RemoveButton.qml 0 + + The trashIcon image is added as a child of the Rectangle. In this case + the children property isn't explicitly used because the default property + of the \l{Rect}{Rect} component is its children. Some elements often don't have children + and use some other default component. When this is the case its possible + to explicitly list the sub component as a child as follows: + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/2a/RemoveButton.qml 0 + + The \l{Image}{Image} element allows loading an image file for display. The source + specified is a URL, and in this case refers to a portable network graphics + file in a relative directory to where the QML file was loaded from. + + Also new in this code is the use of anchors. In QML components can either + have their position and size specified explicitly using x, y, width + and height, or they can instead specify the size and position in relation + to either parent or sibling elements. The \l{Image}{Image} component uses + a combination of both styles. It has a fixed size, but specifies its + position to align to the right of its parent and for its vertical center + to align with the vertical center of its parent. Setting a property + by the identifier of a separate property binds them. This means + that if while running the example the position of the \l{Rect}{Rect} component's + vertical center changed, so to would the vertical center of + the \l{Image}{Image} component. + + The parent value is a special identifier that always refers to the + parent component of a component. + + Anchors are most useful when the size of items might change based on + the component state or contents. However they are limited in that they + must always refer to a parent or sibling component. See + \l{anchor-layout}{Anchor-based Layout} for more information on using + anchors in QML. + + At this point the initial state of the RemoveButton is complete. A small + rounded rectangle with a trash icon. Next we will design the open + state for the button. + + \image declarative-removebutton-open.png + + This is a wider rectangle with two images and some text. The code to + draw this state of the button could be written as follows: + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/3/RemoveButton.qml 0 + + The rectangle width is now wider by 200 pixels. Also the trashIcon has + been replaced. Normally we wouldn't + remove the trashIcon when developing an alternate state of the RemoveButton, + however since this is a tutorial its been done so that its easier to + understand the alternate state we are aiming for and how it relates to + transitioning between states. + + We also introduce the \l{Text}{Text} element. + Left and Right anchors are specified in terms of the neighboring icons + because we want text to fill the space between the icons. This + means as the parent removeButton gets wider, so will the text component. + It also means that if we animate a width change on the removeButton, + any bindings, that is the values specified by an expression such as + \c{parent.left} will be evaluated and animated as well. + + \section1 Defining States + + When designing a component with multiple states, it should be developed + in the initial state and the changes that would be made specified + as an additional state. Its not normally possible to add new children + to an element when changing state + This means that all possible child components should be included + in the initial state, and those that should not be visible in the initial + state should have their opacity set to zero. Thus + for the RemoveButton we specify the starting size of the RemoveButton + and hide any items that should not initially be visible. + + The code snippet below shows what the start of the duel state specification + might look like. + + \code + Rect { + id: removeButton + width: 30 + height: 30 + color: "red" + radius: 5 + Image { + id: trashIcon + width: 22 + height: 22 + anchors.right: parent.right + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + src: "../../shared/pics/trash.png" + opacity: 1 + } + Image { + id: cancelIcon + width: 22 + height: 22 + anchors.right: parent.right + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + src: "../../shared/pics/cancel.png" + opacity: 0 + } + \endcode + + The code above includes components from both states of the RemoveButton, + but by setting opacity="0" for the cancelIcon it means that the + components of the second state won't be drawn yet. + The base state of a component always has an empty name, however new + states can be added that describe how a component and its children + should be changed. For the RemoveButton there is only one non-base state + required. In this tutorial we will name it the 'opened' state. + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/4/RemoveButton.qml states + + In the opened state the width of the button itself changes from the base + width of 30 to the new width of 230. Also the opacity of the children + are changed so that the trash icon is now hidden and the other elements + are now visible. + + \section1 Changing States + + To trigger the change we will react to the 'clicked' signal of a + MouseRegion component. + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/4/RemoveButton.qml mouse region + + MouseRegion components handle mouse actions within their geometry. This + geometry behaves the same way as painted components, such that children + cover their parents and later siblings will cover earlier siblings and + all the children of the earlier sibling, should they overlap. + + When a component has a signal, such as clicked, the action for the signal + can be specified using \c{onSignalName}, as is done above. In this + case when the clicked signal is emitted by the MouseRegion component, + a function called \c{toggle()} is called. It might also have been written + + \code + onClicked: { removeButton.state='opened' } + \endcode + + However in this case we are using a function because it allows multiple + mouse regions to use the same functionality, and also makes it + easier to specify complex behavior in response to a signal. + + An alternative would be to explicitly state the connection: + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/4a/RemoveButton.qml mouse region + + This will connect to the \c{clicked()} signal of the trashMouseRegion component + and execute the associated script. + + The \c{toggle()} function is a new function specified as part of the remove + button element. + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/4/RemoveButton.qml script + + Any QML component can have a set of resources specified. One of those + resources is any Script that might be needed. See the + \l{QtScript Module} for more information on how to write + script code in Qt. + + It is possible to refer to identified QML components + within the script. Hence the function for our RemoveButton will check + if the state is already open to determine what the new state should be. + + \section1 Animation + + Currently the RemoveButton is functional, but snaps between our two states. + Fortunately making the transition between states smooth is very simple. + We only need one more bit of code at the end of our removeButton component. + + \snippet declarative/tutorials/contacts/1_Drawing_and_Animation/5/RemoveButton.qml transition + + All QML components have a transitions property. This describes how + properties of items within the component should change. In this case + we specify that if the x, width or opacity of the removeButton or its + children change due to a change in state, that they should take 200ms + to complete their transition. + + \omit + TODO More on types of animation, e.g. ColorAnimation, Behaviors. + \endomit + + In the next chapter we will show how we can use the remove button in + other QML components. +*/ + +/*! + \page tutorials-declarative-contacts-part2.html + \contentspage {Declarative UI Tutorial}{Contents} + \previouspage {tutorials/declarative/contacts/part1}{Chapter 1} + \nextpage {tutorials/declarative/contacts/part3}{Chapter 3} + \example tutorials/declarative/contacts/part2 + \title Reusing QML Components + \tableofcontents + + The second part of this tutorial covers how to reuse QML components and + have them interact with each other. The RemoveButton developed in the + previous chapter is intended to be part of a more complex control for + editing a field of our contact. This ContactField in turn is intended + to be used in a contact editing control. + + \image declarative-reuse-3.png + + \section1 Loading QML Components + + Reusing the RemoveButton itself is very simple. When parsing a QML file + if a Component is referred to that isn't already in the system, Qt + will try to load it from a file of the same name with the ".qml" extension. + + \snippet declarative/tutorials/contacts/2_Reuse/1/ContactField.qml load + + The above QML code will attempt to load the RemoveButton component from + a file with the name "RemoveButton.qml" from the following search paths. + + \list + \o Any imported directories. These are listed at the start of the file using + \c { import "path" }. + \o the directory of the QML code file + \endlist + + All the properties of the button are + accessible and can be overridden from defaults. The loaded component + can also refer to elements further up in the tree, so that code within + RemoveButton.qml could refer to the contactField component. + Only properties of the top level element in RemoveButton.qml are visible to + the contact field. + + There are also two other ways to reuse components in QML. A component + can be reused from within the same QML file using Component and ComponentInstance + elements. The next code snippet produces three red rounded rectangles + within a large blue rectangle. + + \image declarative-reuse-bluerect.png + + \snippet declarative/tutorials/contacts/2_Reuse/1b/BlueRect.qml all + + This can be useful when the component is not complex enough to justify its + own file. The third way to reuse components allows for delaying loading + of the QML until some later event. Each \l{Item}{Item} includes + a special child, qmlItem, which has its definition provided by the + contents of the qml property of its parent. + + \snippet declarative/tutorials/contacts/2_Reuse/1a/ContactField.qml load + + This last method is useful if the contents of a item need to change at + run time or if the initial complexity of the loaded QML needs to be + reduced in order to improve the time it takes to start the application. In + chapter three this method is used to improve performance of + scrolling through very large numbers of items. + + Because of its simplicity, the first method is the recommended in most + cases and will be the focus of the remainder of this chapter. + + \section1 Properties and Signals + + The next task is to be able to control aspects of the RemoveButton from + the components that use it. In particular controlling how far it + expands and how it reacts when the user clicks on the confirm icon + of the remove button. When reusing a component in a separate QML file + only the attributes of the root element are visible. To allow controlling + attributes of child elements within an imported component we need to define + some properties and signals for the RemoveButton. + + \snippet declarative/tutorials/contacts/2_Reuse/2/RemoveButton.qml define properties and signals + + The children of the remove button can use these properties and signals. The + opened state can now bind the expanded width to the expandedWidth property. + + \snippet declarative/tutorials/contacts/2_Reuse/2/RemoveButton.qml use width + + Also when the confirm icon is clicked, as well as toggling the state it will + emit the confirmed signal of the RemoveButton component. + + \snippet declarative/tutorials/contacts/2_Reuse/2/RemoveButton.qml use signal + + These properties and signals can also be accessed from the contact field the same + way standard system component properties and signals are accessed. + + \snippet declarative/tutorials/contacts/2_Reuse/2/ContactField.qml use properties and signals + + Now when the remove button is expanded, it will expand to the width of the + contact field. Also when the user confirms the remove action, the + text section of the contact field will be cleared. + + \section1 States + + Its also possible to access the state of included components. The FieldText + component we will use in this tutorial is also been written specifically + for our contacts application. In + this case we want it to expand when editing. One way to do this would + be to anchor the field text component to the center of its parent and + then let its own width change push the remove button away, however that + would make it difficult to have the remove button also push the field + text to the left when the remove button expands. Instead we will anchor + the right edge of the field text to the left edge of the remove button + and use a state change in the contact field itself to move the + remove button and the field icon out of + view. + + \snippet declarative/tutorials/contacts/2_Reuse/3/ContactField.qml all + + Apart from accessing the fieldText.state, the above code also uses the when + attribute of its own editingText state. This is an alternative to using + a signal to change state. When the value of the expression for the + when attribute changes, Qt will detect if the contactField needs to enter + that state. In the FieldText element a similar approach is used to fade + out the label of the FieldText when the user enters some text of their own. + + \snippet declarative/tutorials/contacts/2_Reuse/3/FieldText.qml behavior + + \c{fieldText} is the enclosing component and \c{textEdit} is a TextEdit element + provided by Qt. In the QML code above, the opacity of the textLabel is + only 1 if the text for the textEdit is empty. This is a form of + short cut to using states for an element, useful if only one property + is changing as it is for the textLabel. To animate a property change is + similar to animating a state change. Using the Behavior element we can + specify how the property changes if it does change state, allowing for + a smooth transition. + + \section1 Key and Mouse Focus + + Setting focus to true on a component does not always mean + that the component has focus. This is due to the declarative nature + of QML, and can be affected by multiple components both indicating + focus to be true. At the time of writing this tutorial both key and mouse + focus handling are still being improved. Hence we will only lightly cover + the topic. + + For an item to have key focus in QML it is required that: + + \list + \o If there is a FocusRealm ancestor of the component that it has focus as well. + \o That it is the most recent component within the focus realms descendent's + to receive focus + \endlist + + The read-only property activeFocus can be used to determine whether a + component will receive key input. Any un-handled keys will be passed to + the components parent, which in turn will pass keys it doesn't handle up to its + own ancestors. + + Some components such as ListView components are also FocusRealm components, as they + handle focus among the child list items. + + At this stage of the tutorial it is sufficient to use the setting of 'focus' + as we only have a list of line edits and only one should be active at any given time. + + Currently if multiple contact fields were put into our contact editor, + any of the FieldText components could be clicked and opened, and + any of the RemoveButton components could be clicked and opened, all + at the same time. This leads to situations where the users actions + are ambiguous + + \image declarative-reuse-focus.png + + To counteract this we will add a property of the root element to indicate + when an element has 'grabbed' mouse interaction, preventing other + clickable elements from reacting. + + \snippet declarative/tutorials/contacts/2_Reuse/4/Contact.qml grab property + + The code that we want to disable then simply needs to check this property before + acting. + + \snippet declarative/tutorials/contacts/2_Reuse/4/RemoveButton.qml grab + + \note Handling Key and Mouse focus in QML is quite likely to change before + the Qt 4.6 release. +*/ + +/*! + \page tutorials-declarative-contacts-part3.html + \contentspage {Declarative UI Tutorial}{Contents} + \previouspage {tutorials/declarative/contacts/part2}{Chapter 2} + \example tutorials/declarative/contacts/part3 + \title Models, Views and Delegates + \tableofcontents + + In the previous chapters we designed a component to display and + edit a contact. The next step is to display a list of those contacts + and allow the user to expand individual contacts for editing. + + As the previous elements will not be changed in this section, they have + been moved to a lib directory for this tutorial and the relevant + import path has been used. + + \section1 Simple List View + + Displaying lists requires three components. A model that holds the + data displayed, a delegate to indicate how elements are drawn and + a view to arrange the elements. + + \image declarative-tutorial-list.gif + + For the purposes of this tutorial we will be using an SQL query as our + data model. This can be declared in the resources section of + the parent item. + + \snippet declarative/tutorials/contacts/3_Collections/1/ContactView.qml model + + The SqlConnection component describes how to connect to the database in + much the same ways as the QSqlDatabase::addDatabase() function is used. + In this case an SQLite database is used as it can be connected to as a + file, reducing complexity in setting up a database server or credentials. + + The SqlQuery component allows various forms of queries to be described. + When the query is a select statement, the component also acts as a model + allowing it to provide data to a ListView component. The query above + retrieves the fields recid, label, email and phone from a contacts table, + and orders the results by the label of the contact first, and then by + the recid for any contacts with equivalent labels. + + The ListView component is suitable for displaying models and is declared + much like any other QML component. The ListView component also has + a delegate property that defines how to construct components for items in the list. + + \snippet declarative/tutorials/contacts/3_Collections/1/ContactView.qml delegate + + Unlike a child element, this describes a template on how to build the component + for each element, much in the same way that components are loaded from + files such as RemoveButton.qml. The are constructed or destroyed as items + scroll into our out of the visible area of the list. + + The entire view component will look like: + + \snippet declarative/tutorials/contacts/3_Collections/1/ContactView.qml view + + This gives us a list of contacts that the user can flick through. + + \section1 Animating Delegates + + The next step is to allow the user to click on a contact to edit the + contact. We will take advantage of QML to open a Contact component + in the list rather than as a new dialog or view. This is very + similar to how the contents of the FieldText and RemoveButton components + are swapped in and out. + + \image declarative-tutorial-list-open.gif + + \snippet declarative/tutorials/contacts/3_Collections/2/ContactView.qml components + + The first step is to have two children of our delegate component that can + be swapped between. The plain Text component and the Contact component built + in the previous chapters. We also add a MouseRegion that can be clicked upon + to change the state of the delegate component. + + \snippet declarative/tutorials/contacts/3_Collections/2/ContactView.qml states + + This defines the open state of the delegate. It changes the height of the delegate + component to that of the whole list view, pushing the other items off each end of + the list. It sets the lists views scroll yPosition of the ListView to the + y value of the delegate so that the top of the delegate matches the top of the list view. + The next step is to lock the list view. This prevents the user being able to flick + the list view. The final to properties that are set should + be familiar from previous chapters, setting the opacity of the items such + that the new item is visible and the old item hidden. + + We then add a transition so that this becomes animated: + + \snippet declarative/tutorials/contacts/3_Collections/2/ContactView.qml transitions + + This allows the user to click on an item to enter the open state. Elsewhere on our + contact view we add a button so that the user can leave the detailed view of the contact. + + \snippet declarative/tutorials/contacts/3_Collections/2/ContactView.qml button + + And connect it's clicked value to some script to set the state of the delegate + back to its default state. + + \snippet declarative/tutorials/contacts/3_Collections/2/ContactView.qml connections + + Something worth noting at this point is that every delegate created has this connection. + It is important to check whether the delegate is the one in the open state, and + taking some effort to ensure only one is, before acting on the signal from the button. + + \section1 Performance Considerations + + We have now made a contact application that can view a list of contacts, open one, + and close it again. Its now time to take a moment and consider the implications + of a list view delegate. It is created for each and every item in the list, + and while the list cleans up after itself and only has delegate components constructed + for visible items and any single point of animation, the list can scroll very quickly. + This means potentially thousands of delegate components will be constructed and + destroyed when the user is flicking through the list. + + Its important then to try and minimize the complexity of the delegate. This + can be done by delaying the loading of the component. By using the qml property + of the \l{Item}{Item} component, we can delay building the Contact.qml item until the user + attempts to open the list. + + \snippet declarative/tutorials/contacts/3_Collections/3/ContactView.qml setting qml + + Each item has a qml property that represents the filename for the contents of + a special qmlItem child of the \l{Item}{Item}. By setting the qml property of the Details + component on clicking the mouse region, the more complex component isn't loaded + until needed. The down side about this though is the properties of Contact + cannot be set until the item is loaded. This requires using the Bind element. + + + \snippet declarative/tutorials/contacts/3_Collections/3/ContactView.qml binding + + Unlike binding a value to the property of a component directly, the Bind element + allows both the target and the property set to themselves be to dynamic values. + This means that when the qml property is set, it will change the + qmlItem property of the Details component. This in turn triggers the Bind + elements to set the required properties of the qmlItem, which is now + an instance of the Contact component. +*/ |