Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView...

41
Roll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games

Transcript of Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView...

Page 1: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Roll Your Own Qt Quick TreeView

– Alex MontgomeryTelltale Games

Page 2: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

A Bit About Me● Lead Tools Developer at Telltale Games● Background in Widgets● Strong foundation and fondness for the MVC

paradigm and Qt's model / view framework● Gradually switching to QML to create dynamic,

OpenGL accelerated UIs● Needed a way to bring new and existing models

to new Qt Quick tools, so I wrote a Qt Quick TreeView

Page 3: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Simplifying Assumptions● All these concepts can be applied to

complex views, but this presentation will not cover:– Multiple columns

– Headers

– Models written in QML

– Advanced view actions such as drag 'n drop

– Actual C++ TreeView source code (sorry)

Page 4: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Bringing MVC to QML: Goals● Support models written in C++ that stem from

the QAbstractItemModel base● Take advantage of the speed and flexibility of

Qt Quick's drawing API● Be compatible with the design and philosophy

of existing QML view types for lists and tables● Design beautiful interactive UIs that don't have

to fit in the generic confines of the QWidget-based views

Page 5: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

The State of MVC in Qt Widgets● Qt (C++) has support for representing

models in:● Lists (QListView)● Tables (QTableView)● Trees (QTreeView)

Page 6: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

The State of MVC in Qt Quick● Qt Quick / QML has support for displaying

models in:● Lists (ListView)● Tables (GridView)● Trees (???)

Page 7: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

The Gap: a Qt Quick TreeView

● Difficult to design a one-size-fits-all solution● Generating acceptable performance is tricky without

cutting a few corners● With a little hacking, it does exist! Kinda. (but I don't

recommend it) – Nested ListViews using DelegateModel (formerly

VisualDataModel) with a rootIndex property

Why doesn't this exist yet?!

Page 8: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

The Goal: Fill the Gap!● Support C++ models using QModelIndex and

model roles to communicate to the view● Optimize delegate management to support

large-scale models with sorting and filtering without sacrificing performance

● Support multiple delegate types, each written in QML or Qt Quick

● A TreeView QQuickItem with the performance of C++ exported to QML

Page 9: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Language Divide

Model

Descendants ofQAbstractItemModel

C++C++ QMLQML

Delegates

Complex Items,Animations,

effects

TreeView

- Delegate factory - Scroll View- Layout management - Nested Grids

Page 10: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Model Requirements (as Always)● Standard virtual functions (derived from QAbstractItemModel),

just as used with QWidget based views– Parent() - returns a parent index of any QModelIndex

– Index() - return a child index at a given row and col

– RowCount() - Number of rows (children) of a given model index

– ColumnCount() - number of columns (always == 1 for the purposes of this talk)

● Model roles – integer IDs of variables that the view can use to query the model about the state of a particular node addressed by a QModelIndex. The data can be any type supported by QVariant, including custom types.– Qt::DisplayRole, Qt::DecorationRole, Qt::SizeHintRole, etc.

Page 11: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Model Requirements (QML era)● A mapping from model roles (int) to string

representations (QByteArray) ● QHash<int, QByteArray> roleNames() ● Used to insert Javascript-style vars

(QVariants) into each delegate's scope● This is a requirement for existing Qt Quick

MVC views (ListView & GridView)

Page 12: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Model Additions (so says I)● A few extra roles to keep delegates flexible and efficient

– Node ID● A unique identifier greatly simplifies moving delegates around when

servicing model changes like layoutChanged● Can be used in a QMap of IDs to Delegates to ease delegate updates

– Delegate path (or QQmlComponent pointer) per Node type● Gives the TreeView the path of a QML item to factory (.qml file) for

each node type

– Expanded state● Allows the delegate to show an expander control based on model state● Allows the model to control expanded state programatically without

breaking the MVC paradigm by talking directly to the view

Page 13: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Supporting Legacy Models● Legacy models (file system, string, standard, etc.) will not have the

aforementioned extra roles– Solution 1: Derive from the model and provide the extra roles

– Solution 2: Provide the TreeView with lambdas or other function objects that can determine id / delegate path / expanded state based on other role data from the model

– Solution 3: Work harder, not smarter● Node ID - Not strictly necessary just allows for some nice optimizations. Must

handle layoutChanged signals as mini-model resets● Delegate path - Provide only one delegate (the existing QML delegate solution)

that changes its appearance based on data from the model (other roles)● Expanded state – Store an expanded state in the view as a structure alongside

each delegate and require that each delegate have a top-level “expanded” bool property. The view can attach to the delegate's built-in “expandedChanged” signal to allow the delegate to communicate changes back

Page 14: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Delegates – The Face of the Data

● Wait, what's a delegate?– A per-index node, written in QML, that

represents a row or column in a tree and gets all of its info from a QModelIndex

– These are used identically to how they are used in existing QML views (ListView and GridView)

Page 15: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Delegates – A Sum of Their Roles● How do you write one?

– A properly designed MVC delegate can only query data from its model index

– QML delegates receive their model data from role variables inserted into their namespace

– Typically delegates create and bind properties to their role variables

– Delegates are going to be recycled for performance reasons and must not have dangling state

Page 16: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Delegates – Best Practices● Most TreeViews are resizable, avoid using static heights and

widths when possible – use Layouts!● QML States and the “when” property

– Delegates can have multiple States and each State can have multiple PropertyChange objects. Each state can depend on particular role variable values and efficiently change large sets of properties when the role variable is modified

● Transitions and animations– role values change instantly but Transitions can smoothly animate

sub-properties to ehance the user experience● Delegates can even take entire QML Items from their role values for

advanced view customization (to use as children or in particular layouts)

Page 17: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Delegates – Talking Back● Many delegates need to talk back to the model so that they

can change the state of the underlying data– Option 1: Provide a role to access a node (QObject) on the model

back-end● Pros: The Qobject node can have properties, setter methods, or

other Q_INVOKABLE methods for whatever purpose you like● Cons: The model might not use QObjects to represent its nodes

– Option 2: Provide a role to access the current model index, and a way to directly access the the model's setData() function

● Pros: works with any model (including legacy models)● Cons: Can be tricky with QSortFilterProxy filtering

Page 18: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Qt Quick TreeView: Overview● Written in C++, exposed as a QQuickItem to QML, e.g.

qmlRegisterType<TreeView>("mvcLib", 1, 0, "TreeView");● Composed of a Qt Quick Controls ScrollView (or a

simple Flickable) filled with nested GridLayouts of delegates

● Divided into C++ and QML parts, each responsible for different tasks:– QML takes care of bindings and attached properties

(Layout.row, Layout.col)

– C++ takes care of model communication and delegate management

Page 19: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

A Quick TreeView

Scroll Area

Top-leveldelegate

LeafDelegate

Each delegate with children is placed in a grid with itself at the top, and its children in subsequent rows

Page 20: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

GridLayout Structuring● GridLayout is a QML item in QtQuick.Layouts that manages size,

spacing, and general layout of children much like QWidget layouts● GridLayout is ideal for all tree types (including multi-column trees),

as it provides attached properties for its children to specify row and column

● Row and column properties can (currently) only be specified in QML (AFAIK), so it is useful to create helper functions or “derive” a QML type from GridLayout that can encapsulate this functionality

● GridLayouts can be nested to display trees of ordered items, each with children of varying types and sizes

● Each delegate for a QModelIndex that has children is given a grid of its own

Page 21: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Servicing Model Signals I● Tree nodes / indices can be...● Added – handle when complete

– rowsInserted()

– columnsInserted()

● Removed – handle before the items become invalid– rowsAboutToBeRemoved()

– ColumnsAboutToBeRemoved()

● Moved – handle when complete– RowsMoved()

– layoutChanged()

Page 22: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Making Delegate Items (C++)● QQmlComponents serve as delegate

factories to create QML items from a C++ TreeView

● QQmlContexts inject model state via role variables into the scope of QML delegate items

● Model roles define the set of data a delegate can know about its model node

Page 23: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Encapsulating Data with Contexts● What is a QQmlContext?

– Contexts define a set of variables that can be accessed by items in a QML scope

– All QML Items are created into a particular context (defaulting to the QQmlEngine's root context)

– Contexts are relatively quick and efficient (this is how the “id” property works in QML)

– All names in a context must be distinct

– Context properties can be set individually or using entire property sets via a “context object”

Page 24: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Contexts: Properties vs Objects● Context Object: a C++ object that can use properties

declared using the Q_PROPERTY macro to represent entire (static) sets of data

● Context Object Advantages:– All properties are set at once, so QML declarative bindings

do not need to be re-evaluated every time an individual property is set

– Delegate properties might be written (poorly) to depend on each other which can cause unexpected or even non-deterministic behavior. Context objects solve this by ensuring all the object's properties are initialized simultaneously

Page 25: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Contexts: Properties vs Objects● Context Property: an individual named value that can be

set on a QQmlContext. These can be anything QVariants support, including custom types

● Context Property Advantages– Properties don't need to be pre-defined as a Q_PROPERTY in

a QObject written in C++. QObject-style dynamic properties are supported

– When handling role changes, existing context properties can be queried and only modified when necessary

– For models whose index properties only change a few at a time, setting individual properties instead of entire sets can be cheaper

Page 26: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Setting Up Model Roles Vars with Contexts

● The model defines a mapping of data roles to names using virtual QHash<int, QByteArray> roleNames()

● A context is created for each delegate shown in the TreeView

● Each context contains a context property for each named role, or a context object comprising all the individual properties

● Each delegate is created by the TreeView into its own context

● Whenever an index changes role values (dataChanged signal) the context properties are adjusted and re-evaluated

Page 27: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Creating Delegates from Components

● QQmlComponent takes a path to a .qml file and acts as a factory that creates QML items of that type

● When a delegate needs to be created, the Treeview:– Calls the data() method on the passed QModelIndex for each

named role

– Sets up the context with variables from the QVariants returned from each data call (C++ QVariants magically convert to Javascript vars)

– Calls create() on the QQmlComponent, passing the context as a parameter which prompts the factory to instantiate a delegate

– Gives any delegates capable of having children a GridView

– Places the created delegate in its proper row/column of its parent GridView

Page 28: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Servicing Model Signals II● dataChanged() - one or more role variables has

changed– Query each role value using QModelIndex's data() method

● When using context properties: Query each corresponding contextProperty of the delegate's QQmlContext and set new values when necessary (only when changed)

● When using context objects: Query all role values and set them on a new Qobject which is passed to setContextObject on the delegate's context

● modelReset()– Tear down and rebuild entire tree

Page 29: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Optimization: Only Manage Visible Delegates

● Minimize delegate creation, destruction, and non-visible binding changes:– Ignore addRows and removeRows signals for

QmodelIndexes that are not already in the expanded hierarchy

– Check the expanded role when handling any dataChanged signal

● Newly expanded indices need delegates for their children● Newly collapsed indices need their child delegates to be

recycled

Page 30: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Optimization: Recycle Delegates

● Creating and destroying QML items is surprisingly expensive, even on the desktop

● Instead of destroying delegates, they can be stored in a list (or stack for temporal locality) based on their QQmlComponent's factory path

● All delegate states must be solely defined by model roles

Page 31: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Optimization: Minimizing role count

● Each role must be set on each delegate's context and bindings can be expensive to re-evaluate (esp. using context properties)– Keeping the named role count to a minimum

can drastically improve performance

– Objects or containers can be defined as role variables to act as holders for multiple sub-properties

Page 32: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Putting It all Together● Representing large hierarchical data sets in Qt Quick is

challenging without sacrificing flexibility and/or performance

● Using a C++ TreeView and model allows for maximal performance

● Using QML / Qt Quick Items as delegates allows for a better look and feel than widget-based views including OpenGL accelerated effects

● Optimizing and recycling delegates keeps run-time performance high, even when the model calls for large data changes or sorting

Page 33: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

File System Demo● Model: Derived from standard QFileSystemModel adding extra

roles to allow for optimization● TreeView: Optimized C++ view using recyclable delegates● Delegates: Two simple delegate types: Directory and File● Roles:

– From QFileSystemModel: whatsThis, statusTip, filePermissions, toolTip, fileName, filePath, fileIcon

– For the TreeView: nodeType, nodeDelegatePath, nodeID, nodeExpanded

– Extra: readOnly

● Test: – Adding, renaming, and removing files

– Why doesn't read-only respond instantly?

Page 34: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Main.qml

Page 35: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Directory Delegate

Page 36: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

File Delegate

Page 37: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

MyFileSystemModel.h

Page 38: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

MyFileSystemModel.cpp

Page 39: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

MyFileSystemModel.cpp (cont.)

Page 40: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Main.cpp

Page 41: Roll Your Own Qt Quick TreeView - Qt Developer · PDF fileRoll Your Own Qt Quick TreeView – Alex Montgomery Telltale Games. A Bit About Me

Questions● Any further questions?