From bfb3c424abc7c839dc44de0e36024ede99ece736 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 14 Oct 2011 10:51:42 +0200 Subject: Rename Qt Quick-specific classes to QQuick* The QSG (SceneGraph) prefix is too generic for Qt Quick(2)-specific classes. All the classes and files in the declarative/items directory have been renamed. In particular, for classes that are currently public, the renaming is as follows: QSGView --> QQuickView QSGCanvas --> QQuickCanvas QSGItem --> QQuickItem QSGPaintedItem --> QQuickPaintedItem The header files have been renamed accordingly (e.g. qsgview.h --> qquickview.h). Change-Id: Iac937fff81db20bb639486a793c3aeb5230b038c Reviewed-by: Kent Hansen --- src/declarative/items/context2d/context2d.pri | 24 +- .../items/context2d/qquickcanvasitem.cpp | 728 +++ .../items/context2d/qquickcanvasitem_p.h | 148 + .../items/context2d/qquickcontext2d.cpp | 3534 ++++++++++++++ .../items/context2d/qquickcontext2d_p.h | 187 + .../context2d/qquickcontext2dcommandbuffer.cpp | 470 ++ .../context2d/qquickcontext2dcommandbuffer_p.h | 268 ++ .../items/context2d/qquickcontext2dnode.cpp | 124 + .../items/context2d/qquickcontext2dnode_p.h | 86 + .../items/context2d/qquickcontext2dtexture.cpp | 777 +++ .../items/context2d/qquickcontext2dtexture_p.h | 205 + .../items/context2d/qquickcontext2dtile.cpp | 165 + .../items/context2d/qquickcontext2dtile_p.h | 112 + src/declarative/items/context2d/qsgcanvasitem.cpp | 728 --- src/declarative/items/context2d/qsgcanvasitem_p.h | 148 - src/declarative/items/context2d/qsgcontext2d.cpp | 3534 -------------- src/declarative/items/context2d/qsgcontext2d_p.h | 187 - .../items/context2d/qsgcontext2dcommandbuffer.cpp | 470 -- .../items/context2d/qsgcontext2dcommandbuffer_p.h | 268 -- .../items/context2d/qsgcontext2dnode.cpp | 124 - .../items/context2d/qsgcontext2dnode_p.h | 86 - .../items/context2d/qsgcontext2dtexture.cpp | 777 --- .../items/context2d/qsgcontext2dtexture_p.h | 205 - .../items/context2d/qsgcontext2dtile.cpp | 165 - .../items/context2d/qsgcontext2dtile_p.h | 112 - src/declarative/items/items.pri | 236 +- src/declarative/items/qquickanchors.cpp | 1110 +++++ src/declarative/items/qquickanchors_p.h | 201 + src/declarative/items/qquickanchors_p_p.h | 173 + src/declarative/items/qquickanimatedimage.cpp | 397 ++ src/declarative/items/qquickanimatedimage_p.h | 117 + src/declarative/items/qquickanimatedimage_p_p.h | 88 + src/declarative/items/qquickanimation.cpp | 792 +++ src/declarative/items/qquickanimation_p.h | 209 + src/declarative/items/qquickanimation_p_p.h | 153 + src/declarative/items/qquickborderimage.cpp | 601 +++ src/declarative/items/qquickborderimage_p.h | 110 + src/declarative/items/qquickborderimage_p_p.h | 103 + src/declarative/items/qquickcanvas.cpp | 2399 ++++++++++ src/declarative/items/qquickcanvas.h | 135 + src/declarative/items/qquickcanvas_p.h | 308 ++ src/declarative/items/qquickclipnode.cpp | 120 + src/declarative/items/qquickclipnode_p.h | 71 + src/declarative/items/qquickdrag.cpp | 462 ++ src/declarative/items/qquickdrag_p.h | 208 + src/declarative/items/qquickdroparea.cpp | 426 ++ src/declarative/items/qquickdroparea_p.h | 167 + src/declarative/items/qquickevents.cpp | 239 + src/declarative/items/qquickevents_p_p.h | 144 + src/declarative/items/qquickflickable.cpp | 1997 ++++++++ src/declarative/items/qquickflickable_p.h | 277 ++ src/declarative/items/qquickflickable_p_p.h | 262 + src/declarative/items/qquickflipable.cpp | 308 ++ src/declarative/items/qquickflipable_p.h | 104 + src/declarative/items/qquickfocusscope.cpp | 70 + src/declarative/items/qquickfocusscope_p.h | 68 + src/declarative/items/qquickgridview.cpp | 1930 ++++++++ src/declarative/items/qquickgridview_p.h | 146 + src/declarative/items/qquickimage.cpp | 753 +++ src/declarative/items/qquickimage_p.h | 122 + src/declarative/items/qquickimage_p_p.h | 85 + src/declarative/items/qquickimagebase.cpp | 291 ++ src/declarative/items/qquickimagebase_p.h | 119 + src/declarative/items/qquickimagebase_p_p.h | 93 + src/declarative/items/qquickimplicitsizeitem.cpp | 92 + src/declarative/items/qquickimplicitsizeitem_p.h | 101 + src/declarative/items/qquickimplicitsizeitem_p_p.h | 92 + src/declarative/items/qquickitem.cpp | 5038 ++++++++++++++++++++ src/declarative/items/qquickitem.h | 414 ++ src/declarative/items/qquickitem_p.h | 716 +++ src/declarative/items/qquickitemchangelistener_p.h | 82 + src/declarative/items/qquickitemsmodule.cpp | 228 + src/declarative/items/qquickitemsmodule_p.h | 65 + src/declarative/items/qquickitemview.cpp | 1676 +++++++ src/declarative/items/qquickitemview_p.h | 294 ++ src/declarative/items/qquickitemview_p_p.h | 256 + src/declarative/items/qquicklistview.cpp | 2500 ++++++++++ src/declarative/items/qquicklistview_p.h | 215 + src/declarative/items/qquickloader.cpp | 853 ++++ src/declarative/items/qquickloader_p.h | 123 + src/declarative/items/qquickloader_p_p.h | 121 + src/declarative/items/qquickmousearea.cpp | 1131 +++++ src/declarative/items/qquickmousearea_p.h | 226 + src/declarative/items/qquickmousearea_p_p.h | 111 + src/declarative/items/qquickninepatchnode.cpp | 302 ++ src/declarative/items/qquickninepatchnode_p.h | 98 + src/declarative/items/qquickpainteditem.cpp | 537 +++ src/declarative/items/qquickpainteditem.h | 130 + src/declarative/items/qquickpainteditem_p.h | 73 + src/declarative/items/qquickpathview.cpp | 1738 +++++++ src/declarative/items/qquickpathview_p.h | 257 + src/declarative/items/qquickpathview_p_p.h | 194 + src/declarative/items/qquickpincharea.cpp | 600 +++ src/declarative/items/qquickpincharea_p.h | 315 ++ src/declarative/items/qquickpincharea_p_p.h | 116 + src/declarative/items/qquickpositioners.cpp | 1533 ++++++ src/declarative/items/qquickpositioners_p.h | 295 ++ src/declarative/items/qquickpositioners_p_p.h | 164 + src/declarative/items/qquickrectangle.cpp | 555 +++ src/declarative/items/qquickrectangle_p.h | 189 + src/declarative/items/qquickrectangle_p_p.h | 103 + src/declarative/items/qquickrepeater.cpp | 438 ++ src/declarative/items/qquickrepeater_p.h | 110 + src/declarative/items/qquickrepeater_p_p.h | 83 + src/declarative/items/qquickscalegrid.cpp | 214 + src/declarative/items/qquickscalegrid_p_p.h | 134 + src/declarative/items/qquickshadereffect.cpp | 649 +++ src/declarative/items/qquickshadereffect_p.h | 165 + src/declarative/items/qquickshadereffectmesh.cpp | 215 + src/declarative/items/qquickshadereffectmesh_p.h | 103 + src/declarative/items/qquickshadereffectnode.cpp | 316 ++ src/declarative/items/qquickshadereffectnode_p.h | 151 + src/declarative/items/qquickshadereffectsource.cpp | 905 ++++ src/declarative/items/qquickshadereffectsource_p.h | 255 + src/declarative/items/qquicksprite.cpp | 130 + src/declarative/items/qquicksprite_p.h | 136 + src/declarative/items/qquickspriteengine.cpp | 500 ++ src/declarative/items/qquickspriteengine_p.h | 318 ++ src/declarative/items/qquickspriteimage.cpp | 421 ++ src/declarative/items/qquickspriteimage_p.h | 129 + src/declarative/items/qquickstateoperations.cpp | 1346 ++++++ src/declarative/items/qquickstateoperations_p.h | 275 ++ src/declarative/items/qquicktext.cpp | 1938 ++++++++ src/declarative/items/qquicktext_p.h | 252 + src/declarative/items/qquicktext_p_p.h | 168 + src/declarative/items/qquicktextedit.cpp | 1976 ++++++++ src/declarative/items/qquicktextedit_p.h | 306 ++ src/declarative/items/qquicktextedit_p_p.h | 144 + src/declarative/items/qquicktextinput.cpp | 2007 ++++++++ src/declarative/items/qquicktextinput_p.h | 303 ++ src/declarative/items/qquicktextinput_p_p.h | 172 + src/declarative/items/qquicktextnode.cpp | 1343 ++++++ src/declarative/items/qquicktextnode_p.h | 110 + src/declarative/items/qquicktranslate.cpp | 319 ++ src/declarative/items/qquicktranslate_p.h | 162 + src/declarative/items/qquickview.cpp | 417 ++ src/declarative/items/qquickview.h | 120 + src/declarative/items/qquickview_p.h | 107 + src/declarative/items/qquickvisualadaptormodel.cpp | 889 ++++ src/declarative/items/qquickvisualadaptormodel_p.h | 129 + src/declarative/items/qquickvisualdatamodel.cpp | 2538 ++++++++++ src/declarative/items/qquickvisualdatamodel_p.h | 242 + src/declarative/items/qquickvisualitemmodel.cpp | 243 + src/declarative/items/qquickvisualitemmodel_p.h | 178 + src/declarative/items/qsganchors.cpp | 1110 ----- src/declarative/items/qsganchors_p.h | 201 - src/declarative/items/qsganchors_p_p.h | 173 - src/declarative/items/qsganimatedimage.cpp | 397 -- src/declarative/items/qsganimatedimage_p.h | 117 - src/declarative/items/qsganimatedimage_p_p.h | 88 - src/declarative/items/qsganimation.cpp | 792 --- src/declarative/items/qsganimation_p.h | 209 - src/declarative/items/qsganimation_p_p.h | 153 - src/declarative/items/qsgborderimage.cpp | 601 --- src/declarative/items/qsgborderimage_p.h | 110 - src/declarative/items/qsgborderimage_p_p.h | 103 - src/declarative/items/qsgcanvas.cpp | 2399 ---------- src/declarative/items/qsgcanvas.h | 135 - src/declarative/items/qsgcanvas_p.h | 308 -- src/declarative/items/qsgclipnode.cpp | 120 - src/declarative/items/qsgclipnode_p.h | 71 - src/declarative/items/qsgdrag.cpp | 462 -- src/declarative/items/qsgdrag_p.h | 208 - src/declarative/items/qsgdroparea.cpp | 426 -- src/declarative/items/qsgdroparea_p.h | 167 - src/declarative/items/qsgevents.cpp | 239 - src/declarative/items/qsgevents_p_p.h | 144 - src/declarative/items/qsgflickable.cpp | 1997 -------- src/declarative/items/qsgflickable_p.h | 277 -- src/declarative/items/qsgflickable_p_p.h | 262 - src/declarative/items/qsgflipable.cpp | 308 -- src/declarative/items/qsgflipable_p.h | 104 - src/declarative/items/qsgfocusscope.cpp | 70 - src/declarative/items/qsgfocusscope_p.h | 68 - src/declarative/items/qsggridview.cpp | 1930 -------- src/declarative/items/qsggridview_p.h | 146 - src/declarative/items/qsgimage.cpp | 753 --- src/declarative/items/qsgimage_p.h | 122 - src/declarative/items/qsgimage_p_p.h | 85 - src/declarative/items/qsgimagebase.cpp | 291 -- src/declarative/items/qsgimagebase_p.h | 119 - src/declarative/items/qsgimagebase_p_p.h | 93 - src/declarative/items/qsgimplicitsizeitem.cpp | 92 - src/declarative/items/qsgimplicitsizeitem_p.h | 101 - src/declarative/items/qsgimplicitsizeitem_p_p.h | 92 - src/declarative/items/qsgitem.cpp | 5038 -------------------- src/declarative/items/qsgitem.h | 415 -- src/declarative/items/qsgitem_p.h | 716 --- src/declarative/items/qsgitemchangelistener_p.h | 82 - src/declarative/items/qsgitemsmodule.cpp | 228 - src/declarative/items/qsgitemsmodule_p.h | 65 - src/declarative/items/qsgitemview.cpp | 1676 ------- src/declarative/items/qsgitemview_p.h | 294 -- src/declarative/items/qsgitemview_p_p.h | 256 - src/declarative/items/qsglistview.cpp | 2500 ---------- src/declarative/items/qsglistview_p.h | 215 - src/declarative/items/qsgloader.cpp | 853 ---- src/declarative/items/qsgloader_p.h | 123 - src/declarative/items/qsgloader_p_p.h | 121 - src/declarative/items/qsgmousearea.cpp | 1131 ----- src/declarative/items/qsgmousearea_p.h | 226 - src/declarative/items/qsgmousearea_p_p.h | 111 - src/declarative/items/qsgninepatchnode.cpp | 302 -- src/declarative/items/qsgninepatchnode_p.h | 98 - src/declarative/items/qsgpainteditem.cpp | 536 --- src/declarative/items/qsgpainteditem.h | 130 - src/declarative/items/qsgpainteditem_p.h | 73 - src/declarative/items/qsgpathview.cpp | 1738 ------- src/declarative/items/qsgpathview_p.h | 257 - src/declarative/items/qsgpathview_p_p.h | 194 - src/declarative/items/qsgpincharea.cpp | 600 --- src/declarative/items/qsgpincharea_p.h | 315 -- src/declarative/items/qsgpincharea_p_p.h | 116 - src/declarative/items/qsgpositioners.cpp | 1533 ------ src/declarative/items/qsgpositioners_p.h | 295 -- src/declarative/items/qsgpositioners_p_p.h | 164 - src/declarative/items/qsgrectangle.cpp | 555 --- src/declarative/items/qsgrectangle_p.h | 189 - src/declarative/items/qsgrectangle_p_p.h | 103 - src/declarative/items/qsgrepeater.cpp | 438 -- src/declarative/items/qsgrepeater_p.h | 110 - src/declarative/items/qsgrepeater_p_p.h | 83 - src/declarative/items/qsgscalegrid.cpp | 214 - src/declarative/items/qsgscalegrid_p_p.h | 134 - src/declarative/items/qsgshadereffect.cpp | 649 --- src/declarative/items/qsgshadereffect_p.h | 165 - src/declarative/items/qsgshadereffectmesh.cpp | 215 - src/declarative/items/qsgshadereffectmesh_p.h | 103 - src/declarative/items/qsgshadereffectnode.cpp | 316 -- src/declarative/items/qsgshadereffectnode_p.h | 152 - src/declarative/items/qsgshadereffectsource.cpp | 905 ---- src/declarative/items/qsgshadereffectsource_p.h | 255 - src/declarative/items/qsgsprite.cpp | 130 - src/declarative/items/qsgsprite_p.h | 136 - src/declarative/items/qsgspriteengine.cpp | 500 -- src/declarative/items/qsgspriteengine_p.h | 318 -- src/declarative/items/qsgspriteimage.cpp | 421 -- src/declarative/items/qsgspriteimage_p.h | 129 - src/declarative/items/qsgstateoperations.cpp | 1346 ------ src/declarative/items/qsgstateoperations_p.h | 275 -- src/declarative/items/qsgtext.cpp | 1938 -------- src/declarative/items/qsgtext_p.h | 252 - src/declarative/items/qsgtext_p_p.h | 168 - src/declarative/items/qsgtextedit.cpp | 1976 -------- src/declarative/items/qsgtextedit_p.h | 306 -- src/declarative/items/qsgtextedit_p_p.h | 144 - src/declarative/items/qsgtextinput.cpp | 2007 -------- src/declarative/items/qsgtextinput_p.h | 303 -- src/declarative/items/qsgtextinput_p_p.h | 172 - src/declarative/items/qsgtextnode.cpp | 1343 ------ src/declarative/items/qsgtextnode_p.h | 110 - src/declarative/items/qsgtranslate.cpp | 319 -- src/declarative/items/qsgtranslate_p.h | 162 - src/declarative/items/qsgview.cpp | 417 -- src/declarative/items/qsgview.h | 120 - src/declarative/items/qsgview_p.h | 107 - src/declarative/items/qsgvisualadaptormodel.cpp | 889 ---- src/declarative/items/qsgvisualadaptormodel_p.h | 129 - src/declarative/items/qsgvisualdatamodel.cpp | 2538 ---------- src/declarative/items/qsgvisualdatamodel_p.h | 242 - src/declarative/items/qsgvisualitemmodel.cpp | 243 - src/declarative/items/qsgvisualitemmodel_p.h | 178 - 262 files changed, 64881 insertions(+), 64882 deletions(-) create mode 100644 src/declarative/items/context2d/qquickcanvasitem.cpp create mode 100644 src/declarative/items/context2d/qquickcanvasitem_p.h create mode 100644 src/declarative/items/context2d/qquickcontext2d.cpp create mode 100644 src/declarative/items/context2d/qquickcontext2d_p.h create mode 100644 src/declarative/items/context2d/qquickcontext2dcommandbuffer.cpp create mode 100644 src/declarative/items/context2d/qquickcontext2dcommandbuffer_p.h create mode 100644 src/declarative/items/context2d/qquickcontext2dnode.cpp create mode 100644 src/declarative/items/context2d/qquickcontext2dnode_p.h create mode 100644 src/declarative/items/context2d/qquickcontext2dtexture.cpp create mode 100644 src/declarative/items/context2d/qquickcontext2dtexture_p.h create mode 100644 src/declarative/items/context2d/qquickcontext2dtile.cpp create mode 100644 src/declarative/items/context2d/qquickcontext2dtile_p.h delete mode 100644 src/declarative/items/context2d/qsgcanvasitem.cpp delete mode 100644 src/declarative/items/context2d/qsgcanvasitem_p.h delete mode 100644 src/declarative/items/context2d/qsgcontext2d.cpp delete mode 100644 src/declarative/items/context2d/qsgcontext2d_p.h delete mode 100644 src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp delete mode 100644 src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h delete mode 100644 src/declarative/items/context2d/qsgcontext2dnode.cpp delete mode 100644 src/declarative/items/context2d/qsgcontext2dnode_p.h delete mode 100644 src/declarative/items/context2d/qsgcontext2dtexture.cpp delete mode 100644 src/declarative/items/context2d/qsgcontext2dtexture_p.h delete mode 100644 src/declarative/items/context2d/qsgcontext2dtile.cpp delete mode 100644 src/declarative/items/context2d/qsgcontext2dtile_p.h create mode 100644 src/declarative/items/qquickanchors.cpp create mode 100644 src/declarative/items/qquickanchors_p.h create mode 100644 src/declarative/items/qquickanchors_p_p.h create mode 100644 src/declarative/items/qquickanimatedimage.cpp create mode 100644 src/declarative/items/qquickanimatedimage_p.h create mode 100644 src/declarative/items/qquickanimatedimage_p_p.h create mode 100644 src/declarative/items/qquickanimation.cpp create mode 100644 src/declarative/items/qquickanimation_p.h create mode 100644 src/declarative/items/qquickanimation_p_p.h create mode 100644 src/declarative/items/qquickborderimage.cpp create mode 100644 src/declarative/items/qquickborderimage_p.h create mode 100644 src/declarative/items/qquickborderimage_p_p.h create mode 100644 src/declarative/items/qquickcanvas.cpp create mode 100644 src/declarative/items/qquickcanvas.h create mode 100644 src/declarative/items/qquickcanvas_p.h create mode 100644 src/declarative/items/qquickclipnode.cpp create mode 100644 src/declarative/items/qquickclipnode_p.h create mode 100644 src/declarative/items/qquickdrag.cpp create mode 100644 src/declarative/items/qquickdrag_p.h create mode 100644 src/declarative/items/qquickdroparea.cpp create mode 100644 src/declarative/items/qquickdroparea_p.h create mode 100644 src/declarative/items/qquickevents.cpp create mode 100644 src/declarative/items/qquickevents_p_p.h create mode 100644 src/declarative/items/qquickflickable.cpp create mode 100644 src/declarative/items/qquickflickable_p.h create mode 100644 src/declarative/items/qquickflickable_p_p.h create mode 100644 src/declarative/items/qquickflipable.cpp create mode 100644 src/declarative/items/qquickflipable_p.h create mode 100644 src/declarative/items/qquickfocusscope.cpp create mode 100644 src/declarative/items/qquickfocusscope_p.h create mode 100644 src/declarative/items/qquickgridview.cpp create mode 100644 src/declarative/items/qquickgridview_p.h create mode 100644 src/declarative/items/qquickimage.cpp create mode 100644 src/declarative/items/qquickimage_p.h create mode 100644 src/declarative/items/qquickimage_p_p.h create mode 100644 src/declarative/items/qquickimagebase.cpp create mode 100644 src/declarative/items/qquickimagebase_p.h create mode 100644 src/declarative/items/qquickimagebase_p_p.h create mode 100644 src/declarative/items/qquickimplicitsizeitem.cpp create mode 100644 src/declarative/items/qquickimplicitsizeitem_p.h create mode 100644 src/declarative/items/qquickimplicitsizeitem_p_p.h create mode 100644 src/declarative/items/qquickitem.cpp create mode 100644 src/declarative/items/qquickitem.h create mode 100644 src/declarative/items/qquickitem_p.h create mode 100644 src/declarative/items/qquickitemchangelistener_p.h create mode 100644 src/declarative/items/qquickitemsmodule.cpp create mode 100644 src/declarative/items/qquickitemsmodule_p.h create mode 100644 src/declarative/items/qquickitemview.cpp create mode 100644 src/declarative/items/qquickitemview_p.h create mode 100644 src/declarative/items/qquickitemview_p_p.h create mode 100644 src/declarative/items/qquicklistview.cpp create mode 100644 src/declarative/items/qquicklistview_p.h create mode 100644 src/declarative/items/qquickloader.cpp create mode 100644 src/declarative/items/qquickloader_p.h create mode 100644 src/declarative/items/qquickloader_p_p.h create mode 100644 src/declarative/items/qquickmousearea.cpp create mode 100644 src/declarative/items/qquickmousearea_p.h create mode 100644 src/declarative/items/qquickmousearea_p_p.h create mode 100644 src/declarative/items/qquickninepatchnode.cpp create mode 100644 src/declarative/items/qquickninepatchnode_p.h create mode 100644 src/declarative/items/qquickpainteditem.cpp create mode 100644 src/declarative/items/qquickpainteditem.h create mode 100644 src/declarative/items/qquickpainteditem_p.h create mode 100644 src/declarative/items/qquickpathview.cpp create mode 100644 src/declarative/items/qquickpathview_p.h create mode 100644 src/declarative/items/qquickpathview_p_p.h create mode 100644 src/declarative/items/qquickpincharea.cpp create mode 100644 src/declarative/items/qquickpincharea_p.h create mode 100644 src/declarative/items/qquickpincharea_p_p.h create mode 100644 src/declarative/items/qquickpositioners.cpp create mode 100644 src/declarative/items/qquickpositioners_p.h create mode 100644 src/declarative/items/qquickpositioners_p_p.h create mode 100644 src/declarative/items/qquickrectangle.cpp create mode 100644 src/declarative/items/qquickrectangle_p.h create mode 100644 src/declarative/items/qquickrectangle_p_p.h create mode 100644 src/declarative/items/qquickrepeater.cpp create mode 100644 src/declarative/items/qquickrepeater_p.h create mode 100644 src/declarative/items/qquickrepeater_p_p.h create mode 100644 src/declarative/items/qquickscalegrid.cpp create mode 100644 src/declarative/items/qquickscalegrid_p_p.h create mode 100644 src/declarative/items/qquickshadereffect.cpp create mode 100644 src/declarative/items/qquickshadereffect_p.h create mode 100644 src/declarative/items/qquickshadereffectmesh.cpp create mode 100644 src/declarative/items/qquickshadereffectmesh_p.h create mode 100644 src/declarative/items/qquickshadereffectnode.cpp create mode 100644 src/declarative/items/qquickshadereffectnode_p.h create mode 100644 src/declarative/items/qquickshadereffectsource.cpp create mode 100644 src/declarative/items/qquickshadereffectsource_p.h create mode 100644 src/declarative/items/qquicksprite.cpp create mode 100644 src/declarative/items/qquicksprite_p.h create mode 100644 src/declarative/items/qquickspriteengine.cpp create mode 100644 src/declarative/items/qquickspriteengine_p.h create mode 100644 src/declarative/items/qquickspriteimage.cpp create mode 100644 src/declarative/items/qquickspriteimage_p.h create mode 100644 src/declarative/items/qquickstateoperations.cpp create mode 100644 src/declarative/items/qquickstateoperations_p.h create mode 100644 src/declarative/items/qquicktext.cpp create mode 100644 src/declarative/items/qquicktext_p.h create mode 100644 src/declarative/items/qquicktext_p_p.h create mode 100644 src/declarative/items/qquicktextedit.cpp create mode 100644 src/declarative/items/qquicktextedit_p.h create mode 100644 src/declarative/items/qquicktextedit_p_p.h create mode 100644 src/declarative/items/qquicktextinput.cpp create mode 100644 src/declarative/items/qquicktextinput_p.h create mode 100644 src/declarative/items/qquicktextinput_p_p.h create mode 100644 src/declarative/items/qquicktextnode.cpp create mode 100644 src/declarative/items/qquicktextnode_p.h create mode 100644 src/declarative/items/qquicktranslate.cpp create mode 100644 src/declarative/items/qquicktranslate_p.h create mode 100644 src/declarative/items/qquickview.cpp create mode 100644 src/declarative/items/qquickview.h create mode 100644 src/declarative/items/qquickview_p.h create mode 100644 src/declarative/items/qquickvisualadaptormodel.cpp create mode 100644 src/declarative/items/qquickvisualadaptormodel_p.h create mode 100644 src/declarative/items/qquickvisualdatamodel.cpp create mode 100644 src/declarative/items/qquickvisualdatamodel_p.h create mode 100644 src/declarative/items/qquickvisualitemmodel.cpp create mode 100644 src/declarative/items/qquickvisualitemmodel_p.h delete mode 100644 src/declarative/items/qsganchors.cpp delete mode 100644 src/declarative/items/qsganchors_p.h delete mode 100644 src/declarative/items/qsganchors_p_p.h delete mode 100644 src/declarative/items/qsganimatedimage.cpp delete mode 100644 src/declarative/items/qsganimatedimage_p.h delete mode 100644 src/declarative/items/qsganimatedimage_p_p.h delete mode 100644 src/declarative/items/qsganimation.cpp delete mode 100644 src/declarative/items/qsganimation_p.h delete mode 100644 src/declarative/items/qsganimation_p_p.h delete mode 100644 src/declarative/items/qsgborderimage.cpp delete mode 100644 src/declarative/items/qsgborderimage_p.h delete mode 100644 src/declarative/items/qsgborderimage_p_p.h delete mode 100644 src/declarative/items/qsgcanvas.cpp delete mode 100644 src/declarative/items/qsgcanvas.h delete mode 100644 src/declarative/items/qsgcanvas_p.h delete mode 100644 src/declarative/items/qsgclipnode.cpp delete mode 100644 src/declarative/items/qsgclipnode_p.h delete mode 100644 src/declarative/items/qsgdrag.cpp delete mode 100644 src/declarative/items/qsgdrag_p.h delete mode 100644 src/declarative/items/qsgdroparea.cpp delete mode 100644 src/declarative/items/qsgdroparea_p.h delete mode 100644 src/declarative/items/qsgevents.cpp delete mode 100644 src/declarative/items/qsgevents_p_p.h delete mode 100644 src/declarative/items/qsgflickable.cpp delete mode 100644 src/declarative/items/qsgflickable_p.h delete mode 100644 src/declarative/items/qsgflickable_p_p.h delete mode 100644 src/declarative/items/qsgflipable.cpp delete mode 100644 src/declarative/items/qsgflipable_p.h delete mode 100644 src/declarative/items/qsgfocusscope.cpp delete mode 100644 src/declarative/items/qsgfocusscope_p.h delete mode 100644 src/declarative/items/qsggridview.cpp delete mode 100644 src/declarative/items/qsggridview_p.h delete mode 100644 src/declarative/items/qsgimage.cpp delete mode 100644 src/declarative/items/qsgimage_p.h delete mode 100644 src/declarative/items/qsgimage_p_p.h delete mode 100644 src/declarative/items/qsgimagebase.cpp delete mode 100644 src/declarative/items/qsgimagebase_p.h delete mode 100644 src/declarative/items/qsgimagebase_p_p.h delete mode 100644 src/declarative/items/qsgimplicitsizeitem.cpp delete mode 100644 src/declarative/items/qsgimplicitsizeitem_p.h delete mode 100644 src/declarative/items/qsgimplicitsizeitem_p_p.h delete mode 100644 src/declarative/items/qsgitem.cpp delete mode 100644 src/declarative/items/qsgitem.h delete mode 100644 src/declarative/items/qsgitem_p.h delete mode 100644 src/declarative/items/qsgitemchangelistener_p.h delete mode 100644 src/declarative/items/qsgitemsmodule.cpp delete mode 100644 src/declarative/items/qsgitemsmodule_p.h delete mode 100644 src/declarative/items/qsgitemview.cpp delete mode 100644 src/declarative/items/qsgitemview_p.h delete mode 100644 src/declarative/items/qsgitemview_p_p.h delete mode 100644 src/declarative/items/qsglistview.cpp delete mode 100644 src/declarative/items/qsglistview_p.h delete mode 100644 src/declarative/items/qsgloader.cpp delete mode 100644 src/declarative/items/qsgloader_p.h delete mode 100644 src/declarative/items/qsgloader_p_p.h delete mode 100644 src/declarative/items/qsgmousearea.cpp delete mode 100644 src/declarative/items/qsgmousearea_p.h delete mode 100644 src/declarative/items/qsgmousearea_p_p.h delete mode 100644 src/declarative/items/qsgninepatchnode.cpp delete mode 100644 src/declarative/items/qsgninepatchnode_p.h delete mode 100644 src/declarative/items/qsgpainteditem.cpp delete mode 100644 src/declarative/items/qsgpainteditem.h delete mode 100644 src/declarative/items/qsgpainteditem_p.h delete mode 100644 src/declarative/items/qsgpathview.cpp delete mode 100644 src/declarative/items/qsgpathview_p.h delete mode 100644 src/declarative/items/qsgpathview_p_p.h delete mode 100644 src/declarative/items/qsgpincharea.cpp delete mode 100644 src/declarative/items/qsgpincharea_p.h delete mode 100644 src/declarative/items/qsgpincharea_p_p.h delete mode 100644 src/declarative/items/qsgpositioners.cpp delete mode 100644 src/declarative/items/qsgpositioners_p.h delete mode 100644 src/declarative/items/qsgpositioners_p_p.h delete mode 100644 src/declarative/items/qsgrectangle.cpp delete mode 100644 src/declarative/items/qsgrectangle_p.h delete mode 100644 src/declarative/items/qsgrectangle_p_p.h delete mode 100644 src/declarative/items/qsgrepeater.cpp delete mode 100644 src/declarative/items/qsgrepeater_p.h delete mode 100644 src/declarative/items/qsgrepeater_p_p.h delete mode 100644 src/declarative/items/qsgscalegrid.cpp delete mode 100644 src/declarative/items/qsgscalegrid_p_p.h delete mode 100644 src/declarative/items/qsgshadereffect.cpp delete mode 100644 src/declarative/items/qsgshadereffect_p.h delete mode 100644 src/declarative/items/qsgshadereffectmesh.cpp delete mode 100644 src/declarative/items/qsgshadereffectmesh_p.h delete mode 100644 src/declarative/items/qsgshadereffectnode.cpp delete mode 100644 src/declarative/items/qsgshadereffectnode_p.h delete mode 100644 src/declarative/items/qsgshadereffectsource.cpp delete mode 100644 src/declarative/items/qsgshadereffectsource_p.h delete mode 100644 src/declarative/items/qsgsprite.cpp delete mode 100644 src/declarative/items/qsgsprite_p.h delete mode 100644 src/declarative/items/qsgspriteengine.cpp delete mode 100644 src/declarative/items/qsgspriteengine_p.h delete mode 100644 src/declarative/items/qsgspriteimage.cpp delete mode 100644 src/declarative/items/qsgspriteimage_p.h delete mode 100644 src/declarative/items/qsgstateoperations.cpp delete mode 100644 src/declarative/items/qsgstateoperations_p.h delete mode 100644 src/declarative/items/qsgtext.cpp delete mode 100644 src/declarative/items/qsgtext_p.h delete mode 100644 src/declarative/items/qsgtext_p_p.h delete mode 100644 src/declarative/items/qsgtextedit.cpp delete mode 100644 src/declarative/items/qsgtextedit_p.h delete mode 100644 src/declarative/items/qsgtextedit_p_p.h delete mode 100644 src/declarative/items/qsgtextinput.cpp delete mode 100644 src/declarative/items/qsgtextinput_p.h delete mode 100644 src/declarative/items/qsgtextinput_p_p.h delete mode 100644 src/declarative/items/qsgtextnode.cpp delete mode 100644 src/declarative/items/qsgtextnode_p.h delete mode 100644 src/declarative/items/qsgtranslate.cpp delete mode 100644 src/declarative/items/qsgtranslate_p.h delete mode 100644 src/declarative/items/qsgview.cpp delete mode 100644 src/declarative/items/qsgview.h delete mode 100644 src/declarative/items/qsgview_p.h delete mode 100644 src/declarative/items/qsgvisualadaptormodel.cpp delete mode 100644 src/declarative/items/qsgvisualadaptormodel_p.h delete mode 100644 src/declarative/items/qsgvisualdatamodel.cpp delete mode 100644 src/declarative/items/qsgvisualdatamodel_p.h delete mode 100644 src/declarative/items/qsgvisualitemmodel.cpp delete mode 100644 src/declarative/items/qsgvisualitemmodel_p.h (limited to 'src/declarative/items') diff --git a/src/declarative/items/context2d/context2d.pri b/src/declarative/items/context2d/context2d.pri index 31ed75d82a..60b3e4b0c1 100644 --- a/src/declarative/items/context2d/context2d.pri +++ b/src/declarative/items/context2d/context2d.pri @@ -1,16 +1,16 @@ SOURCES += \ - $$PWD/qsgcanvasitem.cpp \ - $$PWD/qsgcontext2d.cpp \ - $$PWD/qsgcontext2dnode.cpp \ - $$PWD/qsgcontext2dtile.cpp \ - $$PWD/qsgcontext2dtexture.cpp \ - $$PWD/qsgcontext2dcommandbuffer.cpp \ + $$PWD/qquickcanvasitem.cpp \ + $$PWD/qquickcontext2d.cpp \ + $$PWD/qquickcontext2dnode.cpp \ + $$PWD/qquickcontext2dtile.cpp \ + $$PWD/qquickcontext2dtexture.cpp \ + $$PWD/qquickcontext2dcommandbuffer.cpp \ HEADERS += \ - $$PWD/qsgcanvasitem_p.h \ - $$PWD/qsgcontext2d_p.h \ - $$PWD/qsgcontext2dnode_p.h \ - $$PWD/qsgcontext2dtile_p.h \ - $$PWD/qsgcontext2dtexture_p.h \ - $$PWD/qsgcontext2dcommandbuffer_p.h \ + $$PWD/qquickcanvasitem_p.h \ + $$PWD/qquickcontext2d_p.h \ + $$PWD/qquickcontext2dnode_p.h \ + $$PWD/qquickcontext2dtile_p.h \ + $$PWD/qquickcontext2dtexture_p.h \ + $$PWD/qquickcontext2dcommandbuffer_p.h \ diff --git a/src/declarative/items/context2d/qquickcanvasitem.cpp b/src/declarative/items/context2d/qquickcanvasitem.cpp new file mode 100644 index 0000000000..86db5e616c --- /dev/null +++ b/src/declarative/items/context2d/qquickcanvasitem.cpp @@ -0,0 +1,728 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qquickcanvasitem_p.h" +#include +#include "qquickcontext2d_p.h" +#include "qquickcontext2dnode_p.h" +#include "qquickcontext2dtexture_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickCanvasItemPrivate : public QQuickItemPrivate +{ +public: + QQuickCanvasItemPrivate(); + ~QQuickCanvasItemPrivate(); + QQuickContext2D* context; + QQuickContext2DTexture* texture; + QSizeF canvasSize; + QSize tileSize; + QRectF canvasWindow; + QRectF dirtyRect; + uint renderInThread : 1; + uint hasCanvasSize :1; + uint hasTileSize :1; + uint hasCanvasWindow :1; + uint componentCompleted :1; + QQuickCanvasItem::RenderTarget renderTarget; + QHash images; + QUrl baseUrl; +}; + +QQuickCanvasItemPrivate::QQuickCanvasItemPrivate() + : QQuickItemPrivate() + , context(0) + , texture(0) + , canvasSize(1, 1) + , tileSize(1, 1) + , renderInThread(false) + , hasCanvasSize(false) + , hasTileSize(false) + , hasCanvasWindow(false) + , componentCompleted(false) + , renderTarget(QQuickCanvasItem::FramebufferObject) +{ +} + +QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate() +{ + qDeleteAll(images); +} + +/*! + \qmlclass Canvas QQuickCanvasItem + \inqmlmodule QtQuick 2 + \since QtQuick 2.0 + \brief The Canvas item provides HTML5 like canvas element which enables you to + draw within the item area by using Javascript. + \inherits Item + \ingroup qml-basic-visual-elements + + With the Canvas item, users can draw straight and curved lines, simple and + complex shapes, graphs, and referenced graphic images. can also add texts, colors, + shadows, gradients, and patterns, and do low level pixel operations, etc. The Canvas item + also enables you to save or export the canvas as a image file or serialize the image data + to data url string. + + To define a drawing area in the Canvas item, just set the \c width and \c height properties. + For example, the following code creates a Canvas item which has a drawing area with a height of 100 + pixels and width of 200 pixels: + \qml + import QtQuick 2.0 + Canvas { + id:mycanvas + width:100 + height:200 + } + \endqml + + Currently the Canvas item only supports the two-dimensional rendering context. + + \section1 Thread Rendering and Render Target + The Canvas item supports two render targets:Canvas.Image and Canvas.FramebufferObject. + The Canvas.Image render target is a \a QImage object which is actually a block of system + memory. This render target support background thread rendering. So if some complex or long + running painting need to be done, the Canvas.Image with thread rendering mode should be + chosen to avoid blocking the UI. Otherwise the Canvas.FramebufferObject render target should + be chosen as it could be much faster with good OpenGL hardware accelaration than rendering into + system memory, especially when the CPU is already very busy. + + The default render target is Canvas.Image and the default renderInThread property is + false. + + \section1 Tiled Canvas + The Canvas item also supports tiled rendering mode by setting the proper canvasSize, tileSize + and the canvasWindow properties. + + With tiled canvas, a virtually very large canvas can be provided by a relatively small canvas + window. The actual memory consumption only relates to the canvas window size. So the canvas size + can be chosen freely as needed. The painting code then doesn't need to worry about the coordinate + system and complex matrix transformations at all. + + As a side effect, by setting a good tile size, the tiles overlapped with the canvas window could be + cached and don't need to redraw, which can improve the performance significantly in some situations. + + \section1 Pixel Operations + The Canvas item support all HTML5 2d context pixel operations. In order to get better + pixel reading/writing performance, the Canvas.Image render target should be chosen. As + for Canvas.FramebufferObject render target, the pixel data need to be exchanged between + the system memory and the graphic card, which can't be benefit from the hardware acceleration + at all. And the OpenGL rendering may synchronise with the V-Sync signal to avoid the + {en.wikipedia.org/wiki/Screen_tearing}{screen tearing} which makes the pixel operations + even slower with the Canvas.FrambufferObject render target. + + \section1 Tips for Porting Existing HTML5 Canvas applications + + Although the Canvas item is provided as a HTML5 like API, and + the canvas context API is as compatible with HTML5 2d context standard + as possible, the working HTML5 canvas applications are still need to + be modified to run in the Canvas item: + \list + \o Removes and replaces all DOM API calls with QML property bindings or Canvas item methods. + \o Removes and replaces all HTML envent handlers with the \a MouseArea item. + \o Changes the setInterval/setTimeout function calls with the \a Timer item. + \o Puts the actual painting code into the \a QtQuick2::Canvas::onPaint handler and triggers the + painting by calling the Canvas's \c markDirty or \c requestPaint methods. + \o For drawing images, loads them by calling the Canvas's loadImage method and then request to paint + them in the onImageLoaded handler. + \endlist + + \sa QtQuick2::Context2D +*/ + +QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent) + : QQuickItem(*(new QQuickCanvasItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickCanvasItem::~QQuickCanvasItem() +{ + Q_D(QQuickCanvasItem); + delete d->context; +} + +/*! + \qmlproperty size QtQuick2::Canvas::canvasSize + Holds the logical canvas size that the context paints on. + + By default, the canvas size is the same size as the current canvas item size. + By setting the canvas size, tile size and canvas window, the Canvas + item can act as a virtual large canvas with many seperately rendered tile rectangle + areas. Only those tiles within the current canvas window would be painted by + the Canvas render engine. + \sa QtQuick2::Canvas::tileSize QtQuick2::Canvas::canvasWindow +*/ +QSizeF QQuickCanvasItem::canvasSize() const +{ + Q_D(const QQuickCanvasItem); + return d->canvasSize; +} + +void QQuickCanvasItem::setCanvasSize(const QSizeF & size) +{ + Q_D(QQuickCanvasItem); + if (d->canvasSize != size) { + d->hasCanvasSize = true; + d->canvasSize = size; + emit canvasSizeChanged(); + polish(); + update(); + } +} + +/*! + \qmlproperty size QtQuick2::Canvas::tileSize + Holds the canvas rendering tile size. + + When the Canvas item in tiled mode by setting the canvas size, tile size and + the canvas window. The canvas render can improve the rendering performance + by rendering and caching tiles instead of rendering the whole canvas everytime. + + Additionally, the canvas size could be infinitely large without allocating more + memories because only those tiles within the current visible region + are actually rendered. + + By default, the tile size is the same with the canvas size. + \sa QtQuick2::Canvas::canvaasSize QtQuick2::Canvas::canvasWindow +*/ +QSize QQuickCanvasItem::tileSize() const +{ + Q_D(const QQuickCanvasItem); + return d->tileSize; +} + +void QQuickCanvasItem::setTileSize(const QSize & size) +{ + Q_D(QQuickCanvasItem); + if (d->tileSize != size) { + d->hasTileSize = true; + d->tileSize = size; + + emit tileSizeChanged(); + polish(); + update(); + } +} + +/*! + \qmlproperty rect QtQuick2::Canvas::canvasWindow + Holds the current canvas visible window. + + By default, the canvas window size is the same as the Canvas item + size with the topleft point as (0, 0). + + If the canvas size is different with the Canvas item size, the Canvas + item can display different visible areas by changing the canvas window's size + and/or position. + \sa QtQuick2::Canvas::canvasSize QtQuick2::Canvas::tileSize +*/ +QRectF QQuickCanvasItem::canvasWindow() const +{ + Q_D(const QQuickCanvasItem); + return d->canvasWindow; +} + +void QQuickCanvasItem::setCanvasWindow(const QRectF& rect) +{ + Q_D(QQuickCanvasItem); + if (d->canvasWindow != rect) { + d->canvasWindow = rect; + + d->hasCanvasWindow = true; + emit canvasWindowChanged(); + polish(); + update(); + } +} + + +QQuickContext2D* QQuickCanvasItem::context() const +{ + Q_D(const QQuickCanvasItem); + return d->context; +} +/*! + \qmlproperty bool QtQuick2::Canvas::renderInThread + Holds the current canvas rendering mode. + + By setting the renderInThread to true, complex and long + running painting can be rendered in a dedicated background + rendering thread to avoid blocking the main GUI. + + Note: Different renderTarget may or may not support the + background rendering thread, if not, the renderInThread + property will be ignored. + + The default value is false. + \sa QtQuick2::Canvas::renderTarget +*/ +bool QQuickCanvasItem::renderInThread() const +{ + Q_D(const QQuickCanvasItem); + return d->renderInThread; +} +/*! + \qmlproperty bool QtQuick2::Canvas::renderTarget + Holds the current canvas render target. + + \list + \o Canvas.Image - render to an in memory image buffer, the render + target supports background rendering. + \o Canvas.FramebufferObject - render to an OpenGL frame buffer, + this render target will ignore the + renderInThread property. The actual + rendering happens in the main QML rendering + process, which may be in a seperate render thread + or in the main GUI thread depends on the platforms. + \endlist + + The default render target is \c Canvas.Image. + \sa QtQuick2::Canvas::renderInThread +*/ +QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const +{ + Q_D(const QQuickCanvasItem); + return d->renderTarget; +} + +void QQuickCanvasItem::setRenderTarget(RenderTarget target) +{ + Q_D(QQuickCanvasItem); + if (d->renderTarget != target) { + d->renderTarget = target; + + if (d->componentCompleted) + createTexture(); + emit renderTargetChanged(); + } +} + +void QQuickCanvasItem::_doPainting(const QRectF& region) +{ + Q_D(QQuickCanvasItem); + emit paint(QDeclarativeV8Handle::fromHandle(d->context->v8value()) + , QQuickContext2DTexture::tiledRect(region, d->tileSize)); + if (d->texture) + d->texture->wake(); +} + +/*! + \qmlproperty bool QtQuick2::Canvas::renderInThread + Holds the current canvas rendering mode. + + When this property is true, all canvas painting commands + are rendered in a background rendering thread, otherwise + the rendering happens in the main GUI thread. + + The default renderInThread value is false. +*/ +void QQuickCanvasItem::setRenderInThread(bool renderInThread) +{ + Q_D(QQuickCanvasItem); + if (d->renderInThread != renderInThread) { + d->renderInThread = renderInThread; + + if (d->componentCompleted) + createTexture(); + + if (d->renderInThread) + connect(this, SIGNAL(painted()), SLOT(update())); + else + disconnect(this, SIGNAL(painted()), this, SLOT(update())); + emit renderInThreadChanged(); + polish(); + update(); + } +} + +void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QQuickCanvasItem); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + const qreal w = newGeometry.width(); + const qreal h = newGeometry.height(); + + if (!d->hasCanvasSize) { + d->canvasSize = QSizeF(w, h); + emit canvasSizeChanged(); + } + + if (!d->hasTileSize) { + d->tileSize = d->canvasSize.toSize(); + emit tileSizeChanged(); + } + + if (!d->hasCanvasWindow) { + d->canvasWindow = newGeometry; + emit canvasWindowChanged(); + } + + polish(); + update(); +} + +void QQuickCanvasItem::componentComplete() +{ + Q_D(QQuickCanvasItem); + QQuickItem::componentComplete(); + + if (!d->context) + createContext(); + createTexture(); + + d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl(); + requestPaint(); + updatePolish(); //force update the canvas sizes to texture for the first time + update(); + d->componentCompleted = true; +} + +void QQuickCanvasItem::updatePolish() +{ + Q_D(QQuickCanvasItem); + QQuickItem::updatePolish(); + if (d->texture) { + if (!d->renderInThread && d->dirtyRect.isValid()) + _doPainting(d->dirtyRect); + + d->texture->canvasChanged(d->canvasSize.toSize() + , d->tileSize + , d->canvasWindow.toAlignedRect() + , d->dirtyRect.toAlignedRect() + , d->smooth); + d->dirtyRect = QRectF(); + } +} + +QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + Q_D(QQuickCanvasItem); + QQuickContext2DNode *node = static_cast(oldNode); + if (!node) + node = new QQuickContext2DNode(this); + + node->setTexture(d->texture); + node->setSize(d->canvasWindow.size()); + node->update(); + return node; +} + +void QQuickCanvasItem::createTexture() +{ + Q_D(QQuickCanvasItem); + + if (!d->texture + || d->texture->threadRendering() != d->renderInThread + || d->texture->renderTarget() != d->renderTarget) { + if (d->texture) { + d->texture->deleteLater(); + d->texture = 0; + } + + if (d->renderTarget == QQuickCanvasItem::Image) { + d->texture = new QQuickContext2DImageTexture(d->renderInThread); + } else if (d->renderTarget == QQuickCanvasItem::FramebufferObject) { + d->texture = new QQuickContext2DFBOTexture(); + } + + if (d->renderInThread && !d->texture->supportThreadRendering()) { + qWarning("Canvas: render target does not support thread rendering, force to non-thread rendering mode."); + d->renderInThread = false; + emit renderInThreadChanged(); + } + + if (d->renderInThread) + connect(d->texture, SIGNAL(textureChanged()), this, SLOT(update())); + + d->texture->setItem(this); + } +} + +void QQuickCanvasItem::createContext() +{ + Q_D(QQuickCanvasItem); + + delete d->context; + + d->context = new QQuickContext2D(this); + + QV8Engine *e = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this)); + d->context->setV8Engine(e); +} + +/*! + \qmlmethod object QtQuick2::Canvas::getContext(string contextId) + + Currently, the canvas item only support the 2D context. If the \a contextId + parameter isn't provided or is "2d", then the QtQuick2::Context2D object is + returned, otherwise returns an invalid value. + */ +QDeclarativeV8Handle QQuickCanvasItem::getContext(const QString &contextId) +{ + Q_D(QQuickCanvasItem); + + if (contextId.toLower() != QLatin1String("2d")) + return QDeclarativeV8Handle::fromHandle(v8::Undefined()); + + if (!d->context) + createContext(); + return QDeclarativeV8Handle::fromHandle(d->context->v8value()); +} + +/*! + \qmlmethod void QtQuick2::Canvas::markDirty(rect region) + + Mark the given \a region as dirty, so that when this region is visible + the canvas render will redraw it. During the rendering process, the + canvas renderer may emit the canvas' "paint" signal so the actual painting + scripts can be putted into the canvas's "onPaint" signal handler function. + + \sa QtQuick2::Canvas::paint QtQuick2::Canvas::requestPaint + */ +void QQuickCanvasItem::markDirty(const QRectF& region) +{ + Q_D(QQuickCanvasItem); + d->dirtyRect |= region; + if (d->componentCompleted) + polish(); + update(); +} + + +/*! + \qmlmethod bool QtQuick2::Canvas::save(string filename) + + Save the current canvas content into an image file \a filename. + The saved image format is automatically decided by the \a filename's + suffix. + + Note: calling this method will force painting the whole canvas, not the + current canvas visible window. + + \sa canvasWindow canvasSize toDataURL + */ +bool QQuickCanvasItem::save(const QString &filename) const +{ + Q_D(const QQuickCanvasItem); + QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename)); + return toImage().save(url.toLocalFile()); +} + +QImage QQuickCanvasItem::loadedImage(const QUrl& url) +{ + Q_D(QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + if (!d->images.contains(fullPathUrl)) { + loadImage(url); + } + QDeclarativePixmap* pix = d->images.value(fullPathUrl); + if (pix->isLoading() || pix->isError()) { + return QImage(); + } + return pix->pixmap().toImage(); +} + +/*! + \qmlmethod void QtQuick2::Canvas::loadImage(url image) + Loads the given \c image asynchronously, when the image is + ready, an imageLoaded signal will be emitted. + The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method. + + Note: Only loaded images can be painted on the Canvas item. + \sa QtQuick2::Canvas::unloadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded + \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage + */ +void QQuickCanvasItem::loadImage(const QUrl& url) +{ + Q_D(QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + if (!d->images.contains(fullPathUrl)) { + QDeclarativePixmap* pix = new QDeclarativePixmap(); + d->images.insert(fullPathUrl, pix); + + pix->load(qmlEngine(this) + , fullPathUrl + , QDeclarativePixmap::Cache | QDeclarativePixmap::Asynchronous); + pix->connectFinished(this, SIGNAL(imageLoaded())); + } +} +/*! + \qmlmethod void QtQuick2::Canvas::loadImage(url image) + Unloads the \c image. + + If the image is unloaded from the Canvas item, it can't be painted by the canvas context + until it's loaded again. + + \sa QtQuick2::Canvas::loadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded + \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage + */ +void QQuickCanvasItem::unloadImage(const QUrl& url) +{ + Q_D(QQuickCanvasItem); + QUrl removeThis = d->baseUrl.resolved(url); + if (d->images.contains(removeThis)) { + delete d->images.value(removeThis); + d->images.remove(removeThis); + } +} + +/*! + \qmlmethod void QtQuick2::Canvas::isImageError(url image) + Returns true if the image can't be loaded because of error happens. + + \sa QtQuick2::Canvas::loadImage + */ +bool QQuickCanvasItem::isImageError(const QUrl& url) const +{ + Q_D(const QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + return d->images.contains(fullPathUrl) + && d->images.value(fullPathUrl)->isError(); +} + +/*! + \qmlmethod void QtQuick2::Canvas::isImageLoading(url image) + Returns true if the Canvas item still is loading the \c image. + + \sa QtQuick2::Canvas::loadImage + */ +bool QQuickCanvasItem::isImageLoading(const QUrl& url) const +{ + Q_D(const QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + return d->images.contains(fullPathUrl) + && d->images.value(fullPathUrl)->isLoading(); +} +/*! + \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image) + Returns true if the \c image is sucessfully loaded and ready to use. + + \sa QtQuick2::Canvas::loadImage + */ +bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const +{ + Q_D(const QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + return d->images.contains(fullPathUrl) + && d->images.value(fullPathUrl)->isReady(); +} + +QImage QQuickCanvasItem::toImage(const QRectF& region) const +{ + Q_D(const QQuickCanvasItem); + if (d->texture) { + if (region.isEmpty()) + return d->texture->toImage(canvasWindow()); + else + return d->texture->toImage(region); + } + return QImage(); +} + +/*! + \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType) + + Returns a data: URL for the image in the canvas. + + The default \a mimeType is "image/png". + + \sa QtQuick2::Canvas::save + */ +QString QQuickCanvasItem::toDataURL(const QString& mimeType) const +{ + QImage image = toImage(); + + if (!image.isNull()) { + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + QString mime = mimeType.toLower(); + QString type; + if (mime == QLatin1Literal("image/png")) { + type = QLatin1Literal("PNG"); + } else if (mime == QLatin1Literal("image/bmp")) + type = QLatin1Literal("BMP"); + else if (mime == QLatin1Literal("image/jpeg")) + type = QLatin1Literal("JPEG"); + else if (mime == QLatin1Literal("image/x-portable-pixmap")) + type = QLatin1Literal("PPM"); + else if (mime == QLatin1Literal("image/tiff")) + type = QLatin1Literal("TIFF"); + else if (mime == QLatin1Literal("image/xpm")) + type = QLatin1Literal("XPM"); + else + return QLatin1Literal("data:,"); + + image.save(&buffer, type.toAscii()); + buffer.close(); + QString dataUrl = QLatin1Literal("data:%1;base64,%2"); + return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData())); + } + return QLatin1Literal("data:,"); +} + +/*! + \qmlsignal QtQuick2::Canvas::onPaint(QtQuick2::Context2D context, rect region) + + This handler is called before the given \c region needs to be rendered. + + This signal can be triggered by QtQuick2::Canvas::markdirty, QtQuick2::Canvas::requestPaint + or by changing the current canvas window. +*/ + +/*! + \qmlsignal QtQuick2::Canvas::onPainted() + + This handler is called after all context painting commands are executed and + the Canvas is actually rendered. +*/ + +QT_END_NAMESPACE diff --git a/src/declarative/items/context2d/qquickcanvasitem_p.h b/src/declarative/items/context2d/qquickcanvasitem_p.h new file mode 100644 index 0000000000..e0261192ed --- /dev/null +++ b/src/declarative/items/context2d/qquickcanvasitem_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCANVASITEM_P_H +#define QQUICKCANVASITEM_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QQuickContext2D; +class QQuickCanvasItemPrivate; +class Q_DECLARATIVE_EXPORT QQuickCanvasItem : public QQuickItem +{ + Q_OBJECT + Q_ENUMS(RenderTarget) + Q_ENUMS(ImageFilterMode) + + Q_PROPERTY(QSizeF canvasSize READ canvasSize WRITE setCanvasSize NOTIFY canvasSizeChanged) + Q_PROPERTY(QSize tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged) + Q_PROPERTY(QRectF canvasWindow READ canvasWindow WRITE setCanvasWindow NOTIFY canvasWindowChanged) + Q_PROPERTY(bool renderInThread READ renderInThread WRITE setRenderInThread NOTIFY renderInThreadChanged) + Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged) +public: + enum RenderTarget { + Image, + FramebufferObject + }; + + enum ImageFilterMode { + Threshold, + Mono, + GrayScale, + Brightness, + Invert, + Blur, + Opaque, + Convolute + }; + + QQuickCanvasItem(QQuickItem *parent = 0); + ~QQuickCanvasItem(); + + QSizeF canvasSize() const; + void setCanvasSize(const QSizeF &); + + QSize tileSize() const; + void setTileSize(const QSize &); + + QRectF canvasWindow() const; + void setCanvasWindow(const QRectF& rect); + + bool renderInThread() const; + void setRenderInThread(bool renderInThread); + + RenderTarget renderTarget() const; + void setRenderTarget(RenderTarget target); + + QQuickContext2D* context() const; + QImage toImage(const QRectF& region = QRectF()) const; + + QImage loadedImage(const QUrl& url); +Q_SIGNALS: + void paint(QDeclarativeV8Handle context, const QRect ®ion); + void painted(); + void canvasSizeChanged(); + void tileSizeChanged(); + void renderInThreadChanged(); + void textureChanged(); + void canvasWindowChanged(); + void renderTargetChanged(); + void imageLoaded(); +public Q_SLOTS: + QString toDataURL(const QString& type = QLatin1String("image/png")) const; + QDeclarativeV8Handle getContext(const QString & = QLatin1String("2d")); + void markDirty(const QRectF& region); + void requestPaint() {markDirty(canvasWindow());} + // Save current canvas to disk + bool save(const QString& filename) const; + void loadImage(const QUrl& url); + void unloadImage(const QUrl& url); + bool isImageLoaded(const QUrl& url) const; + bool isImageLoading(const QUrl& url) const; + bool isImageError(const QUrl& url) const; +private Q_SLOTS: + void _doPainting(const QRectF& region); +protected: + virtual void componentComplete(); + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual void updatePolish(); +private: + void createContext(); + void createTexture(); + Q_DECLARE_PRIVATE(QQuickCanvasItem) + friend class QQuickContext2D; + friend class QQuickContext2DTexture; +}; +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickCanvasItem) + +QT_END_HEADER + +#endif //QQUICKCANVASITEM_P_H diff --git a/src/declarative/items/context2d/qquickcontext2d.cpp b/src/declarative/items/context2d/qquickcontext2d.cpp new file mode 100644 index 0000000000..0c0865b3b3 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2d.cpp @@ -0,0 +1,3534 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontext2d_p.h" +#include "qquickcontext2dcommandbuffer_p.h" +#include "qquickcanvasitem_p.h" +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +/*! + \qmlclass Context2D QQuickContext2D + \inqmlmodule QtQuick 2 + \since QtQuick 2.0 + \brief The Context2D API allows you to draw 2d graphic shapes on the \c Canvas item. + + The Context2D object can be created by \c Canvas item's \c getContext() method: + \code + Canvas { + id:canvas + onPaint:{ + var ctx = canvas.getContext('2d'); + //... + } + } + \endcode + The Context2D API implements the same \l {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard} + with some enhanced features. + + The Context2D API provides the rendering \bold{context} which defines the methods and attributes needed to draw + on the \c Canvas item. The following assigns the canvas rendering context to a \c{context} + variable: + \code + var context = mycanvas.getContext("2d") + \endcode + + The Context2D API renders the canvas as a coordinate system whose origin (0,0) is + at the top left corner, as shown in the figure below. Coordinates increase along + the \c{x} axis from left to right and along the \c{y} axis from top to bottom of + the canvas. + \image qml-item-canvas-context.gif +*/ +static const double Q_PI = 3.14159265358979323846; // pi + +#define DEGREES(t) ((t) * 180.0 / Q_PI) + +#define CHECK_CONTEXT(r) if (!r || !r->context || !r->context->buffer()) \ + V8THROW_ERROR("Not a Context2D object"); + +#define CHECK_CONTEXT_SETTER(r) if (!r || !r->context || !r->context->buffer()) \ + V8THROW_ERROR_SETTER("Not a Context2D object"); +#define qClamp(val, min, max) qMin(qMax(val, min), max) +#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9)) +QColor qt_color_from_string(v8::Local name) +{ + v8::String::AsciiValue str(name); + + char *p = *str; + int len = str.length(); + //rgb/hsl color string has at least 7 characters + if (!p || len > 255 || len <= 7) + return QColor(p); + else { + bool isRgb(false), isHsl(false), hasAlpha(false); + + while (isspace(*p)) p++; + if (strncmp(p, "rgb", 3) == 0) + isRgb = true; + else if (strncmp(p, "hsl", 3) == 0) + isHsl = true; + else + return QColor(p); + + p+=3; //skip "rgb" or "hsl" + hasAlpha = (*p == 'a') ? true : false; + + ++p; //skip "(" + + if (hasAlpha) ++p; //skip "a" + + int rh, gs, bl, alpha = 255; + + //red + while (isspace(*p)) p++; + rh = strtol(p, &p, 10); + if (*p == '%') { + rh = qRound(rh/100.0 * 255); + ++p; + } + if (*p++ != ',') return QColor(); + + //green + while (isspace(*p)) p++; + gs = strtol(p, &p, 10); + if (*p == '%') { + gs = qRound(gs/100.0 * 255); + ++p; + } + if (*p++ != ',') return QColor(); + + //blue + while (isspace(*p)) p++; + bl = strtol(p, &p, 10); + if (*p == '%') { + bl = qRound(bl/100.0 * 255); + ++p; + } + + if (hasAlpha) { + if (*p++!= ',') return QColor(); + while (isspace(*p)) p++; + alpha = qRound(strtod(p, &p) * 255); + } + + if (*p != ')') return QColor(); + if (isRgb) + return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255))); + else + return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)); + } + return QColor(); +} + +QFont qt_font_from_string(const QString& fontString) { + QFont font; + // ### this is simplified and incomplete + // ### TODO:get code from Qt webkit + QStringList tokens = fontString.split(QLatin1String(" ")); + foreach (const QString &token, tokens) { + if (token == QLatin1String("italic")) + font.setItalic(true); + else if (token == QLatin1String("bold")) + font.setBold(true); + else if (token.endsWith(QLatin1String("px"))) { + QString number = token; + number.remove(QLatin1String("px")); + //font.setPointSizeF(number.trimmed().toFloat()); + font.setPixelSize(number.trimmed().toInt()); + } else + font.setFamily(token); + } + + return font; +} + + + +class QQuickContext2DEngineData : public QV8Engine::Deletable +{ +public: + QQuickContext2DEngineData(QV8Engine *engine); + ~QQuickContext2DEngineData(); + + v8::Persistent constructorContext; + v8::Persistent constructorGradient; + v8::Persistent constructorPattern; + v8::Persistent constructorPixelArray; + v8::Persistent constructorImageData; +}; + +V8_DEFINE_EXTENSION(QQuickContext2DEngineData, engineData) + +class QV8Context2DResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(Context2DType) +public: + QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e) {} + QQuickContext2D* context; +}; + +class QV8Context2DStyleResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(Context2DStyleType) +public: + QV8Context2DStyleResource(QV8Engine *e) + : QV8ObjectResource(e) + , patternRepeatX(false) + , patternRepeatY(false) + {} + QBrush brush; + bool patternRepeatX:1; + bool patternRepeatY:1; +}; + +class QV8Context2DPixelArrayResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(Context2DPixelArrayType) +public: + QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {} + + QImage image; +}; + +QImage qt_image_convolute_filter(const QImage& src, const QVector& weights, int radius = 0) +{ + int sides = radius ? radius : qRound(qSqrt(weights.size())); + int half = qFloor(sides/2); + + QImage dst = QImage(src.size(), src.format()); + int w = src.width(); + int h = src.height(); + for (int y = 0; y < dst.height(); ++y) { + QRgb *dr = (QRgb*)dst.scanLine(y); + for (int x = 0; x < dst.width(); ++x) { + unsigned char* dRgb = ((unsigned char*)&dr[x]); + unsigned char red=0, green=0, blue=0, alpha=0; + int sy = y; + int sx = x; + + for (int cy=0; cy= 0 && scy < w && scx >= 0 && scx < h) { + const QRgb *sr = (const QRgb*)(src.constScanLine(scy)); + const unsigned char* sRgb = ((const unsigned char*)&sr[scx]); + qreal wt = radius ? weights[0] : weights[cy*sides+cx]; + red += sRgb[0] * wt; + green += sRgb[1] * wt; + blue += sRgb[2] * wt; + alpha += sRgb[3] * wt; + } + } + } + dRgb[0] = red; + dRgb[1] = green; + dRgb[2] = blue; + dRgb[3] = alpha; + } + } + return dst; +} + +void qt_image_boxblur(QImage& image, int radius, bool quality) +{ + int passes = quality? 3: 1; + for (int i=0; i < passes; i++) { + image = qt_image_convolute_filter(image, QVector() << 1.0/(radius * radius * 1.0), radius); + } +} + +static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator) +{ + if (compositeOperator == QLatin1String("source-over")) { + return QPainter::CompositionMode_SourceOver; + } else if (compositeOperator == QLatin1String("source-out")) { + return QPainter::CompositionMode_SourceOut; + } else if (compositeOperator == QLatin1String("source-in")) { + return QPainter::CompositionMode_SourceIn; + } else if (compositeOperator == QLatin1String("source-atop")) { + return QPainter::CompositionMode_SourceAtop; + } else if (compositeOperator == QLatin1String("destination-atop")) { + return QPainter::CompositionMode_DestinationAtop; + } else if (compositeOperator == QLatin1String("destination-in")) { + return QPainter::CompositionMode_DestinationIn; + } else if (compositeOperator == QLatin1String("destination-out")) { + return QPainter::CompositionMode_DestinationOut; + } else if (compositeOperator == QLatin1String("destination-over")) { + return QPainter::CompositionMode_DestinationOver; + } else if (compositeOperator == QLatin1String("lighter")) { + return QPainter::CompositionMode_Lighten; + } else if (compositeOperator == QLatin1String("copy")) { + return QPainter::CompositionMode_Source; + } else if (compositeOperator == QLatin1String("xor")) { + return QPainter::CompositionMode_Xor; + } else if (compositeOperator == QLatin1String("qt-clear")) { + return QPainter::CompositionMode_Clear; + } else if (compositeOperator == QLatin1String("qt-destination")) { + return QPainter::CompositionMode_Destination; + } else if (compositeOperator == QLatin1String("qt-multiply")) { + return QPainter::CompositionMode_Multiply; + } else if (compositeOperator == QLatin1String("qt-screen")) { + return QPainter::CompositionMode_Screen; + } else if (compositeOperator == QLatin1String("qt-overlay")) { + return QPainter::CompositionMode_Overlay; + } else if (compositeOperator == QLatin1String("qt-darken")) { + return QPainter::CompositionMode_Darken; + } else if (compositeOperator == QLatin1String("qt-lighten")) { + return QPainter::CompositionMode_Lighten; + } else if (compositeOperator == QLatin1String("qt-color-dodge")) { + return QPainter::CompositionMode_ColorDodge; + } else if (compositeOperator == QLatin1String("qt-color-burn")) { + return QPainter::CompositionMode_ColorBurn; + } else if (compositeOperator == QLatin1String("qt-hard-light")) { + return QPainter::CompositionMode_HardLight; + } else if (compositeOperator == QLatin1String("qt-soft-light")) { + return QPainter::CompositionMode_SoftLight; + } else if (compositeOperator == QLatin1String("qt-difference")) { + return QPainter::CompositionMode_Difference; + } else if (compositeOperator == QLatin1String("qt-exclusion")) { + return QPainter::CompositionMode_Exclusion; + } + return QPainter::CompositionMode_SourceOver; +} + +static QString qt_composite_mode_to_string(QPainter::CompositionMode op) +{ + switch (op) { + case QPainter::CompositionMode_SourceOver: + return QLatin1String("source-over"); + case QPainter::CompositionMode_DestinationOver: + return QLatin1String("destination-over"); + case QPainter::CompositionMode_Clear: + return QLatin1String("qt-clear"); + case QPainter::CompositionMode_Source: + return QLatin1String("copy"); + case QPainter::CompositionMode_Destination: + return QLatin1String("qt-destination"); + case QPainter::CompositionMode_SourceIn: + return QLatin1String("source-in"); + case QPainter::CompositionMode_DestinationIn: + return QLatin1String("destination-in"); + case QPainter::CompositionMode_SourceOut: + return QLatin1String("source-out"); + case QPainter::CompositionMode_DestinationOut: + return QLatin1String("destination-out"); + case QPainter::CompositionMode_SourceAtop: + return QLatin1String("source-atop"); + case QPainter::CompositionMode_DestinationAtop: + return QLatin1String("destination-atop"); + case QPainter::CompositionMode_Xor: + return QLatin1String("xor"); + case QPainter::CompositionMode_Plus: + return QLatin1String("plus"); + case QPainter::CompositionMode_Multiply: + return QLatin1String("qt-multiply"); + case QPainter::CompositionMode_Screen: + return QLatin1String("qt-screen"); + case QPainter::CompositionMode_Overlay: + return QLatin1String("qt-overlay"); + case QPainter::CompositionMode_Darken: + return QLatin1String("qt-darken"); + case QPainter::CompositionMode_Lighten: + return QLatin1String("lighter"); + case QPainter::CompositionMode_ColorDodge: + return QLatin1String("qt-color-dodge"); + case QPainter::CompositionMode_ColorBurn: + return QLatin1String("qt-color-burn"); + case QPainter::CompositionMode_HardLight: + return QLatin1String("qt-hard-light"); + case QPainter::CompositionMode_SoftLight: + return QLatin1String("qt-soft-light"); + case QPainter::CompositionMode_Difference: + return QLatin1String("qt-difference"); + case QPainter::CompositionMode_Exclusion: + return QLatin1String("qt-exclusion"); + default: + break; + } + return QString(); +} + + +static v8::Local qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image) +{ + QQuickContext2DEngineData *ed = engineData(engine); + v8::Local imageData = ed->constructorImageData->NewInstance(); + QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine); + if (image.isNull()) { + r->image = QImage(w, h, QImage::Format_ARGB32); + r->image.fill(0x00000000); + } else { + Q_ASSERT(image.width() == w && image.height() == h); + r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32); + } + v8::Local pixelData = ed->constructorPixelArray->NewInstance(); + pixelData->SetExternalResource(r); + + imageData->SetInternalField(0, pixelData); + return imageData; +} + +//static script functions + +/*! + \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas + Holds the canvas item that the context paints on. + + This property is read only. +*/ +static v8::Handle ctx2d_canvas(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + return engine->newQObject(r->context->canvas()); +} + +/*! + \qmlmethod object QtQuick2::Context2D::restore() + Pops the top state on the stack, restoring the context to that state. + + \sa QtQuick2::Context2D::save() +*/ +static v8::Handle ctx2d_restore(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + r->context->popState(); + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::reset() + Resets the context state and properties to the default values. +*/ +static v8::Handle ctx2d_reset(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + r->context->reset(); + r->context->m_path = QPainterPath(); + r->context->m_path.setFillRule(Qt::WindingFill); + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::save() + Pushes the current state onto the state stack. + + Before changing any state attributes, you should save the current state + for future reference. The context maintains a stack of drawing states. + Each state consists of the current transformation matrix, clipping region, + and values of the following attributes: + \list + \o\a QtQuick2::Context2D::strokeStyle + \o\a QtQuick2::Context2D::fillStyle + \o\a QtQuick2::Context2D::fillRule + \o\a QtQuick2::Context2D::globalAlpha + \o\a QtQuick2::Context2D::lineWidth + \o\a QtQuick2::Context2D::lineCap + \o\a QtQuick2::Context2D::lineJoin + \o\a QtQuick2::Context2D::miterLimit + \o\a QtQuick2::Context2D::shadowOffsetX + \o\a QtQuick2::Context2D::shadowOffsetY + \o\a QtQuick2::Context2D::shadowBlur + \o\a QtQuick2::Context2D::shadowColor + \o\a QtQuick2::Context2D::globalCompositeOperation + \o\a QtQuick2::Context2D::font + \o\a QtQuick2::Context2D::textAlign + \o\a QtQuick2::Context2D::textBaseline + \endlist + + The current path is NOT part of the drawing state. The path can be reset by + invoking the \a QtQuick2::Context2D::beginPath() method. +*/ +static v8::Handle ctx2d_save(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + r->context->pushState(); + + return args.This(); +} + +// transformations +/*! + \qmlmethod object QtQuick2::Context2D::rotate(real angle) + Rotate the canvas around the current origin by \c angle in radians and clockwise direction. + \code + ctx.rotate(Math.PI/2); + \endcode + \image qml-item-canvas-rotate.png + + The rotation transformation matrix is as follows: + + \image qml-item-canvas-math-rotate.png + + where the \c angle of rotation is in radians. + +*/ +static v8::Handle ctx2d_rotate(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + if (args.Length() == 1) { + qreal angle = args[0]->NumberValue(); + if (!qIsFinite(angle)) + return args.This(); + + r->context->state.matrix.rotate(DEGREES(angle)); + r->context->buffer()->updateMatrix(r->context->state.matrix); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::scale(real x, real y) + Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors + to the current tranform matrix. + Where \c x is the scale factor in the horizontal direction and \c y is the scale factor in the + vertical direction. + The following code doubles the horizontal size of an object drawn on the canvas and half its + vertical size: + \code + ctx.scale(2.0, 0.5); + \endcode + \image qml-item-canvas-scale.png + +*/ +static v8::Handle ctx2d_scale(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 2) { + qreal x, y; + x = args[0]->NumberValue(); + y = args[1]->NumberValue(); + if (!qIsFinite(x) || !qIsFinite(y)) + return args.This(); + + r->context->state.matrix.scale(x, y); + r->context->buffer()->updateMatrix(r->context->state.matrix); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f) + Changes the transformation matrix to the matrix given by the arguments as described below. + + Modifying the transformation matrix directly enables you to perform scaling, + rotating, and translating transformations in a single step. + + Each point on the canvas is multiplied by the matrix before anything is + drawn. The \l{HTML5 Canvas API} defines the transformation matrix as: + + \image qml-item-canvas-math.png + where: + \list + \o \c{a} is the scale factor in the horizontal (x) direction + \image qml-item-canvas-scalex.png + \o \c{c} is the skew factor in the x direction + \image qml-item-canvas-canvas-skewx.png + \o \c{e} is the translation in the x direction + \image qml-item-canvas-canvas-translate.png + \o \c{b} is the skew factor in the y (vertical) direction + \image qml-item-canvas-canvas-skewy.png + \o \c{d} is the scale factor in the y direction + \image qml-item-canvas-canvas-scaley.png + \o \c{f} is the translation in the y direction + \image qml-item-canvas-canvas-translatey.png + \o the last row remains constant + \endlist + The scale factors and skew factors are multiples; \c{e} and \c{f} are + coordinate space units, just like the units in the \a QtQuick2::Context2D::translate(x,y) + method. + + \sa QtQuick2::Context2D::transform() +*/ +static v8::Handle ctx2d_setTransform(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 6) { + qreal a = args[0]->NumberValue(); + qreal b = args[1]->NumberValue(); + qreal c = args[2]->NumberValue(); + qreal d = args[3]->NumberValue(); + qreal e = args[4]->NumberValue(); + qreal f = args[5]->NumberValue(); + + if (!qIsFinite(a) + || !qIsFinite(b) + || !qIsFinite(c) + || !qIsFinite(d) + || !qIsFinite(e) + || !qIsFinite(f)) + return args.This(); + + r->context->state.matrix = QTransform(a, b, c, d, e, f); + r->context->buffer()->updateMatrix(r->context->state.matrix); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f) + This method is very similar to \a QtQuick2::Context2D::setTransform(), but instead of replacing the old + tranform matrix, this method applies the given tranform matrix to the current matrix by mulitplying to it. + + The \a setTransform(a, b, c, d, e, f) method actually resets the current transform to the identity matrix, + and then invokes the transform(a, b, c, d, e, f) method with the same arguments. + + \sa QtQuick2::Context2D::setTransform() +*/ +static v8::Handle ctx2d_transform(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 6) { + qreal a = args[0]->NumberValue(); + qreal b = args[1]->NumberValue(); + qreal c = args[2]->NumberValue(); + qreal d = args[3]->NumberValue(); + qreal e = args[4]->NumberValue(); + qreal f = args[5]->NumberValue(); + + if (!qIsFinite(a) + || !qIsFinite(b) + || !qIsFinite(c) + || !qIsFinite(d) + || !qIsFinite(e) + || !qIsFinite(f)) + return args.This(); + + r->context->state.matrix *= QTransform(a, b, c, d, e, f); + r->context->buffer()->updateMatrix(r->context->state.matrix); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::translate(real x, real y) + Translates the origin of the canvas to point (\c x, \c y). + + \c x is the horizontal distance that the origin is translated, in coordinate space units, + \c y is the vertical distance that the origin is translated, in coordinate space units. + Translating the origin enables you to draw patterns of different objects on the canvas + without having to measure the coordinates manually for each shape. +*/ +static v8::Handle ctx2d_translate(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 2) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y)) + return args.This(); + + r->context->state.matrix.translate(x, y); + r->context->buffer()->updateMatrix(r->context->state.matrix); + } + + return args.This(); +} + + +/*! + \qmlmethod object QtQuick2::Context2D::resetTransform() + Reset the transformation matrix to default value. + + \sa QtQuick2::Context2D::transform(), QtQuick2::Context2D::setTransform(), QtQuick2::Context2D::reset() +*/ +static v8::Handle ctx2d_resetTransform(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + r->context->state.matrix = QTransform(); + r->context->buffer()->updateMatrix(r->context->state.matrix); + + return args.This(); +} + + +/*! + \qmlmethod object QtQuick2::Context2D::shear(real sh, real sv ) + Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction. +*/ +static v8::Handle ctx2d_shear(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + if (args.Length() == 2) { + qreal sh = args[0]->NumberValue(); + qreal sv = args[1]->NumberValue(); + + if (!qIsFinite(sh) || !qIsFinite(sv)) + return args.This(); + + r->context->state.matrix.shear(sh, sv); + r->context->buffer()->updateMatrix(r->context->state.matrix); + } + return args.This(); +} +// compositing + +/*! + \qmlproperty real QtQuick2::Context2D::globalAlpha + Holds the the current alpha value applied to rendering operations. + The value must be in the range from 0.0 (fully transparent) to 1.0 (fully opque). + The default value is 1.0. +*/ +static v8::Handle ctx2d_globalAlpha(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + return v8::Number::New(r->context->state.globalAlpha); +} + +static void ctx2d_globalAlpha_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + qreal globalAlpha = value->NumberValue(); + + if (!qIsFinite(globalAlpha)) + return; + + if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) { + r->context->state.globalAlpha = globalAlpha; + r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha); + } +} + +/*! + \qmlproperty string QtQuick2::Context2D::globalCompositeOperation + Holds the the current the current composition operation, from the list below: + \list + \o source-atop - A atop B. Display the source image wherever both images are opaque. + Display the destination image wherever the destination image is opaque but the source image is transparent. + Display transparency elsewhere. + \o source-in - A in B. Display the source image wherever both the source image and destination image are opaque. + Display transparency elsewhere. + \o source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent. + Display transparency elsewhere. + \o source-over - (default) A over B. Display the source image wherever the source image is opaque. + Display the destination image elsewhere. + \o destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa. + \o destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa. + \o destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa. + \o destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa. + \o lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit. + \o copy - A (B is ignored). Display the source image instead of the destination image. + \o xor - A xor B. Exclusive OR of the source image and destination image. + \endlist + + Additionally, this property also accepts the compositon modes listed in \a {QPainter::CompositionMode}. According to the W3C standard, these + extension composition modes are provided as "vendorName-operationName" syntax, for example: \c {QPainter::CompositionMode_Exclusion} is porvided as + "qt-exclusion". +*/ +static v8::Handle ctx2d_globalCompositeOperation(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation)); +} + +static void ctx2d_globalCompositeOperation_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + + QString mode = engine->toString(value); + QPainter::CompositionMode cm = qt_composite_mode_from_string(mode); + if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over")) + return; + + if (cm != r->context->state.globalCompositeOperation) { + r->context->state.globalCompositeOperation = cm; + r->context->buffer()->setGlobalCompositeOperation(cm); + } +} + +// colors and styles +/*! + \qmlproperty variant QtQuick2::Context2D::fillStyle + Holds the current style used for filling shapes. + The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored. + This property accepts several color syntaxes: + \list + \o 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)' + \o 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)' + \o 'hsl(hue, saturation, lightness)' + \o 'hsla(hue, saturation, lightness, alpha)' + \o '#RRGGBB' - for example: '#00FFCC' + \o Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0) + \endlist + If the \a fillStyle or \a strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the + best performance, because it's already a valid QColor value, does not need to be parsed everytime. + + The default value is '#000000'. + \sa QtQuick2::Context2D::createLinearGradient + \sa QtQuick2::Context2D::createRadialGradient + \sa QtQuick2::Context2D::createPattern + \sa QtQuick2::Context2D::strokeStyle + */ +static v8::Handle ctx2d_fillStyle(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + QColor color = r->context->state.fillStyle.color(); + if (color.isValid()) { + if (color.alpha() == 255) + return engine->toString(color.name()); + QString alphaString = QString::number(color.alphaF(), 'f'); + while (alphaString.endsWith(QLatin1Char('0'))) + alphaString.chop(1); + if (alphaString.endsWith(QLatin1Char('.'))) + alphaString += QLatin1Char('0'); + return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString)); + } + return r->context->m_fillStyle; +} + +static void ctx2d_fillStyle_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + if (value->IsObject()) { + QColor color = engine->toVariant(value, qMetaTypeId()).value(); + if (color.isValid()) { + r->context->state.fillStyle = color; + r->context->buffer()->setFillStyle(color); + r->context->m_fillStyle = value; + } else { + QV8Context2DStyleResource *style = v8_resource_cast(value->ToObject()); + if (style && style->brush != r->context->state.fillStyle) { + r->context->state.fillStyle = style->brush; + r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY); + r->context->m_fillStyle = value; + r->context->state.fillPatternRepeatX = style->patternRepeatX; + r->context->state.fillPatternRepeatY = style->patternRepeatY; + } + } + } else if (value->IsString()) { + QColor color = qt_color_from_string(value); + if (color.isValid() && r->context->state.fillStyle != QBrush(color)) { + r->context->state.fillStyle = QBrush(color); + r->context->buffer()->setFillStyle(r->context->state.fillStyle); + r->context->m_fillStyle = value; + } + } +} +/*! + \qmlproperty enumeration QtQuick2::Context2D::fillRule + Holds the current fill rule used for filling shapes. The following fill rules supported: + \list + \o Qt.OddEvenFill + \o Qt.WindingFill + \endlist + Note: Unlike the \a QPainterPath, the Canvas API uses the winding fill as the default fill rule. + The fillRule property is part of the context rendering state. + + \sa QtQuick2::Context2D::fillStyle + */ +static v8::Handle ctx2d_fillRule(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + return engine->fromVariant(r->context->state.fillRule); +} + +static void ctx2d_fillRule_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + if ((value->IsString() && engine->toString(value) == QStringLiteral("WindingFill")) + ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) { + r->context->state.fillRule = Qt::WindingFill; + } else if ((value->IsString() && engine->toString(value) == QStringLiteral("OddEvenFill")) + ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) { + r->context->state.fillRule = Qt::OddEvenFill; + } else { + //error + } + r->context->m_path.setFillRule(r->context->state.fillRule); +} +/*! + \qmlproperty variant QtQuick2::Context2D::strokeStyle + Holds the current color or style to use for the lines around shapes, + The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. + Invalid values are ignored. + + The default value is '#000000'. + + \sa QtQuick2::Context2D::createLinearGradient + \sa QtQuick2::Context2D::createRadialGradient + \sa QtQuick2::Context2D::createPattern + \sa QtQuick2::Context2D::fillStyle + */ +v8::Handle ctx2d_strokeStyle(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + QColor color = r->context->state.strokeStyle.color(); + if (color.isValid()) { + if (color.alpha() == 255) + return engine->toString(color.name()); + QString alphaString = QString::number(color.alphaF(), 'f'); + while (alphaString.endsWith(QLatin1Char('0'))) + alphaString.chop(1); + if (alphaString.endsWith(QLatin1Char('.'))) + alphaString += QLatin1Char('0'); + return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString)); + } + return r->context->m_strokeStyle; +} + +static void ctx2d_strokeStyle_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + if (value->IsObject()) { + QColor color = engine->toVariant(value, qMetaTypeId()).value(); + if (color.isValid()) { + r->context->state.fillStyle = color; + r->context->buffer()->setStrokeStyle(color); + r->context->m_strokeStyle = value; + } else { + QV8Context2DStyleResource *style = v8_resource_cast(value->ToObject()); + if (style && style->brush != r->context->state.strokeStyle) { + r->context->state.strokeStyle = style->brush; + r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY); + r->context->m_strokeStyle = value; + r->context->state.strokePatternRepeatX = style->patternRepeatX; + r->context->state.strokePatternRepeatY = style->patternRepeatY; + + } + } + } else if (value->IsString()) { + QColor color = qt_color_from_string(value); + if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) { + r->context->state.strokeStyle = QBrush(color); + r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle); + r->context->m_strokeStyle = value; + } + } +} + +/*! + \qmlmethod object QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1) + Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between + the start point (\a x0, \a y0) and the end point (\a x1, \a y1). + + A gradient is a smooth transition between colors. There are two types of gradients: linear and radial. + Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between + to the gradient's starting and end points or circles. + + \sa QtQuick2::Context2D::CanvasGradient::addColorStop + \sa QtQuick2::Context2D::createRadialGradient + \sa QtQuick2::Context2D::ctx2d_createConicalGradient + \sa QtQuick2::Context2D::createPattern + \sa QtQuick2::Context2D::fillStyle + \sa QtQuick2::Context2D::strokeStyle + */ + +static v8::Handle ctx2d_createLinearGradient(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 4) { + QQuickContext2DEngineData *ed = engineData(engine); + v8::Local gradient = ed->constructorGradient->NewInstance(); + QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine); + qreal x0 = args[0]->NumberValue(); + qreal y0 = args[1]->NumberValue(); + qreal x1 = args[2]->NumberValue(); + qreal y1 = args[3]->NumberValue(); + + if (!qIsFinite(x0) + || !qIsFinite(y0) + || !qIsFinite(x1) + || !qIsFinite(y1)) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments") + + r->brush = QLinearGradient(x0, y0, x1, y1); + gradient->SetExternalResource(r); + return gradient; + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1) + Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with + origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1. + + \sa QtQuick2::Context2D::CanvasGradient::addColorStop + \sa QtQuick2::Context2D::createLinearGradient + \sa QtQuick2::Context2D::ctx2d_createConicalGradient + \sa QtQuick2::Context2D::createPattern + \sa QtQuick2::Context2D::fillStyle + \sa QtQuick2::Context2D::strokeStyle + */ + +static v8::Handle ctx2d_createRadialGradient(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 6) { + QQuickContext2DEngineData *ed = engineData(engine); + v8::Local gradient = ed->constructorGradient->NewInstance(); + QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine); + + qreal x0 = args[0]->NumberValue(); + qreal y0 = args[1]->NumberValue(); + qreal r0 = args[2]->NumberValue(); + qreal x1 = args[3]->NumberValue(); + qreal y1 = args[4]->NumberValue(); + qreal r1 = args[5]->NumberValue(); + + if (!qIsFinite(x0) + || !qIsFinite(y0) + || !qIsFinite(x1) + || !qIsFinite(r0) + || !qIsFinite(r1) + || !qIsFinite(y1)) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments") + + if (r0 < 0 || r1 < 0) + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments") + + + r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0)); + gradient->SetExternalResource(r); + return gradient; + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::createConicalGradient(real x, real y, real angle) + Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y) + with start angle \c angle in units of radians. + + \sa QtQuick2::Context2D::CanvasGradient::addColorStop + \sa QtQuick2::Context2D::createLinearGradient + \sa QtQuick2::Context2D::ctx2d_createRadialGradient + \sa QtQuick2::Context2D::createPattern + \sa QtQuick2::Context2D::fillStyle + \sa QtQuick2::Context2D::strokeStyle + */ + +static v8::Handle ctx2d_createConicalGradient(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 6) { + QQuickContext2DEngineData *ed = engineData(engine); + v8::Local gradient = ed->constructorGradient->NewInstance(); + QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine); + + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal angle = DEGREES(args[2]->NumberValue()); + if (!qIsFinite(x) || !qIsFinite(y)) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments"); + + if (!qIsFinite(angle)) + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments"); + + r->brush = QConicalGradient(x, y, angle); + gradient->SetExternalResource(r); + return gradient; + } + + return args.This(); +} +/*! + \qmlmethod variant createPattern(Color color, enumeration patternMode) + This is a overload function. + Returns a CanvasPattern object that uses the given \c color and \c patternMode. + The valid pattern modes are: + \list + \o Qt.SolidPattern + \o Qt.Dense1Pattern + \o Qt.Dense2Pattern + \o Qt.Dense3Pattern + \o Qt.Dense4Pattern + \o Qt.Dense5Pattern + \o Qt.Dense6Pattern + \o Qt.Dense7Pattern + \o Qt.HorPattern + \o Qt.VerPattern + \o Qt.CrossPattern + \o Qt.BDiagPattern + \o Qt.FDiagPattern + \o Qt.DiagCrossPattern +\endlist + \sa Qt::BrushStyle + */ +/*! + \qmlmethod variant createPattern(Image image, string repetition) + Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument. + + The \a image parameter must be a valid Image item, a valid \a QtQuick2::CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception. + + The allowed values for \a repetition are: + + \list + \o "repeat" - both directions + \o "repeat-x - horizontal only + \o "repeat-y" - vertical only + \o "no-repeat" - neither + \endlist + + If the repetition argument is empty or null, the value "repeat" is used. + + \sa QtQuick2::Context2D::strokeStyle + \sa QtQuick2::Context2D::fillStyle + */ +static v8::Handle ctx2d_createPattern(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 2) { + QQuickContext2DEngineData *ed = engineData(engine); + QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine); + + QColor color = engine->toVariant(args[0], qMetaTypeId()).value(); + if (color.isValid()) { + int patternMode = args[1]->IntegerValue(); + Qt::BrushStyle style = Qt::SolidPattern; + if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) { + style = static_cast(patternMode); + } + styleResouce->brush = QBrush(color, style); + } else { + QImage patternTexture; + + if (args[0]->IsObject()) { + QV8Context2DPixelArrayResource *pixelData = v8_resource_cast(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject()); + if (pixelData) { + patternTexture = pixelData->image; + } + } else { + patternTexture = r->context->createImage(QUrl(engine->toString(args[0]->ToString()))); + } + + if (!patternTexture.isNull()) { + styleResouce->brush.setTextureImage(patternTexture); + + QString repetition = engine->toString(args[1]); + if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) { + styleResouce->patternRepeatX = true; + styleResouce->patternRepeatY = true; + } else if (repetition == QStringLiteral("repeat-x")) { + styleResouce->patternRepeatX = true; + } else if (repetition == QStringLiteral("repeat-y")) { + styleResouce->patternRepeatY = true; + } else if (repetition == QStringLiteral("no-repeat")) { + styleResouce->patternRepeatY = false; + styleResouce->patternRepeatY = false; + } else { + //TODO: exception: SYNTAX_ERR + } + + } + } + + v8::Local pattern = ed->constructorPattern->NewInstance(); + pattern->SetExternalResource(styleResouce); + return pattern; + + } + return v8::Undefined(); +} + +// line styles +/*! + \qmlproperty string QtQuick2::Context2D::lineCap + Holds the the current line cap style. + The possible line cap styles are: + \list + \o butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value. + \o round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line. + \o square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line. + \endlist + Other values are ignored. +*/ +v8::Handle ctx2d_lineCap(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + switch (r->context->state.lineCap) { + case Qt::RoundCap: + return engine->toString(QLatin1String("round")); + case Qt::FlatCap: + return engine->toString(QLatin1String("butt")); + case Qt::SquareCap: + return engine->toString(QLatin1String("square")); + default: + break; + } + return engine->toString(QLatin1String("butt"));; +} + +static void ctx2d_lineCap_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + QString lineCap = engine->toString(value); + Qt::PenCapStyle cap; + if (lineCap == QLatin1String("round")) + cap = Qt::RoundCap; + else if (lineCap == QLatin1String("butt")) + cap = Qt::FlatCap; + else if (lineCap == QLatin1String("square")) + cap = Qt::SquareCap; + else + return; + + if (cap != r->context->state.lineCap) { + r->context->state.lineCap = cap; + r->context->buffer()->setLineCap(cap); + } +} + +/*! + \qmlproperty string QtQuick2::Context2D::lineJoin + Holds the the current line join style. A join exists at any point in a subpath + shared by two consecutive lines. When a subpath is closed, then a join also exists + at its first point (equivalent to its last point) connecting the first and last lines in the subpath. + + The possible line join styles are: + \list + \o bevel - this is all that is rendered at joins. + \o round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins. + \o miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style. + \endlist + Other values are ignored. +*/ +v8::Handle ctx2d_lineJoin(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + switch (r->context->state.lineJoin) { + case Qt::RoundJoin: + return engine->toString(QLatin1String("round")); + case Qt::BevelJoin: + return engine->toString(QLatin1String("bevel")); + case Qt::MiterJoin: + return engine->toString(QLatin1String("miter")); + default: + break; + } + return engine->toString(QLatin1String("miter")); +} + +static void ctx2d_lineJoin_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + QString lineJoin = engine->toString(value); + Qt::PenJoinStyle join; + if (lineJoin == QLatin1String("round")) + join = Qt::RoundJoin; + else if (lineJoin == QLatin1String("bevel")) + join = Qt::BevelJoin; + else if (lineJoin == QLatin1String("miter")) + join = Qt::MiterJoin; + else + return; + + if (join != r->context->state.lineJoin) { + r->context->state.lineJoin = join; + r->context->buffer()->setLineJoin(join); + } +} + +/*! + \qmlproperty real QtQuick2::Context2D::lineWidth + Holds the the current line width. Values that are not finite values greater than zero are ignored. + */ +v8::Handle ctx2d_lineWidth(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + return v8::Number::New(r->context->state.lineWidth); +} + +static void ctx2d_lineWidth_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + qreal w = value->NumberValue(); + + if (w > 0 && qIsFinite(w) && w != r->context->state.lineWidth) { + r->context->state.lineWidth = w; + r->context->buffer()->setLineWidth(w); + } +} + +/*! + \qmlproperty real QtQuick2::Context2D::miterLimit + Holds the current miter limit ratio. + The default miter limit value is 10.0. + */ +v8::Handle ctx2d_miterLimit(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + return v8::Number::New(r->context->state.miterLimit); +} + +static void ctx2d_miterLimit_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + qreal ml = value->NumberValue(); + + if (ml > 0 && qIsFinite(ml) && ml != r->context->state.miterLimit) { + r->context->state.miterLimit = ml; + r->context->buffer()->setMiterLimit(ml); + } +} + +// shadows +/*! + \qmlproperty real QtQuick2::Context2D::shadowBlur + Holds the current level of blur applied to shadows + */ +v8::Handle ctx2d_shadowBlur(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + return v8::Number::New(r->context->state.shadowBlur); +} + +static void ctx2d_shadowBlur_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + qreal blur = value->NumberValue(); + + if (blur > 0 && qIsFinite(blur) && blur != r->context->state.shadowBlur) { + r->context->state.shadowBlur = blur; + r->context->buffer()->setShadowBlur(blur); + } +} + +/*! + \qmlproperty string QtQuick2::Context2D::shadowColor + Holds the current shadow color. + */ +v8::Handle ctx2d_shadowColor(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + return engine->toString(r->context->state.shadowColor.name()); +} + +static void ctx2d_shadowColor_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QColor color = qt_color_from_string(value); + + if (color.isValid() && color != r->context->state.shadowColor) { + r->context->state.shadowColor = color; + r->context->buffer()->setShadowColor(color); + } +} + + +/*! + \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX + Holds the current shadow offset in the positive horizontal distance. + + \sa QtQuick2::Context2D::shadowOffsetY + */ +v8::Handle ctx2d_shadowOffsetX(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + return v8::Number::New(r->context->state.shadowOffsetX); +} + +static void ctx2d_shadowOffsetX_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + qreal offsetX = value->NumberValue(); + if (qIsFinite(offsetX) && offsetX != r->context->state.shadowOffsetX) { + r->context->state.shadowOffsetX = offsetX; + r->context->buffer()->setShadowOffsetX(offsetX); + } +} +/*! + \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY + Holds the current shadow offset in the positive vertical distance. + + \sa QtQuick2::Context2D::shadowOffsetX + */ +v8::Handle ctx2d_shadowOffsetY(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + + return v8::Number::New(r->context->state.shadowOffsetY); +} + +static void ctx2d_shadowOffsetY_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + qreal offsetY = value->NumberValue(); + if (qIsFinite(offsetY) && offsetY != r->context->state.shadowOffsetY) { + r->context->state.shadowOffsetY = offsetY; + r->context->buffer()->setShadowOffsetY(offsetY); + } +} + +v8::Handle ctx2d_path(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + return r->context->m_v8path; +} + +static void ctx2d_path_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + r->context->beginPath(); + if (value->IsObject()) { + QDeclarativePath* path = qobject_cast(engine->toQObject(value)); + if (path) + r->context->m_path = path->path(); + } else { + QString path = engine->toString(value->ToString()); + QDeclarativeSvgParser::parsePathDataFast(path, r->context->m_path); + } + r->context->m_v8path = value; +} + +//rects +/*! + \qmlmethod object QtQuick2::Context2D::clearRect(real x, real y, real w, real h) + Clears all pixels on the canvas in the given rectangle to transparent black. + */ +static v8::Handle ctx2d_clearRect(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 4) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal w = args[2]->NumberValue(); + qreal h = args[3]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return args.This(); + + r->context->buffer()->clearRect(x, y, w, h); + } + + return args.This(); +} +/*! + \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h) + Paint the specified rectangular area using the fillStyle. + + \sa QtQuick2::Context2D::fillStyle + */ +static v8::Handle ctx2d_fillRect(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + if (args.Length() == 4) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal w = args[2]->NumberValue(); + qreal h = args[3]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return args.This(); + + r->context->buffer()->fillRect(x, y, w, h); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h) + Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin, + and (if appropriate) miterLimit attributes. + + \sa QtQuick2::Context2D::strokeStyle + \sa QtQuick2::Context2D::lineWidth + \sa QtQuick2::Context2D::lineJoin + \sa QtQuick2::Context2D::miterLimit + */ +static v8::Handle ctx2d_strokeRect(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 4) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal w = args[2]->NumberValue(); + qreal h = args[3]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return args.This(); + + r->context->buffer()->strokeRect(x, y, w, h); + } + + return args.This(); +} + +// Complex shapes (paths) API +/*! + \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise) + Adds an arc to the current subpath that lies on the circumference of the circle whose center is at the point (\c x,\cy) and whose radius is \c radius. + \image qml-item-canvas-arcTo2.png + \sa QtQuick2::Context2D::arcTo + See {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc} + */ +static v8::Handle ctx2d_arc(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + if (args.Length() >= 5) { + bool antiClockwise = false; + + if (args.Length() == 6) + antiClockwise = args[5]->BooleanValue(); + + qreal radius = args[2]->NumberValue(); + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal sa = args[3]->NumberValue(); + qreal ea = args[4]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(sa) || !qIsFinite(ea)) + return args.This(); + + if (radius < 0) + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius"); + + r->context->arc(args[0]->NumberValue(), + args[1]->NumberValue(), + radius, + args[3]->NumberValue(), + args[4]->NumberValue(), + antiClockwise); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius) + + Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line. + To draw an arc, you begin with the same steps your followed to create a line: + \list + \o Call the context.beginPath() method to set a new path. + \o Call the context.moveTo(\c x, \c y) method to set your starting position on the canvas at the point (\c x,\c y). + \o To draw an arc or circle, call the context.arcTo(\c x1, \c y1, \c x2, \c y2,\c radius) method. + This adds an arc with starting point (\c x1,\c y1), ending point (\c x2, \c y2), and radius \c radius to the current subpath and connects + it to the previous subpath by a straight line. + \endlist + \image qml-item-canvas-arcTo.png + Both startAngle and endAngle are measured from the x axis in units of radians. + + \image qml-item-canvas-startAngle.png + The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction. + \sa QtQuick2::Context2D::arc + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d context standard for arcTo} + */ +static v8::Handle ctx2d_arcTo(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + + if (args.Length() == 5) { + qreal x1 = args[0]->NumberValue(); + qreal y1 = args[1]->NumberValue(); + qreal x2 = args[2]->NumberValue(); + qreal y2 = args[3]->NumberValue(); + + if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2)) + return args.This(); + + qreal radius = args[4]->NumberValue(); + if (radius < 0) + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius"); + r->context->arcTo(args[0]->NumberValue(), + args[1]->NumberValue(), + args[2]->NumberValue(), + args[3]->NumberValue(), + args[4]->NumberValue()); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::beginPath() + + Resets the current path to a new path. + */ +static v8::Handle ctx2d_beginPath(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + r->context->beginPath(); + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y) + + Adds a cubic Bezier curve between the current position and the given endPoint using the control points specified by (\c cp1x, cp1y), + and (\c cp2x, \c cp2y). + After the curve is added, the current position is updated to be at the end point (\c x, \c y) of the curve. + The following code produces the path shown below: + \code + ctx.strokeStyle = Qt.rgba(0, 0, 0, 1); + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(20, 0);//start point + ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0); + ctx.stroke(); + \endcode + \image qml-item-canvas-bezierCurveTo.png + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo} + \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo} + */ +static v8::Handle ctx2d_bezierCurveTo(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 6) { + qreal cp1x = args[0]->NumberValue(); + qreal cp1y = args[1]->NumberValue(); + qreal cp2x = args[2]->NumberValue(); + qreal cp2y = args[3]->NumberValue(); + qreal x = args[4]->NumberValue(); + qreal y = args[5]->NumberValue(); + + if (!qIsFinite(cp1x) || !qIsFinite(cp1y) || !qIsFinite(cp2x) || !qIsFinite(cp2y) || !qIsFinite(x) || !qIsFinite(y)) + return args.This(); + + r->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::clip() + + Creates the clipping region from the current path. + Any parts of the shape outside the clipping path are not displayed. + To create a complex shape using the \a clip() method: + + \list 1 + \o Call the \c{context.beginPath()} method to set the clipping path. + \o Define the clipping path by calling any combination of the \c{lineTo}, + \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods. + \o Call the \c{context.clip()} method. + \endlist + + The new shape displays. The following shows how a clipping path can + modify how an image displays: + + \image qml-canvas-clip-complex.png + \sa QtQuick2::Context2D::beginPath() + \sa QtQuick2::Context2D::closePath() + \sa QtQuick2::Context2D::stroke() + \sa QtQuick2::Context2D::fill() + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip} + */ +static v8::Handle ctx2d_clip(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QPainterPath clipPath = r->context->m_path; + clipPath.closeSubpath(); + if (!r->context->state.clipPath.isEmpty()) + r->context->state.clipPath = clipPath.intersected(r->context->state.clipPath); + else + r->context->state.clipPath = clipPath; + r->context->buffer()->clip(r->context->state.clipPath); + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::closePath() + Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path. + The current point of the new path is the previous subpath's first point. + + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath} + */ +static v8::Handle ctx2d_closePath(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + r->context->closePath(); + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::fill() + + Fills the subpaths with the current fill style. + + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill} + + \sa QtQuick2::Context2D::fillStyle + */ +static v8::Handle ctx2d_fill(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r); + + r->context->buffer()->fill(r->context->m_path); + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::lineTo(real x, real y) + + Draws a line from the current position to the point (x, y). + */ +static v8::Handle ctx2d_lineTo(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 2) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y)) + return args.This(); + + r->context->lineTo(x, y); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::moveTo(real x, real y) + + Creates a new subpath with the given point. + */ +static v8::Handle ctx2d_moveTo(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + if (args.Length() == 2) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y)) + return args.This(); + r->context->moveTo(x, y); + } + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y) + + Adds a quadratic Bezier curve between the current point and the endpoint (\c x, \c y) with the control point specified by (\c cpx, \c cpy). + + See {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for for quadraticCurveTo} + */ +static v8::Handle ctx2d_quadraticCurveTo(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + if (args.Length() == 4) { + qreal cpx = args[0]->NumberValue(); + qreal cpy = args[1]->NumberValue(); + qreal x = args[2]->NumberValue(); + qreal y = args[3]->NumberValue(); + + if (!qIsFinite(cpx) || !qIsFinite(cpy) || !qIsFinite(x) || !qIsFinite(y)) + return args.This(); + + r->context->quadraticCurveTo(cpx, cpy, x, y); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::rect(real x, real y, real w, real h) + + Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath. + */ +static v8::Handle ctx2d_rect(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 4) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal w = args[2]->NumberValue(); + qreal h = args[3]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return args.This(); + + r->context->rect(x, y, w, h); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius) + + Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the + ellipses defining the corners of the rounded rectangle. + */ +static v8::Handle ctx2d_roundedRect(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + if (args.Length() == 6) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal w = args[2]->NumberValue(); + qreal h = args[3]->NumberValue(); + qreal xr = args[4]->NumberValue(); + qreal yr = args[5]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return args.This(); + + if (!qIsFinite(xr) || !qIsFinite(yr)) + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "roundedRect(): Invalid arguments"); + + r->context->roundedRect(x, y, w, h, xr, yr); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::ellipse(real x, real y, real w, real h) + + Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h, + and adds it to the path as a closed subpath. + + The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position). + */ +static v8::Handle ctx2d_ellipse(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + if (args.Length() == 4) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal w = args[2]->NumberValue(); + qreal h = args[3]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return args.This(); + + + r->context->ellipse(x, y, w, h); + } + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::text(string text, real x, real y) + + Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied. + The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y). + */ +static v8::Handle ctx2d_text(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + if (args.Length() == 3) { + qreal x = args[1]->NumberValue(); + qreal y = args[2]->NumberValue(); + + if (!qIsFinite(x) || !qIsFinite(y)) + return args.This(); + r->context->text(engine->toString(args[0]), x, y); + } + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::stroke() + + Strokes the subpaths with the current stroke style. + + See {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke} + + \sa QtQuick2::Context2D::strokeStyle + */ +static v8::Handle ctx2d_stroke(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + + r->context->buffer()->stroke(r->context->m_path); + + return args.This(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::isPointInPath(real x, real y) + + Returns true if the given point is in the current path. + + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath} + */ +static v8::Handle ctx2d_isPointInPath(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + bool pointInPath = false; + if (args.Length() == 2) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + if (!qIsFinite(x) || !qIsFinite(y)) + return v8::Boolean::New(false); + pointInPath = r->context->isPointInPath(x, y); + } + return v8::Boolean::New(pointInPath); +} + +static v8::Handle ctx2d_drawFocusRing(const v8::Arguments &args) +{ + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported"); + return args.This(); +} + +static v8::Handle ctx2d_setCaretSelectionRect(const v8::Arguments &args) +{ + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported"); + return args.This(); +} + +static v8::Handle ctx2d_caretBlinkRate(const v8::Arguments &args) +{ + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported"); + return args.This(); +} +// text +/*! + \qmlproperty string QtQuick2::Context2D::font + Holds the current font settings. + + The default font value is "10px sans-serif". + See {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font} + */ +v8::Handle ctx2d_font(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + return engine->toString(r->context->state.font.toString()); +} + +static void ctx2d_font_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + QString fs = engine->toString(value); + QFont font = qt_font_from_string(fs); + if (font != r->context->state.font) { + r->context->state.font = font; + } +} + +/*! + \qmlproperty string QtQuick2::Context2D::textAlign + + Holds the current text alignment settings. + The possible values are: + \list + \o start + \o end + \o left + \o right + \o center + \endlist + Other values are ignored. The default value is "start". + */ +v8::Handle ctx2d_textAlign(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + QV8Engine *engine = V8ENGINE_ACCESSOR(); + switch (r->context->state.textAlign) { + case QQuickContext2D::Start: + return engine->toString(QLatin1String("start")); + case QQuickContext2D::End: + return engine->toString(QLatin1String("end")); + case QQuickContext2D::Left: + return engine->toString(QLatin1String("left")); + case QQuickContext2D::Right: + return engine->toString(QLatin1String("right")); + case QQuickContext2D::Center: + return engine->toString(QLatin1String("center")); + default: + break; + } + return engine->toString(QLatin1String("start")); +} + +static void ctx2d_textAlign_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + QV8Engine *engine = V8ENGINE_ACCESSOR(); + + QString textAlign = engine->toString(value); + + QQuickContext2D::TextAlignType ta; + if (textAlign == QLatin1String("start")) + ta = QQuickContext2D::Start; + else if (textAlign == QLatin1String("end")) + ta = QQuickContext2D::End; + else if (textAlign == QLatin1String("left")) + ta = QQuickContext2D::Left; + else if (textAlign == QLatin1String("right")) + ta = QQuickContext2D::Right; + else if (textAlign == QLatin1String("center")) + ta = QQuickContext2D::Center; + else + return; + + if (ta != r->context->state.textAlign) { + r->context->state.textAlign = ta; + } +} + +/*! + \qmlproperty string QtQuick2::Context2D::textBaseline + + Holds the current baseline alignment settings. + The possible values are: + \list + \o top + \o hanging + \o middle + \o alphabetic + \o ideographic + \o bottom + \endlist + Other values are ignored. The default value is "alphabetic". + */ +v8::Handle ctx2d_textBaseline(v8::Local, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE_ACCESSOR(); + switch (r->context->state.textBaseline) { + case QQuickContext2D::Alphabetic: + return engine->toString(QLatin1String("alphabetic")); + case QQuickContext2D::Hanging: + return engine->toString(QLatin1String("hanging")); + case QQuickContext2D::Top: + return engine->toString(QLatin1String("top")); + case QQuickContext2D::Bottom: + return engine->toString(QLatin1String("bottom")); + case QQuickContext2D::Middle: + return engine->toString(QLatin1String("middle")); + default: + break; + } + return engine->toString(QLatin1String("alphabetic")); +} + +static void ctx2d_textBaseline_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QV8Context2DResource *r = v8_resource_cast(info.This()); + CHECK_CONTEXT_SETTER(r) + QV8Engine *engine = V8ENGINE_ACCESSOR(); + QString textBaseline = engine->toString(value); + + QQuickContext2D::TextBaseLineType tb; + if (textBaseline == QLatin1String("alphabetic")) + tb = QQuickContext2D::Alphabetic; + else if (textBaseline == QLatin1String("hanging")) + tb = QQuickContext2D::Hanging; + else if (textBaseline == QLatin1String("top")) + tb = QQuickContext2D::Top; + else if (textBaseline == QLatin1String("bottom")) + tb = QQuickContext2D::Bottom; + else if (textBaseline == QLatin1String("middle")) + tb = QQuickContext2D::Middle; + else + return; + + if (tb != r->context->state.textBaseline) { + r->context->state.textBaseline = tb; + } +} + +/*! + \qmlmethod object QtQuick2::Context2D::fillText(text, x, y) + Fills the given text at the given position. + \sa QtQuick2::Context2D::font + \sa QtQuick2::Context2D::textAlign + \sa QtQuick2::Context2D::textBaseline + \sa QtQuick2::Context2D::strokeText + */ +static v8::Handle ctx2d_fillText(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + if (args.Length() == 3) { + qreal x = args[1]->NumberValue(); + qreal y = args[2]->NumberValue(); + if (!qIsFinite(x) || !qIsFinite(y)) + return args.This(); + QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0])); + r->context->buffer()->fill(textPath); + } + return args.This(); +} +/*! + \qmlmethod object QtQuick2::Context2D::strokeText(text, x, y) + Strokes the given text at the given position. + \sa QtQuick2::Context2D::font + \sa QtQuick2::Context2D::textAlign + \sa QtQuick2::Context2D::textBaseline + \sa QtQuick2::Context2D::fillText + */ +static v8::Handle ctx2d_strokeText(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + if (args.Length() == 3) { + qreal x = args[1]->NumberValue(); + qreal y = args[2]->NumberValue(); + if (!qIsFinite(x) || !qIsFinite(y)) + return args.This(); + QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0])); + r->context->buffer()->stroke(textPath); + } + return args.This(); +} +/*! + \qmlclass QtQuick2::TextMetrics + \inqmlmodule QtQuick 2 + \since QtQuick 2.0 + \brief The Context2D TextMetrics interface. + The TextMetrics object can be created by QtQuick2::Context2D::measureText method. + See {http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details. + + \sa QtQuick2::Context2D::measureText + \sa QtQuick2::TextMetrics::width + */ + +/*! + \qmlproperty int QtQuick2::TextMetrics::width + Holds the advance width of the text that was passed to the QtQuick2::Context2D::measureText() method. + This property is read only. + */ + +/*! + \qmlmethod variant QtQuick2::Context2D::measureText(text) + Returns a TextMetrics object with the metrics of the given text in the current font. + */ +static v8::Handle ctx2d_measureText(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 1) { + QFontMetrics fm(r->context->state.font); + uint width = fm.width(engine->toString(args[0])); + v8::Local tm = v8::Object::New(); + tm->Set(v8::String::New("width"), v8::Number::New(width)); + return tm; + } + return v8::Undefined(); +} + +// drawing images +/*! + \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy) + Draws the given \a image on the canvas at position (\a dx, \a dy). + Note: + The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object. + When given as Image item, if the image isn't fully loaded, this method draws nothing. + When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first. + This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object. + + \sa QtQuick2::CanvasImageData + \sa QtQuick2::Image + \sa QtQuick2::Canvas::loadImage + \sa QtQuick2::Canvas::isImageLoaded + \sa QtQuick2::Canvas::imageLoaded + + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage} + */ +/*! + \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh) + This is an overloaded function. + Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw, + height \a dh. + + Note: + The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object. + When given as Image item, if the image isn't fully loaded, this method draws nothing. + When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first. + This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object. + + \sa QtQuick2::CanvasImageData + \sa QtQuick2::Image + \sa QtQuick2::Canvas::loadImage + \sa QtQuick2::Canvas::isImageLoaded + \sa QtQuick2::Canvas::imageLoaded + + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage} + */ +/*! + \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh) + This is an overloaded function. + Draws the given item as \a image from source point (\a sx, \a sy) and source width \sw, source height \sh + onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh. + + + Note: + The \a image type can be an Image or Canvas item, an image url or a \a {QtQuick2::CanvasImageData} object. + When given as Image item, if the image isn't fully loaded, this method draws nothing. + When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first. + This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object. + + \sa QtQuick2::CanvasImageData + \sa QtQuick2::Image + \sa QtQuick2::Canvas::loadImage + \sa QtQuick2::Canvas::isImageLoaded + \sa QtQuick2::Canvas::imageLoaded + + \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage} +*/ +static v8::Handle ctx2d_drawImage(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + qreal sx, sy, sw, sh, dx, dy, dw, dh; + + if (!args.Length()) + return args.This(); + + QImage image; + if (args[0]->IsString()) { + QUrl url(engine->toString(args[0]->ToString())); + if (!url.isValid()) + V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); + + image = r->context->createImage(url); + } else if (args[0]->IsObject()) { + QQuickImage *imageItem = qobject_cast(engine->toQObject(args[0]->ToObject())); + QQuickCanvasItem *canvas = qobject_cast(engine->toQObject(args[0]->ToObject())); + + QV8Context2DPixelArrayResource *pix = v8_resource_cast(args[0]->ToObject()->GetInternalField(0)->ToObject()); + if (pix) { + image = pix->image; + } else if (imageItem) { + image = imageItem->pixmap().toImage(); + } else if (canvas) { + image = canvas->toImage(); + } else { + V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); + } + } else { + V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); + } + if (args.Length() == 3) { + dx = args[1]->NumberValue(); + dy = args[2]->NumberValue(); + sx = 0; + sy = 0; + sw = image.width(); + sh = image.height(); + dw = sw; + dh = sh; + } else if (args.Length() == 5) { + sx = 0; + sy = 0; + sw = image.width(); + sh = image.height(); + dx = args[1]->NumberValue(); + dy = args[2]->NumberValue(); + dw = args[3]->NumberValue(); + dh = args[4]->NumberValue(); + } else if (args.Length() == 9) { + sx = args[1]->NumberValue(); + sy = args[2]->NumberValue(); + sw = args[3]->NumberValue(); + sh = args[4]->NumberValue(); + dx = args[5]->NumberValue(); + dy = args[6]->NumberValue(); + dw = args[7]->NumberValue(); + dh = args[8]->NumberValue(); + } else { + return args.This(); + } + + if (!qIsFinite(sx) + || !qIsFinite(sy) + || !qIsFinite(sw) + || !qIsFinite(sh) + || !qIsFinite(dx) + || !qIsFinite(dy) + || !qIsFinite(dw) + || !qIsFinite(dh)) + return args.This(); + + if (!image.isNull()) { + if (sx < 0 || sy < 0 || sw == 0 || sh == 0 + || sx + sw > image.width() || sy + sh > image.height() + || sx + sw < 0 || sy + sh < 0) { + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error"); + } + + r->context->buffer()->drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh); + } + + return args.This(); +} + +// pixel manipulation +/*! + \qmlclass QtQuick2::CanvasImageData + The \a QtQuick2::CanvasImageData object holds the image pixel data. + + The \a QtQuick2::CanvasImageData object has the actual dimensions of the data stored in + this object and holds the one-dimensional array containing the data in RGBA order, + as integers in the range 0 to 255. + + \sa QtQuick2::CanvasImageData::width + \sa QtQuick2::CanvasImageData::height + \sa QtQuick2::CanvasImageData::data + \sa QtQuick2::Context2D::createImageData + \sa QtQuick2::Context2D::getImageData + \sa QtQuick2::Context2D::putImageData + */ +/*! + \qmlproperty QtQuick2::CanvasImageData::width + Holds the actual width dimension of the data in the ImageData object, in device pixels. + */ +v8::Handle ctx2d_imageData_width(v8::Local, const v8::AccessorInfo &args) +{ + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); + if (!r) + return v8::Integer::New(0); + return v8::Integer::New(r->image.width()); +} + +/*! + \qmlproperty QtQuick2::CanvasImageData::height + Holds the actual height dimension of the data in the ImageData object, in device pixels. + */ +v8::Handle ctx2d_imageData_height(v8::Local, const v8::AccessorInfo &args) +{ + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); + if (!r) + return v8::Integer::New(0); + + return v8::Integer::New(r->image.height()); +} + +/*! + \qmlproperty QtQuick2::CanvasImageData::data + Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255. + */ +v8::Handle ctx2d_imageData_data(v8::Local, const v8::AccessorInfo &args) +{ + return args.This()->GetInternalField(0); +} + +/*! + \qmlmethod void QtQuick2::CanvasImageData::mirrr( bool horizontal = false, bool vertical = true) + Mirrors the image data in place in the \c horizontal and/or the \c vertical direction depending on + whether horizontal and vertical are set to true or false. + The default \c horizontal value is false, the default \c vertical value is true. +*/ +static v8::Handle ctx2d_imageData_mirror(const v8::Arguments &args) +{ + bool horizontal = false, vertical = true; + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); + + if (!r) { + //error + return v8::Undefined(); + } + + if (args.Length() > 2) { + //error + return v8::Undefined(); + } + + if (args.Length() == 1) { + horizontal = args[0]->BooleanValue(); + } else if (args.Length() == 2) { + horizontal = args[0]->BooleanValue(); + vertical = args[1]->BooleanValue(); + } + r->image = r->image.mirrored(horizontal, vertical); + return args.This(); +} + +/*! + \qmlmethod void QtQuick2::CanvasImageData::filter(enumeration mode, args) + Filters the image data as defined by one of the following modes: + \list + \o Canvas.Threshold - converts the image to black and white pixels depending + if they are above or below the threshold defined by the level parameter. + The level must be between 0.0 (black) and 1.0(white). + If no level is specified, 0.5 is used. + \o Canvas.Mono - converts the image to the 1-bit per pixel format. + \o Canvas.GrayScale - converts any colors in the image to grayscale equivalents. + \o Canvas.Brightness -increase/decrease a fixed \c adjustment value to each pixel's RGB channel value. + \o Canvas.Invert - sets each pixel to its inverse value. + \o Canvas.Blur - executes a box blur with the pixel \c radius parameter specifying the range of the blurring for each pixel. + the default blur \c radius is 3. This filter also accepts another \c quality parameter, if true, the filter will + execute 3-passes box blur to simulate the Guassian blur. The default \c quality value is false. + \o Canvas.Opaque - sets the alpha channel to entirely opaque. + \o Canvas.Convolute - executes a generic {http://en.wikipedia.org/wiki/Convolution}{Convolution} filter, the second + parameter contains the convoluton matrix data as a number array. + \endlist + +*/ +static v8::Handle ctx2d_imageData_filter(const v8::Arguments &args) +{ + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); + + if (!r) { + //error + return v8::Undefined(); + } + + if (args.Length() >= 1) { + int filterFlag = args[0]->IntegerValue(); + switch (filterFlag) { + case QQuickCanvasItem::Mono : + { + r->image = r->image.convertToFormat(QImage::Format_Mono).convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + break; + case QQuickCanvasItem::GrayScale : + { + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + unsigned char* rgb = ((unsigned char*)&row[x]); + rgb[0] = rgb[1] = rgb[2] = qGray(rgb[0], rgb[1], rgb[2]); + } + } + } + break; + case QQuickCanvasItem::Threshold : + { + qreal threshold = 0.5; + if (args.Length() > 1) + threshold = args[1]->NumberValue(); + + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + unsigned char* rgb = ((unsigned char*)&row[x]); + unsigned char v = qGray(rgb[0], rgb[1], rgb[2]) >= threshold*255 ? 255 : 0; + rgb[0] = rgb[1] = rgb[2] = v; + } + } + } + break; + case QQuickCanvasItem::Brightness : + { + int adjustment = 1; + if (args.Length() > 1) + adjustment = args[1]->IntegerValue(); + + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + ((unsigned char*)&row[x])[0] += adjustment; + ((unsigned char*)&row[x])[1] += adjustment; + ((unsigned char*)&row[x])[2] += adjustment; + } + } + } + break; + case QQuickCanvasItem::Invert : + { + r->image.invertPixels(); + } + break; + case QQuickCanvasItem::Blur : + { + int radius = 3; + bool quality = false; + + if (args.Length() > 1) + radius = args[1]->IntegerValue() / 2; + if (args.Length() > 2) + quality = args[2]->BooleanValue(); + + qt_image_boxblur(r->image, radius, quality); + } + break; + case QQuickCanvasItem::Opaque : + { + for (int y = 0; y < r->image.height(); ++y) { + QRgb *row = (QRgb*)r->image.scanLine(y); + for (int x = 0; x < r->image.width(); ++x) { + ((unsigned char*)&row[x])[3] = 255; + } + } + } + break; + case QQuickCanvasItem::Convolute : + { + if (args.Length() > 1 && args[1]->IsArray()) { + v8::Local array = v8::Local::Cast(args[1]); + QVector weights; + for (uint32_t i = 0; i < array->Length(); ++i) + weights.append(array->Get(i)->NumberValue()); + r->image = qt_image_convolute_filter(r->image, weights); + } else { + //error + } + } + break; + default: + break; + } + } + + return args.This(); +} +/*! + \qmlclass QtQuick2::CanvasPixelArray + The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data. + The CanvasPixelArray can be accessed as normal Javascript array. + \sa QtQuick2::CanvasImageData + \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray} + */ + +/*! + \qmlproperty QtQuick2::CanvasPixelArray::length + The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData. + The length attribute of a CanvasPixelArray object must return this h×w×4 number value. + This property is read only. +*/ +v8::Handle ctx2d_pixelArray_length(v8::Local, const v8::AccessorInfo &args) +{ + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()); + if (!r || r->image.isNull()) return v8::Undefined(); + + return v8::Integer::New(r->image.width() * r->image.height() * 4); +} + +v8::Handle ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args) +{ + QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()); + + if (r && index < static_cast(r->image.width() * r->image.height() * 4)) { + const quint32 w = r->image.width(); + const quint32 row = (index / 4) / w; + const quint32 col = (index / 4) % w; + const QRgb* pixel = reinterpret_cast(r->image.constScanLine(row)); + pixel += col; + switch (index % 4) { + case 0: + return v8::Integer::New(qRed(*pixel)); + case 1: + return v8::Integer::New(qGreen(*pixel)); + case 2: + return v8::Integer::New(qBlue(*pixel)); + case 3: + return v8::Integer::New(qAlpha(*pixel)); + } + } + return v8::Undefined(); +} + +v8::Handle ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local value, const v8::AccessorInfo& info) +{ + QV8Context2DPixelArrayResource *r = v8_resource_cast(info.This()); + + const int v = value->Uint32Value(); + if (r && index < static_cast(r->image.width() * r->image.height() * 4) && v > 0 && v <= 255) { + const quint32 w = r->image.width(); + const quint32 row = (index / 4) / w; + const quint32 col = (index / 4) % w; + + QRgb* pixel = reinterpret_cast(r->image.scanLine(row)); + pixel += col; + switch (index % 4) { + case 0: + *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel)); + break; + case 1: + *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel)); + break; + case 2: + *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel)); + break; + case 3: + *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v); + break; + } + } + return v8::Undefined(); +} +/*! + \qmlmethod QtQuick2::CanvasImageData createImageData(real sw, real sh) + Creates a CanvasImageData object with the given dimensions(\a sw, \a sh). + */ +/*! + \qmlmethod QtQuick2::CanvasImageData createImageData(QtQuick2::CanvasImageData imageData) + Creates a CanvasImageData object with the same dimensions as the argument. + */ +/*! + \qmlmethod QtQuick2::CanvasImageData createImageData(Url imageUrl) + Creates a CanvasImageData object with the given image loaded from \a imageUrl. + Note:The \a imageUrl must be already loaded before this function call, if not, an empty + CanvasImageData obect will be returned. + + \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::isImageLoaded + */ +static v8::Handle ctx2d_createImageData(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 1) { + if (args[0]->IsObject()) { + v8::Local imgData = args[0]->ToObject(); + QV8Context2DPixelArrayResource *pa = v8_resource_cast(imgData->GetInternalField(0)->ToObject()); + if (pa) { + qreal w = imgData->Get(v8::String::New("width"))->NumberValue(); + qreal h = imgData->Get(v8::String::New("height"))->NumberValue(); + return qt_create_image_data(w, h, engine, QImage()); + } + } else if (args[0]->IsString()) { + QImage image = r->context->createImage(QUrl(engine->toString(args[0]->ToString()))); + return qt_create_image_data(image.width(), image.height(), engine, image); + } + } else if (args.Length() == 2) { + qreal w = args[0]->NumberValue(); + qreal h = args[1]->NumberValue(); + + if (!qIsFinite(w) || !qIsFinite(h)) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments"); + + if (w > 0 && h > 0) + return qt_create_image_data(w, h, engine, QImage()); + else + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments"); + } + return v8::Undefined(); +} + +/*! + \qmlmethod QtQuick2::CanvasImageData getImageData(real sx, real sy, real sw, real sh) + Returns an CanvasImageData object containing the image data for the given rectangle of the canvas. + */ +static v8::Handle ctx2d_getImageData(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + + QV8Engine *engine = V8ENGINE(); + if (args.Length() == 4) { + qreal x = args[0]->NumberValue(); + qreal y = args[1]->NumberValue(); + qreal w = args[2]->NumberValue(); + qreal h = args[3]->NumberValue(); + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(w)) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments"); + + if (w <= 0 || h <= 0) + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments"); + + QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h)); + v8::Local imageData = qt_create_image_data(w, h, engine, image); + + return imageData; + } + return v8::Null(); +} + +/*! + \qmlmethod object QtQuick2::Context2D::putImageData(QtQuick2::CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight) + Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted. + */ +static v8::Handle ctx2d_putImageData(const v8::Arguments &args) +{ + QV8Context2DResource *r = v8_resource_cast(args.This()); + CHECK_CONTEXT(r) + if (args.Length() != 3 && args.Length() != 7) + return v8::Undefined(); + + if (args[0]->IsNull() || !args[0]->IsObject()) { + V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch"); + } + qreal dx = args[1]->NumberValue(); + qreal dy = args[2]->NumberValue(); + qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight; + + if (!qIsFinite(dx) || !qIsFinite(dy)) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments"); + + v8::Local imageData = args[0]->ToObject(); + QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast(imageData->Get(v8::String::New("data"))->ToObject()); + if (pixelArray) { + w = imageData->Get(v8::String::New("width"))->NumberValue(); + h = imageData->Get(v8::String::New("height"))->NumberValue(); + + if (args.Length() == 7) { + dirtyX = args[3]->NumberValue(); + dirtyY = args[4]->NumberValue(); + dirtyWidth = args[5]->NumberValue(); + dirtyHeight = args[6]->NumberValue(); + + if (!qIsFinite(dirtyX) || !qIsFinite(dirtyY) || !qIsFinite(dirtyWidth) || !qIsFinite(dirtyHeight)) + V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments"); + + + if (dirtyWidth < 0) { + dirtyX = dirtyX+dirtyWidth; + dirtyWidth = -dirtyWidth; + } + + if (dirtyHeight < 0) { + dirtyY = dirtyY+dirtyHeight; + dirtyHeight = -dirtyHeight; + } + + if (dirtyX < 0) { + dirtyWidth = dirtyWidth+dirtyX; + dirtyX = 0; + } + + if (dirtyY < 0) { + dirtyHeight = dirtyHeight+dirtyY; + dirtyY = 0; + } + + if (dirtyX+dirtyWidth > w) { + dirtyWidth = w - dirtyX; + } + + if (dirtyY+dirtyHeight > h) { + dirtyHeight = h - dirtyY; + } + + if (dirtyWidth <=0 || dirtyHeight <= 0) + return args.This(); + } else { + dirtyX = 0; + dirtyY = 0; + dirtyWidth = w; + dirtyHeight = h; + } + + QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight); + r->context->buffer()->drawImage(image, dirtyX, dirtyY, dirtyWidth, dirtyHeight, dx, dy, dirtyWidth, dirtyHeight); + } + return args.This(); +} + +/*! + \qmlclass QtQuick2::CanvasGradient + \inqmlmodule QtQuick 2 + \since QtQuick 2.0 + \brief The Context2D opaque CanvasGradient interface. + */ + +/*! + \qmlmethod QtQuick2::CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color) + Adds a color stop with the given color to the gradient at the given offset. + 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end. + + For example: + \code + var gradient = ctx.createLinearGradient(0, 0, 100, 100); + gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1)); + gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1'); + \endcode + */ +static v8::Handle ctx2d_gradient_addColorStop(const v8::Arguments &args) +{ + QV8Context2DStyleResource *style = v8_resource_cast(args.This()); + if (!style) + V8THROW_ERROR("Not a CanvasGradient object"); + + QV8Engine *engine = V8ENGINE(); + + if (args.Length() == 2) { + + if (!style->brush.gradient()) + V8THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information"); + QGradient gradient = *(style->brush.gradient()); + qreal pos = args[0]->NumberValue(); + QColor color; + + if (args[1]->IsObject()) { + color = engine->toVariant(args[1], qMetaTypeId()).value(); + } else { + color = qt_color_from_string(args[1]); + } + if (pos < 0.0 || pos > 1.0 || !qIsFinite(pos)) { + V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range"); + } + + if (color.isValid()) { + gradient.setColorAt(pos, color); + } else { + V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string"); + } + style->brush = gradient; + } + + return args.This(); +} + + +void QQuickContext2D::beginPath() +{ + m_path = QPainterPath(); + m_path.setFillRule(state.fillRule); +} + +void QQuickContext2D::closePath() +{ + if (m_path.isEmpty()) + return; + + QRectF boundRect = m_path.boundingRect(); + if (boundRect.width() || boundRect.height()) + m_path.closeSubpath(); + //FIXME:QPainterPath set the current point to (0,0) after close subpath + //should be the first point of the previous subpath +} + +void QQuickContext2D::moveTo( qreal x, qreal y) +{ + //FIXME: moveTo should not close the previous subpath + m_path.moveTo(state.matrix.map(QPointF(x, y))); +} + +void QQuickContext2D::lineTo( qreal x, qreal y) +{ + m_path.lineTo(state.matrix.map(QPointF(x, y))); +} + +void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy, + qreal x, qreal y) +{ + m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)), + state.matrix.map(QPointF(x, y))); +} + +void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y, + qreal cp2x, qreal cp2y, + qreal x, qreal y) +{ + m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)), + state.matrix.map(QPointF(cp2x, cp2y)), + state.matrix.map(QPointF(x, y))); +} + +void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius) +{ + QPointF p0(m_path.currentPosition()); + + QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y())); + QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y())); + float p1p0_length = qSqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); + float p1p2_length = qSqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); + + double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); + + // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8) + // We could have used areCollinear() here, but since we're reusing + // the variables computed above later on we keep this logic. + if (qFuzzyCompare(qAbs(cos_phi), 1.0)) { + m_path.lineTo(p1); + return; + } + + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); + + QPointF orth_p1p0(p1p0.y(), -p1p0.x()); + float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); + float factor_ra = radius / orth_p1p0_length; + + // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 + double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y()); + + QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); + + // calculate angles for addArc + orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y()); + float sa = acos(orth_p1p0.x() / orth_p1p0_length); + if (orth_p1p0.y() < 0.f) + sa = 2 * Q_PI - sa; + + // anticlockwise logic + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); + QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y())); + float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); + float ea = acos(orth_p1p2.x() / orth_p1p2_length); + if (orth_p1p2.y() < 0) + ea = 2 * Q_PI - ea; + if ((sa > ea) && ((sa - ea) < Q_PI)) + anticlockwise = true; + if ((sa < ea) && ((ea - sa) > Q_PI)) + anticlockwise = true; + + arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false); +} + +void QQuickContext2D::arcTo(qreal x1, qreal y1, + qreal x2, qreal y2, + qreal radius) +{ + QPointF st = state.matrix.map(QPointF(x1, y1)); + QPointF end = state.matrix.map(QPointF(x2, y2)); + + if (!m_path.elementCount()) { + m_path.moveTo(st); + } else if (st == m_path.currentPosition() || st == end || !radius) { + m_path.lineTo(st); + } else { + addArcTo(st, end, radius); + } +} + +void QQuickContext2D::rect(qreal x, qreal y, + qreal w, qreal h) +{ + m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h))); +} + +void QQuickContext2D::roundedRect(qreal x, qreal y, + qreal w, qreal h, + qreal xr, qreal yr) +{ + QPainterPath path; + path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize); + m_path.addPath(state.matrix.map(path)); +} + +void QQuickContext2D::ellipse(qreal x, qreal y, + qreal w, qreal h) +{ + QPainterPath path; + path.addEllipse(x, y, w, h); + m_path.addPath(state.matrix.map(path)); +} + +void QQuickContext2D::text(const QString& str, qreal x, qreal y) +{ + QPainterPath path; + path.addText(x, y, state.font, str); + m_path.addPath(state.matrix.map(path)); +} + +void QQuickContext2D::arc(qreal xc, + qreal yc, + qreal radius, + qreal sar, + qreal ear, + bool antiClockWise, + bool transform) +{ + + if (transform) { + QPointF point = state.matrix.map(QPointF(xc, yc)); + xc = point.x(); + yc = point.y(); + } + //### HACK + + // In Qt we don't switch the coordinate system for degrees + // and still use the 0,0 as bottom left for degrees so we need + // to switch + sar = -sar; + ear = -ear; + antiClockWise = !antiClockWise; + //end hack + + float sa = DEGREES(sar); + float ea = DEGREES(ear); + + double span = 0; + + double xs = xc - radius; + double ys = yc - radius; + double width = radius*2; + double height = radius*2; + if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360))) + // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the + // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole + // circumference of this circle. + span = 360; + else { + if (!antiClockWise && (ea < sa)) { + span += 360; + } else if (antiClockWise && (sa < ea)) { + span -= 360; + } + //### this is also due to switched coordinate system + // we would end up with a 0 span instead of 360 + if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) && + qFuzzyCompare(qAbs(span), 360))) { + span += ea - sa; + } + if (!m_path.elementCount()) + m_path.moveTo(xs, ys); + } + + + if (transform) { + QPointF currentPos = m_path.currentPosition(); + QPointF startPos = QPointF(xc + radius * qCos(sar), + yc - radius * qSin(sar)); + if (currentPos != startPos) + m_path.lineTo(startPos); + } + + m_path.arcTo(xs, ys, width, height, sa, span); +} + +int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics) +{ + int offset = 0; + switch (value) { + case QQuickContext2D::Top: + break; + case QQuickContext2D::Alphabetic: + case QQuickContext2D::Middle: + case QQuickContext2D::Hanging: + offset = metrics.ascent(); + break; + case QQuickContext2D::Bottom: + offset = metrics.height(); + break; + } + return offset; +} + +static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text) +{ + int offset = 0; + if (value == QQuickContext2D::Start) + value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Left : QQuickContext2D::Right; + else if (value == QQuickContext2D::End) + value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Right: QQuickContext2D::Left; + switch (value) { + case QQuickContext2D::Center: + offset = metrics.width(text)/2; + break; + case QQuickContext2D::Right: + offset = metrics.width(text); + case QQuickContext2D::Left: + default: + break; + } + return offset; +} + + +QImage QQuickContext2D::createImage(const QUrl& url) +{ + return m_canvas->loadedImage(url); +} + +QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text) +{ + const QFontMetrics metrics(state.font); + int yoffset = baseLineOffset(static_cast(state.textBaseline), metrics); + int xoffset = textAlignOffset(static_cast(state.textAlign), metrics, text); + + QPainterPath textPath; + + textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text); + textPath = state.matrix.map(textPath); + return textPath; +} + + +bool QQuickContext2D::isPointInPath(qreal x, qreal y) const +{ + return m_path.contains(QPointF(x, y)); +} + +QQuickContext2D::QQuickContext2D(QQuickCanvasItem* item) + : m_canvas(item) + , m_buffer(new QQuickContext2DCommandBuffer) + , m_v8engine(0) +{ + reset(); +} + +QQuickContext2D::~QQuickContext2D() +{ +} + +v8::Handle QQuickContext2D::v8value() const +{ + return m_v8value; +} + +QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine) +{ + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + v8::Local ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine)); + ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine)); + ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine)); + ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("roundedRect"), V8FUNCTION(ctx2d_roundedRect, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine)); + ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine)); + ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine)); + + constructorContext = qPersistentNew(ft->GetFunction()); + + v8::Local ftGradient = v8::FunctionTemplate::New(); + ftGradient->InstanceTemplate()->SetHasExternalResource(true); + ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine)); + constructorGradient = qPersistentNew(ftGradient->GetFunction()); + + v8::Local ftPattern = v8::FunctionTemplate::New(); + ftPattern->InstanceTemplate()->SetHasExternalResource(true); + constructorPattern = qPersistentNew(ftPattern->GetFunction()); + + v8::Local ftPixelArray = v8::FunctionTemplate::New(); + ftPixelArray->InstanceTemplate()->SetHasExternalResource(true); + ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine)); + ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine)); + constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction()); + + v8::Local ftImageData = v8::FunctionTemplate::New(); + ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine)); + ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine)); + ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine)); + ftImageData->PrototypeTemplate()->Set(v8::String::New("mirror"), V8FUNCTION(ctx2d_imageData_mirror, engine)); + ftImageData->PrototypeTemplate()->Set(v8::String::New("filter"), V8FUNCTION(ctx2d_imageData_filter, engine)); + ftImageData->InstanceTemplate()->SetInternalFieldCount(1); + constructorImageData = qPersistentNew(ftImageData->GetFunction()); +} + +QQuickContext2DEngineData::~QQuickContext2DEngineData() +{ + qPersistentDispose(constructorContext); + qPersistentDispose(constructorGradient); + qPersistentDispose(constructorPattern); + qPersistentDispose(constructorImageData); + qPersistentDispose(constructorPixelArray); +} + +void QQuickContext2D::popState() +{ + if (m_stateStack.isEmpty()) + return; + + QQuickContext2D::State newState = m_stateStack.pop(); + + if (state.matrix != newState.matrix) + buffer()->updateMatrix(newState.matrix); + + if (newState.globalAlpha != state.globalAlpha) + buffer()->setGlobalAlpha(newState.globalAlpha); + + if (newState.globalCompositeOperation != state.globalCompositeOperation) + buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation); + + if (newState.fillStyle != state.fillStyle) + buffer()->setFillStyle(newState.fillStyle); + + if (newState.strokeStyle != state.strokeStyle) + buffer()->setStrokeStyle(newState.strokeStyle); + + if (newState.lineWidth != state.lineWidth) + buffer()->setLineWidth(newState.lineWidth); + + if (newState.lineCap != state.lineCap) + buffer()->setLineCap(newState.lineCap); + + if (newState.lineJoin != state.lineJoin) + buffer()->setLineJoin(newState.lineJoin); + + if (newState.miterLimit != state.miterLimit) + buffer()->setMiterLimit(newState.miterLimit); + + if (newState.clipPath != state.clipPath) { + buffer()->clip(newState.clipPath); + } + + if (newState.shadowBlur != state.shadowBlur) + buffer()->setShadowBlur(newState.shadowBlur); + + if (newState.shadowColor != state.shadowColor) + buffer()->setShadowColor(newState.shadowColor); + + if (newState.shadowOffsetX != state.shadowOffsetX) + buffer()->setShadowOffsetX(newState.shadowOffsetX); + + if (newState.shadowOffsetY != state.shadowOffsetY) + buffer()->setShadowOffsetY(newState.shadowOffsetY); + state = newState; +} +void QQuickContext2D::pushState() +{ + m_stateStack.push(state); +} + +void QQuickContext2D::reset() +{ + QQuickContext2D::State newState; + newState.matrix = QTransform(); + + QPainterPath defaultClipPath; + + QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height()); + r = r.united(m_canvas->canvasWindow().toRect()); + defaultClipPath.addRect(r); + newState.clipPath = defaultClipPath; + newState.clipPath.setFillRule(Qt::WindingFill); + + newState.strokeStyle = QColor("#000000"); + newState.fillStyle = QColor("#000000"); + newState.fillPatternRepeatX = false; + newState.fillPatternRepeatY = false; + newState.strokePatternRepeatX = false; + newState.strokePatternRepeatY = false; + newState.fillRule = Qt::WindingFill; + newState.globalAlpha = 1.0; + newState.lineWidth = 1; + newState.lineCap = Qt::FlatCap; + newState.lineJoin = Qt::MiterJoin; + newState.miterLimit = 10; + newState.shadowOffsetX = 0; + newState.shadowOffsetY = 0; + newState.shadowBlur = 0; + newState.shadowColor = qRgba(0, 0, 0, 0); + newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver; + newState.font = QFont(QLatin1String("sans-serif"), 10); + newState.textAlign = QQuickContext2D::Start; + newState.textBaseline = QQuickContext2D::Alphabetic; + + m_stateStack.clear(); + m_stateStack.push(newState); + popState(); + m_buffer->clearRect(0, 0, m_canvas->width(), m_canvas->height()); +} + +void QQuickContext2D::setV8Engine(QV8Engine *engine) +{ + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + if (m_v8engine != engine) { + m_v8engine = engine; + + qPersistentDispose(m_v8value); + + if (m_v8engine == 0) + return; + + QQuickContext2DEngineData *ed = engineData(engine); + m_v8value = qPersistentNew(ed->constructorContext->NewInstance()); + QV8Context2DResource *r = new QV8Context2DResource(engine); + r->context = this; + m_v8value->SetExternalResource(r); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/context2d/qquickcontext2d_p.h b/src/declarative/items/context2d/qquickcontext2d_p.h new file mode 100644 index 0000000000..3c5e89c335 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2d_p.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTEXT2D_P_H +#define QQUICKCONTEXT2D_P_H + +#include +#include + +#include +#include +#include +#include +#include + + + +#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose! + +#ifdef QQUICKCONTEXT2D_DEBUG +#include +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickCanvasItem; +class QQuickContext2DCommandBuffer; +class QDeclarativePixmap; + +class Q_DECLARATIVE_EXPORT QQuickContext2D +{ +public: + enum TextBaseLineType { Alphabetic=0, Top, Middle, Bottom, Hanging}; + enum TextAlignType { Start=0, End, Left, Right, Center}; + enum PaintCommand { + Invalid = 0, + UpdateMatrix, + ClearRect, + FillRect, + StrokeRect, + Fill, + Stroke, + Clip, + UpdateBrush, + GlobalAlpha, + GlobalCompositeOperation, + StrokeStyle, + FillStyle, + LineWidth, + LineCap, + LineJoin, + MiterLimit, + ShadowOffsetX, + ShadowOffsetY, + ShadowBlur, + ShadowColor, + Font, + TextBaseline, + TextAlign, + FillText, + StrokeText, + DrawImage, + GetImageData + }; + + + struct State { + QTransform matrix; + QPainterPath clipPath; + QBrush strokeStyle; + QBrush fillStyle; + bool fillPatternRepeatX:1; + bool fillPatternRepeatY:1; + bool strokePatternRepeatX:1; + bool strokePatternRepeatY:1; + Qt::FillRule fillRule; + qreal globalAlpha; + qreal lineWidth; + Qt::PenCapStyle lineCap; + Qt::PenJoinStyle lineJoin; + qreal miterLimit; + qreal shadowOffsetX; + qreal shadowOffsetY; + qreal shadowBlur; + QColor shadowColor; + QPainter::CompositionMode globalCompositeOperation; + QFont font; + QQuickContext2D::TextAlignType textAlign; + QQuickContext2D::TextBaseLineType textBaseline; + }; + + QQuickContext2D(QQuickCanvasItem* item); + ~QQuickContext2D(); + + inline QQuickCanvasItem* canvas() const {return m_canvas;} + inline QQuickContext2DCommandBuffer* buffer() const {return m_buffer;} + + v8::Handle v8value() const; + void setV8Engine(QV8Engine *eng); + void popState(); + void pushState(); + void reset(); + + // path API + void beginPath(); + void closePath(); + void moveTo(qreal x, qreal y); + void lineTo(qreal x, qreal y); + void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y); + void bezierCurveTo(qreal cp1x, qreal cp1y, + qreal cp2x, qreal cp2y, qreal x, qreal y); + void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius); + void rect(qreal x, qreal y, qreal w, qreal h); + void roundedRect(qreal x, qreal y,qreal w, qreal h, qreal xr, qreal yr); + void ellipse(qreal x, qreal y,qreal w, qreal h); + void text(const QString& str, qreal x, qreal y); + void arc(qreal x, qreal y, qreal radius, + qreal startAngle, qreal endAngle, + bool anticlockwise, bool transform=true); + void addArcTo(const QPointF& p1, const QPointF& p2, float radius); + + bool isPointInPath(qreal x, qreal y) const; + + QPainterPath createTextGlyphs(qreal x, qreal y, const QString& text); + QImage createImage(const QUrl& url); + + State state; + QStack m_stateStack; + QQuickCanvasItem* m_canvas; + QQuickContext2DCommandBuffer* m_buffer; + QPainterPath m_path; + v8::Local m_fillStyle; + v8::Local m_strokeStyle; + v8::Handle m_v8path; + QV8Engine *m_v8engine; + v8::Persistent m_v8value; +}; + + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QQuickContext2D) + +QT_END_HEADER + +#endif // QQUICKCONTEXT2D_P_H diff --git a/src/declarative/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/declarative/items/context2d/qquickcontext2dcommandbuffer.cpp new file mode 100644 index 0000000000..e91a21c8b0 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -0,0 +1,470 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontext2dcommandbuffer_p.h" +#include "qquickcanvasitem_p.h" +#include +#include + +#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY)) + +QT_BEGIN_NAMESPACE + +void qt_image_boxblur(QImage& image, int radius, bool quality); + +static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QImage shadowImg(image.width() + blur + qAbs(offsetX), + image.height() + blur + qAbs(offsetY), + QImage::Format_ARGB32_Premultiplied); + shadowImg.fill(0); + QPainter tmpPainter(&shadowImg); + tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); + qreal shadowX = offsetX > 0? offsetX : 0; + qreal shadowY = offsetY > 0? offsetY : 0; + + tmpPainter.drawImage(shadowX, shadowY, image); + tmpPainter.end(); + + if (blur > 0) + qt_image_boxblur(shadowImg, blur/2, true); + + // blacken the image with shadow color... + tmpPainter.begin(&shadowImg); + tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + tmpPainter.fillRect(shadowImg.rect(), color); + tmpPainter.end(); + return shadowImg; +} + +static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QRectF r = shadowRect; + r.moveTo(0, 0); + + QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied); + QPainter tp; + tp.begin(&shadowImage); + tp.fillRect(r, p->brush()); + tp.end(); + shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color); + + qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0); + qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0); + + p->drawImage(dx, dy, shadowImage); + p->fillRect(shadowRect, p->brush()); +} + +static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QRectF r = path.boundingRect(); + QImage img(r.size().width() + r.left() + 1, + r.size().height() + r.top() + 1, + QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter tp(&img); + tp.fillPath(path.translated(0, 0), p->brush()); + tp.end(); + + QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); + qreal dx = r.left() + (offsetX < 0? offsetX:0); + qreal dy = r.top() + (offsetY < 0? offsetY:0); + + p->drawImage(dx, dy, shadowImage); + p->fillPath(path, p->brush()); +} + +static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) +{ + QRectF r = path.boundingRect(); + QImage img(r.size().width() + r.left() + 1, + r.size().height() + r.top() + 1, + QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter tp(&img); + tp.strokePath(path, p->pen()); + tp.end(); + + QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); + qreal dx = r.left() + (offsetX < 0? offsetX:0); + qreal dy = r.top() + (offsetY < 0? offsetY:0); + p->drawImage(dx, dy, shadowImage); + p->strokePath(path, p->pen()); +} +static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY) +{ + // Patterns must be painted so that the top left of the first image is anchored at + // the origin of the coordinate space + if (!image.isNull()) { + int w = image.width(); + int h = image.height(); + int startX, startY; + QRect r(static_cast(rect.x()), static_cast(rect.y()), static_cast(rect.width()), static_cast(rect.height())); + + // startX, startY is the coordinate of the first image we need to put on the left-top of the rect + if (repeatX && repeatY) { + // repeat + // startX, startY is at the left top side of the left-top of the rect + startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); + startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); + } else { + if (!repeatX && !repeatY) { + // no-repeat + // only draw the image once at orgin once, check if need to draw + QRect imageRect(0, 0, w, h); + if (imageRect.intersects(r)) { + startX = 0; + startY = 0; + } else + return; + } else if (repeatX && !repeatY) { + // repeat-x + // startY is fixed, but startX change based on the left-top of the rect + QRect imageRect(r.x(), 0, r.width(), h); + if (imageRect.intersects(r)) { + startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); + startY = 0; + } else + return; + } else { + // repeat-y + // startX is fixed, but startY change based on the left-top of the rect + QRect imageRect(0, r.y(), w, r.height()); + if (imageRect.intersects(r)) { + startX = 0; + startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); + } else + return; + } + } + + int x = startX; + int y = startY; + do { + // repeat Y + do { + // repeat X + QRect imageRect(x, y, w, h); + QRect intersectRect = imageRect.intersected(r); + QPoint destStart(intersectRect.x(), intersectRect.y()); + QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height()); + + p->drawImage(destStart, image, sourceRect); + x += w; + } while (repeatX && x < r.x() + r.width()); + x = startX; + y += h; + } while (repeatY && y < r.y() + r.height()); + } +} + +QPen QQuickContext2DCommandBuffer::makePen(QQuickContext2D::State state) +{ + QPen pen; + pen.setWidthF(state.lineWidth); + pen.setCapStyle(state.lineCap); + pen.setJoinStyle(state.lineJoin); + pen.setMiterLimit(state.miterLimit); + pen.setBrush(state.strokeStyle); + return pen; +} + +void QQuickContext2DCommandBuffer::setPainterState(QPainter* p, QQuickContext2D::State state, const QPen& pen) +{ + p->setTransform(state.matrix * p->transform()); + + if (pen != p->pen()) + p->setPen(pen); + + if (state.fillStyle != p->brush()) + p->setBrush(state.fillStyle); + + if (state.font != p->font()) + p->setFont(state.font); + + if (state.globalAlpha != p->opacity()) { + p->setOpacity(state.globalAlpha); + } + + if (state.globalCompositeOperation != p->compositionMode()) + p->setCompositionMode(state.globalCompositeOperation); +} + +QQuickContext2D::State QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State state) +{ + if (!p) + return state; + + reset(); + + QTransform originMatrix = p->transform(); + + QPen pen = makePen(state); + setPainterState(p, state, pen); + + while (hasNext()) { + QQuickContext2D::PaintCommand cmd = takeNextCommand(); + switch (cmd) { + case QQuickContext2D::UpdateMatrix: + { + state.matrix = takeMatrix(); + p->setTransform(state.matrix * originMatrix); + break; + } + case QQuickContext2D::ClearRect: + { + QPainter::CompositionMode cm = p->compositionMode(); + qreal alpha = p->opacity(); + p->setCompositionMode(QPainter::CompositionMode_Source); + p->setOpacity(0); + p->fillRect(takeRect(), QColor(qRgba(0, 0, 0, 0))); + p->setCompositionMode(cm); + p->setOpacity(alpha); + break; + } + case QQuickContext2D::FillRect: + { + QRectF r = takeRect(); + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) + fillRectShadow(p, r, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + else + p->fillRect(r, p->brush()); + break; + } + case QQuickContext2D::ShadowColor: + { + state.shadowColor = takeColor(); + break; + } + case QQuickContext2D::ShadowBlur: + { + state.shadowBlur = takeShadowBlur(); + break; + } + case QQuickContext2D::ShadowOffsetX: + { + state.shadowOffsetX = takeShadowOffsetX(); + break; + } + case QQuickContext2D::ShadowOffsetY: + { + state.shadowOffsetY = takeShadowOffsetY(); + break; + } + case QQuickContext2D::FillStyle: + { + state.fillStyle = takeFillStyle(); + state.fillPatternRepeatX = takeBool(); + state.fillPatternRepeatY = takeBool(); + p->setBrush(state.fillStyle); + break; + } + case QQuickContext2D::StrokeStyle: + { + state.strokeStyle = takeStrokeStyle(); + state.strokePatternRepeatX = takeBool(); + state.strokePatternRepeatY = takeBool(); + pen.setBrush(state.strokeStyle); + p->setPen(pen); + break; + } + case QQuickContext2D::LineWidth: + { + state.lineWidth = takeLineWidth(); + pen.setWidth(state.lineWidth); + p->setPen(pen); + break; + } + case QQuickContext2D::LineCap: + { + state.lineCap = takeLineCap(); + pen.setCapStyle(state.lineCap); + p->setPen(pen); + break; + } + case QQuickContext2D::LineJoin: + { + state.lineJoin = takeLineJoin(); + pen.setJoinStyle(state.lineJoin); + p->setPen(pen); + break; + } + case QQuickContext2D::MiterLimit: + { + state.miterLimit = takeMiterLimit(); + pen.setMiterLimit(state.miterLimit); + p->setPen(pen); + break; + } + case QQuickContext2D::TextAlign: + case QQuickContext2D::TextBaseline: + break; + case QQuickContext2D::Fill: + { + QPainterPath path = takePath(); + path.closeSubpath(); + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) + fillShadowPath(p,path, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + else + p->fillPath(path, p->brush()); + break; + } + case QQuickContext2D::Stroke: + { + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) + strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + else + p->strokePath(takePath(), p->pen()); + break; + } + case QQuickContext2D::Clip: + { + state.clipPath = takePath(); + p->setClipping(true); + p->setClipPath(state.clipPath); + break; + } + case QQuickContext2D::GlobalAlpha: + { + state.globalAlpha = takeGlobalAlpha(); + p->setOpacity(state.globalAlpha); + break; + } + case QQuickContext2D::GlobalCompositeOperation: + { + state.globalCompositeOperation = takeGlobalCompositeOperation(); + p->setCompositionMode(state.globalCompositeOperation); + break; + } + case QQuickContext2D::DrawImage: + { + qreal sx = takeReal(); + qreal sy = takeReal(); + qreal sw = takeReal(); + qreal sh = takeReal(); + qreal dx = takeReal(); + qreal dy = takeReal(); + qreal dw = takeReal(); + qreal dh = takeReal(); + QImage image = takeImage(); + + if (!image.isNull()) { + if (sw == -1 || sh == -1) { + sw = image.width(); + sh = image.height(); + } + if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height()) + image = image.copy(sx, sy, sw, sh); + + image = image.scaled(dw, dh); + + if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) { + QImage shadow = makeShadowImage(image, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); + qreal shadow_dx = dx + (state.shadowOffsetX < 0? state.shadowOffsetY:0); + qreal shadow_dy = dy + (state.shadowOffsetX < 0? state.shadowOffsetY:0); + p->drawImage(shadow_dx, shadow_dy, shadow); + } + p->drawImage(dx, dy, image); + } + break; + } + case QQuickContext2D::GetImageData: + { + //TODO: + break; + } + default: + break; + } + } + + p->end(); + return state; +} + +QQuickContext2DCommandBuffer::QQuickContext2DCommandBuffer() + : cmdIdx(0) + , intIdx(0) + , boolIdx(0) + , realIdx(0) + , colorIdx(0) + , matrixIdx(0) + , brushIdx(0) + , pathIdx(0) + , imageIdx(0) +{ +} + + +QQuickContext2DCommandBuffer::~QQuickContext2DCommandBuffer() +{ +} + +void QQuickContext2DCommandBuffer::clear() +{ + commands.clear(); + ints.clear(); + bools.clear(); + reals.clear(); + colors.clear(); + matrixes.clear(); + brushes.clear(); + pathes.clear(); + images.clear(); + reset(); +} + +void QQuickContext2DCommandBuffer::reset() +{ + cmdIdx = 0; + intIdx = 0; + boolIdx = 0; + realIdx = 0; + colorIdx = 0; + matrixIdx = 0; + brushIdx = 0; + pathIdx = 0; + imageIdx = 0; +} + +QT_END_NAMESPACE + diff --git a/src/declarative/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/declarative/items/context2d/qquickcontext2dcommandbuffer_p.h new file mode 100644 index 0000000000..46964d3411 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dcommandbuffer_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTEXT2DCOMMANDBUFFER_P_H +#define QQUICKCONTEXT2DCOMMANDBUFFER_P_H + +#include "qquickcontext2d_p.h" +#include + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickCanvasItem; +class QMutex; + +class QQuickContext2DCommandBuffer +{ +public: + QQuickContext2DCommandBuffer(); + ~QQuickContext2DCommandBuffer(); + void reset(); + void clear(); + inline int size() {return commands.size();} + inline bool isEmpty() const {return commands.isEmpty(); } + inline bool hasNext() const {return cmdIdx < commands.size(); } + inline QQuickContext2D::PaintCommand takeNextCommand() { return commands[cmdIdx++]; } + + inline qreal takeGlobalAlpha() { return takeReal(); } + inline QPainter::CompositionMode takeGlobalCompositeOperation(){ return static_cast(takeInt()); } + inline QBrush takeStrokeStyle() { return takeBrush(); } + inline QBrush takeFillStyle() { return takeBrush(); } + + inline qreal takeLineWidth() { return takeReal(); } + inline Qt::PenCapStyle takeLineCap() { return static_cast(takeInt());} + inline Qt::PenJoinStyle takeLineJoin(){ return static_cast(takeInt());} + inline qreal takeMiterLimit() { return takeReal(); } + + inline void setGlobalAlpha( qreal alpha) + { + commands << QQuickContext2D::GlobalAlpha; + reals << alpha; + } + + inline void setGlobalCompositeOperation(QPainter::CompositionMode cm) + { + commands << QQuickContext2D::GlobalCompositeOperation; + ints << cm; + } + + inline void setStrokeStyle(const QBrush &style, bool repeatX = false, bool repeatY = false) + { + commands << QQuickContext2D::StrokeStyle; + brushes << style; + bools << repeatX << repeatY; + } + + inline void drawImage(const QImage& image, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh) + { + commands << QQuickContext2D::DrawImage; + images << image; + reals << sx << sy << sw << sh << dx << dy << dw << dh; + } + + inline qreal takeShadowOffsetX() { return takeReal(); } + inline qreal takeShadowOffsetY() { return takeReal(); } + inline qreal takeShadowBlur() { return takeReal(); } + inline QColor takeShadowColor() { return takeColor(); } + + + inline void updateMatrix(const QTransform& matrix) + { + commands << QQuickContext2D::UpdateMatrix; + matrixes << matrix; + } + + inline void clearRect(qreal x, qreal y, qreal w, qreal h) + { + commands << QQuickContext2D::ClearRect; + reals << x << y << w << h; + } + + inline void fillRect(qreal x, qreal y, qreal w, qreal h) + { + commands << QQuickContext2D::FillRect; + reals << x << y << w << h; + } + + inline void strokeRect(qreal x, qreal y, qreal w, qreal h) + { + QPainterPath p; + p.addRect(x, y, w, h); + + commands << QQuickContext2D::Stroke; + pathes << p; + } + + + inline void fill(const QPainterPath& path) + { + commands << QQuickContext2D::Fill; + pathes << path; + + } + + inline void stroke(const QPainterPath& path) + { + commands << QQuickContext2D::Stroke; + pathes << path; + } + + inline void clip(const QPainterPath& path) + { + commands << QQuickContext2D::Clip; + pathes << path; + } + + + + inline void setFillStyle(const QBrush &style, bool repeatX = false, bool repeatY = false) + { + commands << QQuickContext2D::FillStyle; + brushes << style; + bools << repeatX << repeatY; + } + + + inline void setLineWidth( qreal w) + { + commands << QQuickContext2D::LineWidth; + reals << w; + } + + inline void setLineCap(Qt::PenCapStyle cap) + { + commands << QQuickContext2D::LineCap; + ints << cap; + } + + inline void setLineJoin(Qt::PenJoinStyle join) + { + commands << QQuickContext2D::LineJoin; + ints << join; + } + + inline void setMiterLimit( qreal limit) + { + commands << QQuickContext2D::MiterLimit; + reals << limit; + } + + inline void setShadowOffsetX( qreal x) + { + commands << QQuickContext2D::ShadowOffsetX; + reals << x; + } + + inline void setShadowOffsetY( qreal y) + { + commands << QQuickContext2D::ShadowOffsetY; + reals << y; + } + + inline void setShadowBlur( qreal b) + { + commands << QQuickContext2D::ShadowBlur; + reals << b; + } + + inline void setShadowColor(const QColor &color) + { + commands << QQuickContext2D::ShadowColor; + colors << color; + } + + inline QTransform takeMatrix() { return matrixes[matrixIdx++]; } + + // rects + inline QRectF takeRect() { + qreal x, y, w, h; + x = takeReal(); + y = takeReal(); + w = takeReal(); + h = takeReal(); + return QRectF(x, y, w ,h); + } + + inline QPainterPath takePath() { return pathes[pathIdx++]; } + + inline const QImage& takeImage() { return images[imageIdx++]; } + + inline int takeInt() { return ints[intIdx++]; } + inline bool takeBool() {return bools[boolIdx++]; } + inline qreal takeReal() { return reals[realIdx++]; } + inline QColor takeColor() { return colors[colorIdx++]; } + inline QBrush takeBrush() { return brushes[brushIdx++]; } + + QQuickContext2D::State replay(QPainter* painter, QQuickContext2D::State state); +private: + QPen makePen(QQuickContext2D::State state); + void setPainterState(QPainter* painter, QQuickContext2D::State state, const QPen& pen); + int cmdIdx; + int intIdx; + int boolIdx; + int realIdx; + int colorIdx; + int matrixIdx; + int brushIdx; + int pathIdx; + int imageIdx; + QVector commands; + + QVector ints; + QVector bools; + QVector reals; + QVector colors; + QVector matrixes; + QVector brushes; + QVector pathes; + QVector images; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // QQUICKCONTEXT2DCOMMANDBUFFER_P_H diff --git a/src/declarative/items/context2d/qquickcontext2dnode.cpp b/src/declarative/items/context2d/qquickcontext2dnode.cpp new file mode 100644 index 0000000000..76b50d9747 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dnode.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontext2dnode_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + + +QQuickContext2DNode::QQuickContext2DNode(QQuickCanvasItem* item) + : QSGGeometryNode() + , m_item(item) + , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) + , m_texture(0) + , m_size(1, 1) + , m_dirtyGeometry(false) + , m_dirtyTexture(false) +{ + setMaterial(&m_materialO); + setOpaqueMaterial(&m_material); + setGeometry(&m_geometry); + setFlag(UsePreprocess, true); +} + +QQuickContext2DNode::~QQuickContext2DNode() +{ + delete m_texture; +} + +void QQuickContext2DNode::setSize(const QSizeF& size) +{ + if (m_size != size) { + m_dirtyGeometry = true; + m_size = size; + } +} + +void QQuickContext2DNode::preprocess() +{ + bool doDirty = false; + QSGDynamicTexture *t = qobject_cast(m_material.texture()); + if (t) { + doDirty = t->updateTexture(); + } + if (doDirty) { + m_dirtyTexture = true; + markDirty(DirtyMaterial); + } +} +void QQuickContext2DNode::setTexture(QQuickContext2DTexture* texture) +{ + if (texture != m_texture) { + m_dirtyTexture = true; + m_texture = texture; + } +} + +void QQuickContext2DNode::update() +{ + if (m_dirtyGeometry) + updateGeometry(); + if (m_dirtyTexture) + updateTexture(); + + m_dirtyGeometry = false; + m_dirtyTexture = false; +} + +void QQuickContext2DNode::updateTexture() +{ + m_material.setTexture(m_texture); + m_materialO.setTexture(m_texture); + markDirty(DirtyMaterial); +} + +void QQuickContext2DNode::updateGeometry() +{ + QRectF source = m_texture->textureSubRect(); + QSGGeometry::updateTexturedRectGeometry(&m_geometry, + QRectF(0, 0, m_size.width(), m_size.height()), + source); + markDirty(DirtyGeometry); +} +QT_END_NAMESPACE diff --git a/src/declarative/items/context2d/qquickcontext2dnode_p.h b/src/declarative/items/context2d/qquickcontext2dnode_p.h new file mode 100644 index 0000000000..7403acaf07 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dnode_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTEXT2DNODE_P_H +#define QQUICKCONTEXT2DNODE_P_H + +#include +#include + +#include "qquickcanvasitem_p.h" +#include "qquickcontext2dtexture_p.h" +#include "qquickcontext2d_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickContext2DNode : public QSGGeometryNode +{ +public: + QQuickContext2DNode(QQuickCanvasItem* item); + virtual ~QQuickContext2DNode(); + void setTexture(QQuickContext2DTexture* texture); + void update(); + void preprocess(); + void setSize(const QSizeF& size); +private: + void updateTexture(); + void updateGeometry(); + + QQuickCanvasItem* m_item; + QSGOpaqueTextureMaterial m_material; + QSGTextureMaterial m_materialO; + QSGGeometry m_geometry; + QQuickContext2DTexture* m_texture; + QSizeF m_size; + + bool m_dirtyGeometry; + bool m_dirtyTexture; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // QQUICKCONTEXT2DNODE_P_H diff --git a/src/declarative/items/context2d/qquickcontext2dtexture.cpp b/src/declarative/items/context2d/qquickcontext2dtexture.cpp new file mode 100644 index 0000000000..9518f91d45 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dtexture.cpp @@ -0,0 +1,777 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontext2dtexture_p.h" +#include "qquickcontext2dtile_p.h" +#include "qquickcanvasitem_p.h" +#include +#include +#include "qquickcontext2dcommandbuffer_p.h" +#include + +#include +#include +#include + +#define QT_MINIMUM_FBO_SIZE 64 + +static inline int qt_next_power_of_two(int v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + ++v; + return v; +} + + +Q_GLOBAL_STATIC(QThread, globalCanvasThreadRenderInstance) + + +QQuickContext2DTexture::QQuickContext2DTexture() + : QSGDynamicTexture() + , m_context(0) + , m_canvasSize(QSize(1, 1)) + , m_tileSize(QSize(1, 1)) + , m_canvasWindow(QRect(0, 0, 1, 1)) + , m_dirtyCanvas(false) + , m_dirtyTexture(false) + , m_threadRendering(false) + , m_smooth(false) + , m_tiledCanvas(false) + , m_doGrabImage(false) + , m_painting(false) +{ +} + +QQuickContext2DTexture::~QQuickContext2DTexture() +{ + clearTiles(); +} + +QSize QQuickContext2DTexture::textureSize() const +{ + return m_canvasWindow.size(); +} + +void QQuickContext2DTexture::markDirtyTexture() +{ + lock(); + m_dirtyTexture = true; + unlock(); + emit textureChanged(); +} + +bool QQuickContext2DTexture::setCanvasSize(const QSize &size) +{ + if (m_canvasSize != size) { + m_canvasSize = size; + m_dirtyCanvas = true; + return true; + } + return false; +} + +bool QQuickContext2DTexture::setTileSize(const QSize &size) +{ + if (m_tileSize != size) { + m_tileSize = size; + m_dirtyCanvas = true; + return true; + } + return false; +} + +void QQuickContext2DTexture::setSmooth(bool smooth) +{ + m_smooth = smooth; +} + +void QQuickContext2DTexture::setItem(QQuickCanvasItem* item) +{ + if (!item) { + lock(); + m_item = 0; + m_context = 0; + unlock(); + wake(); + } else if (m_item != item) { + lock(); + m_item = item; + m_context = item->context(); + m_state = m_context->state; + unlock(); + connect(this, SIGNAL(textureChanged()), m_item, SIGNAL(painted())); + } +} + +bool QQuickContext2DTexture::setCanvasWindow(const QRect& r) +{ + if (m_canvasWindow != r) { + m_canvasWindow = r; + return true; + } + return false; +} + +bool QQuickContext2DTexture::setDirtyRect(const QRect &r) +{ + bool doDirty = false; + if (m_tiledCanvas) { + foreach (QQuickContext2DTile* t, m_tiles) { + bool dirty = t->rect().intersected(r).isValid(); + t->markDirty(dirty); + if (dirty) + doDirty = true; + } + } else { + doDirty = m_canvasWindow.intersected(r).isValid(); + } + return doDirty; +} + +void QQuickContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth) +{ + lock(); + + QSize ts = tileSize; + if (ts.width() > canvasSize.width()) + ts.setWidth(canvasSize.width()); + + if (ts.height() > canvasSize.height()) + ts.setHeight(canvasSize.height()); + + setCanvasSize(canvasSize); + setTileSize(ts); + + if (canvasSize == canvasWindow.size()) { + m_tiledCanvas = false; + m_dirtyCanvas = false; + } else { + m_tiledCanvas = true; + } + + bool doDirty = false; + if (dirtyRect.isValid()) + doDirty = setDirtyRect(dirtyRect); + + bool windowChanged = setCanvasWindow(canvasWindow); + if (windowChanged || doDirty) { + if (m_threadRendering) + QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection); + else if (supportDirectRendering()) { + QMetaObject::invokeMethod(this, "paint", Qt::DirectConnection); + } + } + + setSmooth(smooth); + unlock(); +} + +void QQuickContext2DTexture::paintWithoutTiles() +{ + QQuickContext2DCommandBuffer* ccb = m_context->buffer(); + + if (ccb->isEmpty() && m_threadRendering && !m_doGrabImage) { + lock(); + if (m_item) + QMetaObject::invokeMethod(m_item, "_doPainting", Qt::QueuedConnection, Q_ARG(QRectF, QRectF(0, 0, m_canvasSize.width(), m_canvasSize.height()))); + wait(); + unlock(); + } + if (ccb->isEmpty()) { + return; + } + + QPaintDevice* device = beginPainting(); + if (!device) { + endPainting(); + return; + } + + QPainter p; + p.begin(device); + if (m_smooth) + p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing + | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + else + p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing + | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, false); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + m_state = ccb->replay(&p, m_state); + + ccb->clear(); + markDirtyTexture(); + endPainting(); +} + +bool QQuickContext2DTexture::canvasDestroyed() +{ + bool noCanvas = false; + lock(); + noCanvas = m_item == 0; + unlock(); + return noCanvas; +} + +void QQuickContext2DTexture::paint() +{ + if (canvasDestroyed()) + return; + + if (!m_tiledCanvas) { + paintWithoutTiles(); + } else { + QQuickContext2D::State oldState = m_state; + QQuickContext2DCommandBuffer* ccb = m_context->buffer(); + + lock(); + QRect tiledRegion = createTiles(m_canvasWindow.intersected(QRect(QPoint(0, 0), m_canvasSize))); + unlock(); + + if (!tiledRegion.isEmpty()) { + if (m_threadRendering && !m_doGrabImage) { + QRect dirtyRect; + + lock(); + foreach (QQuickContext2DTile* tile, m_tiles) { + if (tile->dirty()) { + if (dirtyRect.isEmpty()) + dirtyRect = tile->rect(); + else + dirtyRect |= tile->rect(); + } + } + unlock(); + + if (dirtyRect.isValid()) { + lock(); + if (m_item) + QMetaObject::invokeMethod(m_item, "_doPainting", Qt::QueuedConnection, Q_ARG(QRectF, dirtyRect)); + wait(); + unlock(); + } + } + + if (beginPainting()) { + foreach (QQuickContext2DTile* tile, m_tiles) { + bool dirtyTile = false, dirtyCanvas = false, smooth = false; + + lock(); + dirtyTile = tile->dirty(); + smooth = m_smooth; + dirtyCanvas = m_dirtyCanvas; + unlock(); + + //canvas size or tile size may change during painting tiles + if (dirtyCanvas) { + if (m_threadRendering) + QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection); + endPainting(); + return; + } else if (dirtyTile) { + m_state = ccb->replay(tile->createPainter(smooth), oldState); + tile->drawFinished(); + lock(); + tile->markDirty(false); + unlock(); + } + + compositeTile(tile); + } + ccb->clear(); + endPainting(); + markDirtyTexture(); + } + } + } +} + +QRect QQuickContext2DTexture::tiledRect(const QRectF& window, const QSize& tileSize) +{ + if (window.isEmpty()) + return QRect(); + + const int tw = tileSize.width(); + const int th = tileSize.height(); + const int h1 = window.left() / tw; + const int v1 = window.top() / th; + + const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw; + const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th; + + return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th); +} + +QRect QQuickContext2DTexture::createTiles(const QRect& window) +{ + QList oldTiles = m_tiles; + m_tiles.clear(); + + if (window.isEmpty()) { + m_dirtyCanvas = false; + return QRect(); + } + + QRect r = tiledRect(window, m_tileSize); + + const int tw = m_tileSize.width(); + const int th = m_tileSize.height(); + const int h1 = window.left() / tw; + const int v1 = window.top() / th; + + + const int htiles = r.width() / tw; + const int vtiles = r.height() / th; + + for (int yy = 0; yy < vtiles; ++yy) { + for (int xx = 0; xx < htiles; ++xx) { + int ht = xx + h1; + int vt = yy + v1; + + QQuickContext2DTile* tile = 0; + + QPoint pos(ht * tw, vt * th); + QRect rect(pos, m_tileSize); + + for (int i = 0; i < oldTiles.size(); i++) { + if (oldTiles[i]->rect() == rect) { + tile = oldTiles.takeAt(i); + break; + } + } + + if (!tile) + tile = createTile(); + + tile->setRect(rect); + m_tiles.append(tile); + } + } + + qDeleteAll(oldTiles); + + m_dirtyCanvas = false; + return r; +} + +void QQuickContext2DTexture::clearTiles() +{ + qDeleteAll(m_tiles); + m_tiles.clear(); +} + +QQuickContext2DFBOTexture::QQuickContext2DFBOTexture() + : QQuickContext2DTexture() + , m_fbo(0) + , m_multisampledFbo(0) + , m_paint_device(0) +{ + m_threadRendering = false; +} + +QQuickContext2DFBOTexture::~QQuickContext2DFBOTexture() +{ + delete m_fbo; + delete m_multisampledFbo; + delete m_paint_device; +} + +bool QQuickContext2DFBOTexture::setCanvasSize(const QSize &size) +{ + QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.width())) + , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.height()))); + + if (m_canvasSize != s) { + m_canvasSize = s; + m_dirtyCanvas = true; + return true; + } + return false; +} + +bool QQuickContext2DFBOTexture::setTileSize(const QSize &size) +{ + QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.width())) + , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.height()))); + if (m_tileSize != s) { + m_tileSize = s; + m_dirtyCanvas = true; + return true; + } + return false; +} + +bool QQuickContext2DFBOTexture::setCanvasWindow(const QRect& canvasWindow) +{ + QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(canvasWindow.size().width())) + , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(canvasWindow.size().height()))); + + + bool doChanged = false; + if (m_fboSize != s) { + m_fboSize = s; + doChanged = true; + } + + if (m_canvasWindow != canvasWindow) + m_canvasWindow = canvasWindow; + + return doChanged; +} + +void QQuickContext2DFBOTexture::bind() +{ + glBindTexture(GL_TEXTURE_2D, textureId()); + updateBindOptions(); +} + +QRectF QQuickContext2DFBOTexture::textureSubRect() const +{ + return QRectF(0 + , 0 + , qreal(m_canvasWindow.width()) / m_fboSize.width() + , qreal(m_canvasWindow.height()) / m_fboSize.height()); +} + + +int QQuickContext2DFBOTexture::textureId() const +{ + return m_fbo? m_fbo->texture() : 0; +} + + +bool QQuickContext2DFBOTexture::updateTexture() +{ + if (!m_context->buffer()->isEmpty()) { + paint(); + } + + bool textureUpdated = m_dirtyTexture; + + m_dirtyTexture = false; + + if (m_doGrabImage) { + grabImage(); + m_condition.wakeOne(); + m_doGrabImage = false; + } + return textureUpdated; +} + +QQuickContext2DTile* QQuickContext2DFBOTexture::createTile() const +{ + return new QQuickContext2DFBOTile(); +} + +void QQuickContext2DFBOTexture::grabImage() +{ + if (m_fbo) { + m_grabedImage = m_fbo->toImage(); + } +} + +bool QQuickContext2DFBOTexture::doMultisampling() const +{ + static bool extensionsChecked = false; + static bool multisamplingSupported = false; + + if (!extensionsChecked) { + QList extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' '); + multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample") + && extensions.contains("GL_EXT_framebuffer_blit"); + extensionsChecked = true; + } + + return multisamplingSupported && m_smooth; +} + +QImage QQuickContext2DFBOTexture::toImage(const QRectF& region) +{ +#define QML_CONTEXT2D_WAIT_MAX 5000 + + m_doGrabImage = true; + if (m_item) + m_item->update(); + + QImage grabbed; + m_mutex.lock(); + bool ok = m_condition.wait(&m_mutex, QML_CONTEXT2D_WAIT_MAX); + + if (!ok) + grabbed = QImage(); + + if (region.isValid()) + grabbed = m_grabedImage.copy(region.toRect()); + else + grabbed = m_grabedImage; + m_grabedImage = QImage(); + return grabbed; +} + +void QQuickContext2DFBOTexture::compositeTile(QQuickContext2DTile* tile) +{ + QQuickContext2DFBOTile* t = static_cast(tile); + QRect target = t->rect().intersect(m_canvasWindow); + if (target.isValid()) { + QRect source = target; + + source.moveTo(source.topLeft() - t->rect().topLeft()); + target.moveTo(target.topLeft() - m_canvasWindow.topLeft()); + + QOpenGLFramebufferObject::blitFramebuffer(m_fbo, target, t->fbo(), source); + } +} +QQuickCanvasItem::RenderTarget QQuickContext2DFBOTexture::renderTarget() const +{ + return QQuickCanvasItem::FramebufferObject; +} +QPaintDevice* QQuickContext2DFBOTexture::beginPainting() +{ + QQuickContext2DTexture::beginPainting(); + + if (m_canvasWindow.size().isEmpty() && !m_threadRendering) { + delete m_fbo; + delete m_multisampledFbo; + m_fbo = 0; + m_multisampledFbo = 0; + return 0; + } else if (!m_fbo || m_fbo->size() != m_fboSize) { + delete m_fbo; + delete m_multisampledFbo; + if (doMultisampling()) { + { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setSamples(8); + m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format); + } + { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::NoAttachment); + m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); + } + } else { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + + m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); + } + } + + if (doMultisampling()) + m_multisampledFbo->bind(); + else + m_fbo->bind(); + + + if (!m_paint_device) { + QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(m_fbo->size()); + gl_device->setPaintFlipped(true); + m_paint_device = gl_device; + } + + return m_paint_device; +} + +void QQuickContext2DFBOTexture::endPainting() +{ + QQuickContext2DTexture::endPainting(); + if (m_multisampledFbo) { + QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); + m_multisampledFbo->release(); + } else if (m_fbo) + m_fbo->release(); +} +void qt_quit_context2d_render_thread() +{ + QThread* thread = globalCanvasThreadRenderInstance(); + + if (thread->isRunning()) { + thread->exit(0); + thread->wait(1000); + } +} + +QQuickContext2DImageTexture::QQuickContext2DImageTexture(bool threadRendering) + : QQuickContext2DTexture() + , m_texture(new QSGPlainTexture()) +{ + m_texture->setOwnsTexture(true); + m_texture->setHasMipmaps(false); + + m_threadRendering = threadRendering; + + if (m_threadRendering) { + QThread* thread = globalCanvasThreadRenderInstance(); + moveToThread(thread); + + if (!thread->isRunning()) { + qAddPostRoutine(qt_quit_context2d_render_thread); + thread->start(); + } + } +} + +QQuickContext2DImageTexture::~QQuickContext2DImageTexture() +{ + delete m_texture; +} + +int QQuickContext2DImageTexture::textureId() const +{ + return m_texture->textureId(); +} + +void QQuickContext2DImageTexture::lock() +{ + if (m_threadRendering) + m_mutex.lock(); +} +void QQuickContext2DImageTexture::unlock() +{ + if (m_threadRendering) + m_mutex.unlock(); +} + +void QQuickContext2DImageTexture::wait() +{ + if (m_threadRendering) + m_waitCondition.wait(&m_mutex); +} + +void QQuickContext2DImageTexture::wake() +{ + if (m_threadRendering) + m_waitCondition.wakeOne(); +} + +bool QQuickContext2DImageTexture::supportDirectRendering() const +{ + return !m_threadRendering; +} + +QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const +{ + return QQuickCanvasItem::Image; +} + +void QQuickContext2DImageTexture::bind() +{ + m_texture->bind(); +} + +bool QQuickContext2DImageTexture::updateTexture() +{ + lock(); + bool textureUpdated = m_dirtyTexture; + if (m_dirtyTexture) { + m_texture->setImage(m_image); + m_dirtyTexture = false; + } + unlock(); + return textureUpdated; +} + +QQuickContext2DTile* QQuickContext2DImageTexture::createTile() const +{ + return new QQuickContext2DImageTile(); +} + +void QQuickContext2DImageTexture::grabImage(const QRect& r) +{ + m_doGrabImage = true; + paint(); + m_doGrabImage = false; + m_grabedImage = m_image.copy(r); +} + +QImage QQuickContext2DImageTexture::toImage(const QRectF& region) +{ + QRect r = region.isValid() ? region.toRect() : QRect(QPoint(0, 0), m_canvasWindow.size()); + if (threadRendering()) { + wake(); + QMetaObject::invokeMethod(this, "grabImage", Qt::BlockingQueuedConnection, Q_ARG(QRect, r)); + } else { + QMetaObject::invokeMethod(this, "grabImage", Qt::DirectConnection, Q_ARG(QRect, r)); + } + QImage image = m_grabedImage; + m_grabedImage = QImage(); + return image; +} + +QPaintDevice* QQuickContext2DImageTexture::beginPainting() +{ + QQuickContext2DTexture::beginPainting(); + + if (m_canvasWindow.size().isEmpty()) + return 0; + + lock(); + if (m_image.size() != m_canvasWindow.size()) { + m_image = QImage(m_canvasWindow.size(), QImage::Format_ARGB32_Premultiplied); + m_image.fill(0x00000000); + } + unlock(); + return &m_image; +} + +void QQuickContext2DImageTexture::compositeTile(QQuickContext2DTile* tile) +{ + Q_ASSERT(!tile->dirty()); + QQuickContext2DImageTile* t = static_cast(tile); + QRect target = t->rect().intersect(m_canvasWindow); + if (target.isValid()) { + QRect source = target; + source.moveTo(source.topLeft() - t->rect().topLeft()); + target.moveTo(target.topLeft() - m_canvasWindow.topLeft()); + + lock(); + m_painter.begin(&m_image); + m_painter.setCompositionMode(QPainter::CompositionMode_Source); + m_painter.drawImage(target, t->image(), source); + m_painter.end(); + unlock(); + } +} diff --git a/src/declarative/items/context2d/qquickcontext2dtexture_p.h b/src/declarative/items/context2d/qquickcontext2dtexture_p.h new file mode 100644 index 0000000000..ed45a09f7c --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dtexture_p.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTEXT2DTEXTURE_P_H +#define QQUICKCONTEXT2DTEXTURE_P_H + +#include +#include "qquickcanvasitem_p.h" +#include "qquickcontext2d_p.h" + +#include +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickContext2DTile; +class QQuickContext2DCommandBuffer; + +class QQuickContext2DTexture : public QSGDynamicTexture +{ + Q_OBJECT +public: + QQuickContext2DTexture(); + ~QQuickContext2DTexture(); + + virtual bool hasAlphaChannel() const {return true;} + virtual bool hasMipmaps() const {return false;} + virtual QSize textureSize() const; + virtual void lock() {} + virtual void unlock() {} + virtual void wait() {} + virtual void wake() {} + bool threadRendering() const {return m_threadRendering;} + virtual bool supportThreadRendering() const = 0; + virtual bool supportDirectRendering() const = 0; + virtual QQuickCanvasItem::RenderTarget renderTarget() const = 0; + virtual QImage toImage(const QRectF& region = QRectF()) = 0; + static QRect tiledRect(const QRectF& window, const QSize& tileSize); + + virtual bool setCanvasSize(const QSize &size); + virtual bool setTileSize(const QSize &size); + virtual bool setCanvasWindow(const QRect& canvasWindow); + void setSmooth(bool smooth); + bool setDirtyRect(const QRect &dirtyRect); + virtual void canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth); + bool canvasDestroyed(); +Q_SIGNALS: + void textureChanged(); + +public Q_SLOTS: + void markDirtyTexture(); + void setItem(QQuickCanvasItem* item); + void paint(); + +protected: + void paintWithoutTiles(); + virtual QPaintDevice* beginPainting() {m_painting = true; return 0; } + virtual void endPainting() {m_painting = false;} + virtual QQuickContext2DTile* createTile() const = 0; + virtual void compositeTile(QQuickContext2DTile* tile) = 0; + + void clearTiles(); + QRect createTiles(const QRect& window); + + QList m_tiles; + QQuickContext2D* m_context; + + QQuickContext2D::State m_state; + + QQuickCanvasItem* m_item; + QSize m_canvasSize; + QSize m_tileSize; + QRect m_canvasWindow; + + uint m_dirtyCanvas : 1; + uint m_dirtyTexture : 1; + uint m_threadRendering : 1; + uint m_smooth : 1; + uint m_tiledCanvas : 1; + uint m_doGrabImage : 1; + uint m_painting : 1; +}; + +class QQuickContext2DFBOTexture : public QQuickContext2DTexture +{ + Q_OBJECT + +public: + QQuickContext2DFBOTexture(); + ~QQuickContext2DFBOTexture(); + virtual int textureId() const; + virtual bool updateTexture(); + virtual QQuickContext2DTile* createTile() const; + virtual QImage toImage(const QRectF& region = QRectF()); + virtual QPaintDevice* beginPainting(); + virtual void endPainting(); + QRectF textureSubRect() const; + virtual bool supportThreadRendering() const {return false;} + virtual bool supportDirectRendering() const {return false;} + virtual QQuickCanvasItem::RenderTarget renderTarget() const; + virtual void compositeTile(QQuickContext2DTile* tile); + virtual void bind(); + virtual bool setCanvasSize(const QSize &size); + virtual bool setTileSize(const QSize &size); + virtual bool setCanvasWindow(const QRect& canvasWindow); +private Q_SLOTS: + void grabImage(); + +private: + bool doMultisampling() const; + QImage m_grabedImage; + QOpenGLFramebufferObject *m_fbo; + QOpenGLFramebufferObject *m_multisampledFbo; + QMutex m_mutex; + QWaitCondition m_condition; + QSize m_fboSize; + QPaintDevice *m_paint_device; +}; + +class QSGPlainTexture; +class QQuickContext2DImageTexture : public QQuickContext2DTexture +{ + Q_OBJECT + +public: + QQuickContext2DImageTexture(bool threadRendering = true); + ~QQuickContext2DImageTexture(); + virtual int textureId() const; + virtual void bind(); + virtual bool supportThreadRendering() const {return true;} + virtual bool supportDirectRendering() const; + virtual QQuickCanvasItem::RenderTarget renderTarget() const; + virtual void lock(); + virtual void unlock(); + virtual void wait(); + virtual void wake(); + + virtual bool updateTexture(); + virtual QQuickContext2DTile* createTile() const; + virtual QImage toImage(const QRectF& region = QRectF()); + virtual QPaintDevice* beginPainting(); + virtual void compositeTile(QQuickContext2DTile* tile); + +private Q_SLOTS: + void grabImage(const QRect& r); +private: + QImage m_image; + QImage m_grabedImage; + QMutex m_mutex; + QWaitCondition m_waitCondition; + QPainter m_painter; + QSGPlainTexture* m_texture; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // QQUICKCONTEXT2DTEXTURE_P_H diff --git a/src/declarative/items/context2d/qquickcontext2dtile.cpp b/src/declarative/items/context2d/qquickcontext2dtile.cpp new file mode 100644 index 0000000000..6217c66094 --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dtile.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcontext2dtile_p.h" + +#include +#include +#include + +QQuickContext2DTile::QQuickContext2DTile() + : m_dirty(true) + , m_rect(QRect(0, 0, 1, 1)) + , m_device(0) +{ +} + +QQuickContext2DTile::~QQuickContext2DTile() +{ + if (m_painter.isActive()) + m_painter.end(); +} + +QPainter* QQuickContext2DTile::createPainter(bool smooth) +{ + if (m_painter.isActive()) + m_painter.end(); + + if (m_device) { + aboutToDraw(); + m_painter.begin(m_device); + m_painter.resetTransform(); + m_painter.setCompositionMode(QPainter::CompositionMode_Source); + +#ifdef QQUICKCONTEXT2D_DEBUG + int v = 100; + int gray = (m_rect.x() / m_rect.width() + m_rect.y() / m_rect.height()) % 2; + if (gray) + v = 150; + m_painter.fillRect(QRect(0, 0, m_rect.width(), m_rect.height()), QColor(v, v, v, 255)); +#endif + if (smooth) + m_painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing + | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); + else + m_painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing + | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, false); + + m_painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + m_painter.translate(-m_rect.left(), -m_rect.top()); + m_painter.setClipRect(m_rect); + m_painter.setClipping(false); + return &m_painter; + } + + return 0; +} + +QQuickContext2DFBOTile::QQuickContext2DFBOTile() + : QQuickContext2DTile() + , m_fbo(0) +{ +} + + +QQuickContext2DFBOTile::~QQuickContext2DFBOTile() +{ + delete m_fbo; +} + +void QQuickContext2DFBOTile::aboutToDraw() +{ + m_fbo->bind(); + if (!m_device) { + QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(rect().size()); + m_device = gl_device; + QPainter p(m_device); + p.fillRect(QRectF(0, 0, m_fbo->width(), m_fbo->height()), QColor(qRgba(0, 0, 0, 0))); + p.end(); + } +} + +void QQuickContext2DFBOTile::drawFinished() +{ + m_fbo->release(); +} + +void QQuickContext2DFBOTile::setRect(const QRect& r) +{ + if (m_rect == r) + return; + m_rect = r; + m_dirty = true; + if (!m_fbo || m_fbo->size() != r.size()) { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setInternalTextureFormat(GL_RGBA); + format.setMipmap(false); + + if (m_painter.isActive()) + m_painter.end(); + + delete m_fbo; + m_fbo = new QOpenGLFramebufferObject(r.size(), format); + } +} + + +QQuickContext2DImageTile::QQuickContext2DImageTile() + : QQuickContext2DTile() +{ +} + +QQuickContext2DImageTile::~QQuickContext2DImageTile() +{ +} + +void QQuickContext2DImageTile::setRect(const QRect& r) +{ + if (m_rect == r) + return; + m_rect = r; + m_dirty = true; + if (m_image.size() != r.size()) { + m_image = QImage(r.size(), QImage::Format_ARGB32_Premultiplied); + } + m_device = &m_image; +} diff --git a/src/declarative/items/context2d/qquickcontext2dtile_p.h b/src/declarative/items/context2d/qquickcontext2dtile_p.h new file mode 100644 index 0000000000..92c59efbcf --- /dev/null +++ b/src/declarative/items/context2d/qquickcontext2dtile_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCONTEXT2DTILE_P_H +#define QQUICKCONTEXT2DTILE_P_H + +#include "qquickcontext2d_p.h" +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickContext2DTexture; +class QQuickContext2DCommandBuffer; + +class QQuickContext2DTile +{ +public: + QQuickContext2DTile(); + ~QQuickContext2DTile(); + + bool dirty() const {return m_dirty;} + void markDirty(bool dirty) {m_dirty = dirty;} + + QRect rect() const {return m_rect;} + + virtual void setRect(const QRect& r) = 0; + virtual QPainter* createPainter(bool smooth = false); + virtual void drawFinished() {} + +protected: + virtual void aboutToDraw() {} + uint m_dirty : 1; + QRect m_rect; + QPaintDevice* m_device; + QPainter m_painter; +}; + + +class QQuickContext2DFBOTile : public QQuickContext2DTile +{ +public: + QQuickContext2DFBOTile(); + ~QQuickContext2DFBOTile(); + virtual void setRect(const QRect& r); + QOpenGLFramebufferObject* fbo() const {return m_fbo;} + void drawFinished(); + +protected: + void aboutToDraw(); +private: + + + QOpenGLFramebufferObject *m_fbo; +}; + +class QQuickContext2DImageTile : public QQuickContext2DTile +{ +public: + QQuickContext2DImageTile(); + ~QQuickContext2DImageTile(); + void setRect(const QRect& r); + const QImage& image() const {return m_image;} +private: + QImage m_image; +}; +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // QQUICKCONTEXT2DTILE_P_H diff --git a/src/declarative/items/context2d/qsgcanvasitem.cpp b/src/declarative/items/context2d/qsgcanvasitem.cpp deleted file mode 100644 index b3b4eab30d..0000000000 --- a/src/declarative/items/context2d/qsgcanvasitem.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include "qsgcanvasitem_p.h" -#include -#include "qsgcontext2d_p.h" -#include "qsgcontext2dnode_p.h" -#include "qsgcontext2dtexture_p.h" -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGCanvasItemPrivate : public QSGItemPrivate -{ -public: - QSGCanvasItemPrivate(); - ~QSGCanvasItemPrivate(); - QSGContext2D* context; - QSGContext2DTexture* texture; - QSizeF canvasSize; - QSize tileSize; - QRectF canvasWindow; - QRectF dirtyRect; - uint renderInThread : 1; - uint hasCanvasSize :1; - uint hasTileSize :1; - uint hasCanvasWindow :1; - uint componentCompleted :1; - QSGCanvasItem::RenderTarget renderTarget; - QHash images; - QUrl baseUrl; -}; - -QSGCanvasItemPrivate::QSGCanvasItemPrivate() - : QSGItemPrivate() - , context(0) - , texture(0) - , canvasSize(1, 1) - , tileSize(1, 1) - , renderInThread(false) - , hasCanvasSize(false) - , hasTileSize(false) - , hasCanvasWindow(false) - , componentCompleted(false) - , renderTarget(QSGCanvasItem::FramebufferObject) -{ -} - -QSGCanvasItemPrivate::~QSGCanvasItemPrivate() -{ - qDeleteAll(images); -} - -/*! - \qmlclass Canvas QSGCanvasItem - \inqmlmodule QtQuick 2 - \since QtQuick 2.0 - \brief The Canvas item provides HTML5 like canvas element which enables you to - draw within the item area by using Javascript. - \inherits Item - \ingroup qml-basic-visual-elements - - With the Canvas item, users can draw straight and curved lines, simple and - complex shapes, graphs, and referenced graphic images. can also add texts, colors, - shadows, gradients, and patterns, and do low level pixel operations, etc. The Canvas item - also enables you to save or export the canvas as a image file or serialize the image data - to data url string. - - To define a drawing area in the Canvas item, just set the \c width and \c height properties. - For example, the following code creates a Canvas item which has a drawing area with a height of 100 - pixels and width of 200 pixels: - \qml - import QtQuick 2.0 - Canvas { - id:mycanvas - width:100 - height:200 - } - \endqml - - Currently the Canvas item only supports the two-dimensional rendering context. - - \section1 Thread Rendering and Render Target - The Canvas item supports two render targets:Canvas.Image and Canvas.FramebufferObject. - The Canvas.Image render target is a \a QImage object which is actually a block of system - memory. This render target support background thread rendering. So if some complex or long - running painting need to be done, the Canvas.Image with thread rendering mode should be - chosen to avoid blocking the UI. Otherwise the Canvas.FramebufferObject render target should - be chosen as it could be much faster with good OpenGL hardware accelaration than rendering into - system memory, especially when the CPU is already very busy. - - The default render target is Canvas.Image and the default renderInThread property is - false. - - \section1 Tiled Canvas - The Canvas item also supports tiled rendering mode by setting the proper canvasSize, tileSize - and the canvasWindow properties. - - With tiled canvas, a virtually very large canvas can be provided by a relatively small canvas - window. The actual memory consumption only relates to the canvas window size. So the canvas size - can be chosen freely as needed. The painting code then doesn't need to worry about the coordinate - system and complex matrix transformations at all. - - As a side effect, by setting a good tile size, the tiles overlapped with the canvas window could be - cached and don't need to redraw, which can improve the performance significantly in some situations. - - \section1 Pixel Operations - The Canvas item support all HTML5 2d context pixel operations. In order to get better - pixel reading/writing performance, the Canvas.Image render target should be chosen. As - for Canvas.FramebufferObject render target, the pixel data need to be exchanged between - the system memory and the graphic card, which can't be benefit from the hardware acceleration - at all. And the OpenGL rendering may synchronise with the V-Sync signal to avoid the - {en.wikipedia.org/wiki/Screen_tearing}{screen tearing} which makes the pixel operations - even slower with the Canvas.FrambufferObject render target. - - \section1 Tips for Porting Existing HTML5 Canvas applications - - Although the Canvas item is provided as a HTML5 like API, and - the canvas context API is as compatible with HTML5 2d context standard - as possible, the working HTML5 canvas applications are still need to - be modified to run in the Canvas item: - \list - \o Removes and replaces all DOM API calls with QML property bindings or Canvas item methods. - \o Removes and replaces all HTML envent handlers with the \a MouseArea item. - \o Changes the setInterval/setTimeout function calls with the \a Timer item. - \o Puts the actual painting code into the \a QtQuick2::Canvas::onPaint handler and triggers the - painting by calling the Canvas's \c markDirty or \c requestPaint methods. - \o For drawing images, loads them by calling the Canvas's loadImage method and then request to paint - them in the onImageLoaded handler. - \endlist - - \sa QtQuick2::Context2D -*/ - -QSGCanvasItem::QSGCanvasItem(QSGItem *parent) - : QSGItem(*(new QSGCanvasItemPrivate), parent) -{ - setFlag(ItemHasContents); -} - -QSGCanvasItem::~QSGCanvasItem() -{ - Q_D(QSGCanvasItem); - delete d->context; -} - -/*! - \qmlproperty size QtQuick2::Canvas::canvasSize - Holds the logical canvas size that the context paints on. - - By default, the canvas size is the same size as the current canvas item size. - By setting the canvas size, tile size and canvas window, the Canvas - item can act as a virtual large canvas with many seperately rendered tile rectangle - areas. Only those tiles within the current canvas window would be painted by - the Canvas render engine. - \sa QtQuick2::Canvas::tileSize QtQuick2::Canvas::canvasWindow -*/ -QSizeF QSGCanvasItem::canvasSize() const -{ - Q_D(const QSGCanvasItem); - return d->canvasSize; -} - -void QSGCanvasItem::setCanvasSize(const QSizeF & size) -{ - Q_D(QSGCanvasItem); - if (d->canvasSize != size) { - d->hasCanvasSize = true; - d->canvasSize = size; - emit canvasSizeChanged(); - polish(); - update(); - } -} - -/*! - \qmlproperty size QtQuick2::Canvas::tileSize - Holds the canvas rendering tile size. - - When the Canvas item in tiled mode by setting the canvas size, tile size and - the canvas window. The canvas render can improve the rendering performance - by rendering and caching tiles instead of rendering the whole canvas everytime. - - Additionally, the canvas size could be infinitely large without allocating more - memories because only those tiles within the current visible region - are actually rendered. - - By default, the tile size is the same with the canvas size. - \sa QtQuick2::Canvas::canvaasSize QtQuick2::Canvas::canvasWindow -*/ -QSize QSGCanvasItem::tileSize() const -{ - Q_D(const QSGCanvasItem); - return d->tileSize; -} - -void QSGCanvasItem::setTileSize(const QSize & size) -{ - Q_D(QSGCanvasItem); - if (d->tileSize != size) { - d->hasTileSize = true; - d->tileSize = size; - - emit tileSizeChanged(); - polish(); - update(); - } -} - -/*! - \qmlproperty rect QtQuick2::Canvas::canvasWindow - Holds the current canvas visible window. - - By default, the canvas window size is the same as the Canvas item - size with the topleft point as (0, 0). - - If the canvas size is different with the Canvas item size, the Canvas - item can display different visible areas by changing the canvas window's size - and/or position. - \sa QtQuick2::Canvas::canvasSize QtQuick2::Canvas::tileSize -*/ -QRectF QSGCanvasItem::canvasWindow() const -{ - Q_D(const QSGCanvasItem); - return d->canvasWindow; -} - -void QSGCanvasItem::setCanvasWindow(const QRectF& rect) -{ - Q_D(QSGCanvasItem); - if (d->canvasWindow != rect) { - d->canvasWindow = rect; - - d->hasCanvasWindow = true; - emit canvasWindowChanged(); - polish(); - update(); - } -} - - -QSGContext2D* QSGCanvasItem::context() const -{ - Q_D(const QSGCanvasItem); - return d->context; -} -/*! - \qmlproperty bool QtQuick2::Canvas::renderInThread - Holds the current canvas rendering mode. - - By setting the renderInThread to true, complex and long - running painting can be rendered in a dedicated background - rendering thread to avoid blocking the main GUI. - - Note: Different renderTarget may or may not support the - background rendering thread, if not, the renderInThread - property will be ignored. - - The default value is false. - \sa QtQuick2::Canvas::renderTarget -*/ -bool QSGCanvasItem::renderInThread() const -{ - Q_D(const QSGCanvasItem); - return d->renderInThread; -} -/*! - \qmlproperty bool QtQuick2::Canvas::renderTarget - Holds the current canvas render target. - - \list - \o Canvas.Image - render to an in memory image buffer, the render - target supports background rendering. - \o Canvas.FramebufferObject - render to an OpenGL frame buffer, - this render target will ignore the - renderInThread property. The actual - rendering happens in the main QML rendering - process, which may be in a seperate render thread - or in the main GUI thread depends on the platforms. - \endlist - - The default render target is \c Canvas.Image. - \sa QtQuick2::Canvas::renderInThread -*/ -QSGCanvasItem::RenderTarget QSGCanvasItem::renderTarget() const -{ - Q_D(const QSGCanvasItem); - return d->renderTarget; -} - -void QSGCanvasItem::setRenderTarget(RenderTarget target) -{ - Q_D(QSGCanvasItem); - if (d->renderTarget != target) { - d->renderTarget = target; - - if (d->componentCompleted) - createTexture(); - emit renderTargetChanged(); - } -} - -void QSGCanvasItem::_doPainting(const QRectF& region) -{ - Q_D(QSGCanvasItem); - emit paint(QDeclarativeV8Handle::fromHandle(d->context->v8value()) - , QSGContext2DTexture::tiledRect(region, d->tileSize)); - if (d->texture) - d->texture->wake(); -} - -/*! - \qmlproperty bool QtQuick2::Canvas::renderInThread - Holds the current canvas rendering mode. - - When this property is true, all canvas painting commands - are rendered in a background rendering thread, otherwise - the rendering happens in the main GUI thread. - - The default renderInThread value is false. -*/ -void QSGCanvasItem::setRenderInThread(bool renderInThread) -{ - Q_D(QSGCanvasItem); - if (d->renderInThread != renderInThread) { - d->renderInThread = renderInThread; - - if (d->componentCompleted) - createTexture(); - - if (d->renderInThread) - connect(this, SIGNAL(painted()), SLOT(update())); - else - disconnect(this, SIGNAL(painted()), this, SLOT(update())); - emit renderInThreadChanged(); - polish(); - update(); - } -} - -void QSGCanvasItem::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) -{ - Q_D(QSGCanvasItem); - QSGItem::geometryChanged(newGeometry, oldGeometry); - - const qreal w = newGeometry.width(); - const qreal h = newGeometry.height(); - - if (!d->hasCanvasSize) { - d->canvasSize = QSizeF(w, h); - emit canvasSizeChanged(); - } - - if (!d->hasTileSize) { - d->tileSize = d->canvasSize.toSize(); - emit tileSizeChanged(); - } - - if (!d->hasCanvasWindow) { - d->canvasWindow = newGeometry; - emit canvasWindowChanged(); - } - - polish(); - update(); -} - -void QSGCanvasItem::componentComplete() -{ - Q_D(QSGCanvasItem); - QSGItem::componentComplete(); - - if (!d->context) - createContext(); - createTexture(); - - d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl(); - requestPaint(); - updatePolish(); //force update the canvas sizes to texture for the first time - update(); - d->componentCompleted = true; -} - -void QSGCanvasItem::updatePolish() -{ - Q_D(QSGCanvasItem); - QSGItem::updatePolish(); - if (d->texture) { - if (!d->renderInThread && d->dirtyRect.isValid()) - _doPainting(d->dirtyRect); - - d->texture->canvasChanged(d->canvasSize.toSize() - , d->tileSize - , d->canvasWindow.toAlignedRect() - , d->dirtyRect.toAlignedRect() - , d->smooth); - d->dirtyRect = QRectF(); - } -} - -QSGNode *QSGCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - Q_D(QSGCanvasItem); - QSGContext2DNode *node = static_cast(oldNode); - if (!node) - node = new QSGContext2DNode(this); - - node->setTexture(d->texture); - node->setSize(d->canvasWindow.size()); - node->update(); - return node; -} - -void QSGCanvasItem::createTexture() -{ - Q_D(QSGCanvasItem); - - if (!d->texture - || d->texture->threadRendering() != d->renderInThread - || d->texture->renderTarget() != d->renderTarget) { - if (d->texture) { - d->texture->deleteLater(); - d->texture = 0; - } - - if (d->renderTarget == QSGCanvasItem::Image) { - d->texture = new QSGContext2DImageTexture(d->renderInThread); - } else if (d->renderTarget == QSGCanvasItem::FramebufferObject) { - d->texture = new QSGContext2DFBOTexture(); - } - - if (d->renderInThread && !d->texture->supportThreadRendering()) { - qWarning("Canvas: render target does not support thread rendering, force to non-thread rendering mode."); - d->renderInThread = false; - emit renderInThreadChanged(); - } - - if (d->renderInThread) - connect(d->texture, SIGNAL(textureChanged()), this, SLOT(update())); - - d->texture->setItem(this); - } -} - -void QSGCanvasItem::createContext() -{ - Q_D(QSGCanvasItem); - - delete d->context; - - d->context = new QSGContext2D(this); - - QV8Engine *e = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this)); - d->context->setV8Engine(e); -} - -/*! - \qmlmethod object QtQuick2::Canvas::getContext(string contextId) - - Currently, the canvas item only support the 2D context. If the \a contextId - parameter isn't provided or is "2d", then the QtQuick2::Context2D object is - returned, otherwise returns an invalid value. - */ -QDeclarativeV8Handle QSGCanvasItem::getContext(const QString &contextId) -{ - Q_D(QSGCanvasItem); - - if (contextId.toLower() != QLatin1String("2d")) - return QDeclarativeV8Handle::fromHandle(v8::Undefined()); - - if (!d->context) - createContext(); - return QDeclarativeV8Handle::fromHandle(d->context->v8value()); -} - -/*! - \qmlmethod void QtQuick2::Canvas::markDirty(rect region) - - Mark the given \a region as dirty, so that when this region is visible - the canvas render will redraw it. During the rendering process, the - canvas renderer may emit the canvas' "paint" signal so the actual painting - scripts can be putted into the canvas's "onPaint" signal handler function. - - \sa QtQuick2::Canvas::paint QtQuick2::Canvas::requestPaint - */ -void QSGCanvasItem::markDirty(const QRectF& region) -{ - Q_D(QSGCanvasItem); - d->dirtyRect |= region; - if (d->componentCompleted) - polish(); - update(); -} - - -/*! - \qmlmethod bool QtQuick2::Canvas::save(string filename) - - Save the current canvas content into an image file \a filename. - The saved image format is automatically decided by the \a filename's - suffix. - - Note: calling this method will force painting the whole canvas, not the - current canvas visible window. - - \sa canvasWindow canvasSize toDataURL - */ -bool QSGCanvasItem::save(const QString &filename) const -{ - Q_D(const QSGCanvasItem); - QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename)); - return toImage().save(url.toLocalFile()); -} - -QImage QSGCanvasItem::loadedImage(const QUrl& url) -{ - Q_D(QSGCanvasItem); - QUrl fullPathUrl = d->baseUrl.resolved(url); - if (!d->images.contains(fullPathUrl)) { - loadImage(url); - } - QDeclarativePixmap* pix = d->images.value(fullPathUrl); - if (pix->isLoading() || pix->isError()) { - return QImage(); - } - return pix->pixmap().toImage(); -} - -/*! - \qmlmethod void QtQuick2::Canvas::loadImage(url image) - Loads the given \c image asynchronously, when the image is - ready, an imageLoaded signal will be emitted. - The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method. - - Note: Only loaded images can be painted on the Canvas item. - \sa QtQuick2::Canvas::unloadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded - \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage - */ -void QSGCanvasItem::loadImage(const QUrl& url) -{ - Q_D(QSGCanvasItem); - QUrl fullPathUrl = d->baseUrl.resolved(url); - if (!d->images.contains(fullPathUrl)) { - QDeclarativePixmap* pix = new QDeclarativePixmap(); - d->images.insert(fullPathUrl, pix); - - pix->load(qmlEngine(this) - , fullPathUrl - , QDeclarativePixmap::Cache | QDeclarativePixmap::Asynchronous); - pix->connectFinished(this, SIGNAL(imageLoaded())); - } -} -/*! - \qmlmethod void QtQuick2::Canvas::loadImage(url image) - Unloads the \c image. - - If the image is unloaded from the Canvas item, it can't be painted by the canvas context - until it's loaded again. - - \sa QtQuick2::Canvas::loadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded - \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage - */ -void QSGCanvasItem::unloadImage(const QUrl& url) -{ - Q_D(QSGCanvasItem); - QUrl removeThis = d->baseUrl.resolved(url); - if (d->images.contains(removeThis)) { - delete d->images.value(removeThis); - d->images.remove(removeThis); - } -} - -/*! - \qmlmethod void QtQuick2::Canvas::isImageError(url image) - Returns true if the image can't be loaded because of error happens. - - \sa QtQuick2::Canvas::loadImage - */ -bool QSGCanvasItem::isImageError(const QUrl& url) const -{ - Q_D(const QSGCanvasItem); - QUrl fullPathUrl = d->baseUrl.resolved(url); - return d->images.contains(fullPathUrl) - && d->images.value(fullPathUrl)->isError(); -} - -/*! - \qmlmethod void QtQuick2::Canvas::isImageLoading(url image) - Returns true if the Canvas item still is loading the \c image. - - \sa QtQuick2::Canvas::loadImage - */ -bool QSGCanvasItem::isImageLoading(const QUrl& url) const -{ - Q_D(const QSGCanvasItem); - QUrl fullPathUrl = d->baseUrl.resolved(url); - return d->images.contains(fullPathUrl) - && d->images.value(fullPathUrl)->isLoading(); -} -/*! - \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image) - Returns true if the \c image is sucessfully loaded and ready to use. - - \sa QtQuick2::Canvas::loadImage - */ -bool QSGCanvasItem::isImageLoaded(const QUrl& url) const -{ - Q_D(const QSGCanvasItem); - QUrl fullPathUrl = d->baseUrl.resolved(url); - return d->images.contains(fullPathUrl) - && d->images.value(fullPathUrl)->isReady(); -} - -QImage QSGCanvasItem::toImage(const QRectF& region) const -{ - Q_D(const QSGCanvasItem); - if (d->texture) { - if (region.isEmpty()) - return d->texture->toImage(canvasWindow()); - else - return d->texture->toImage(region); - } - return QImage(); -} - -/*! - \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType) - - Returns a data: URL for the image in the canvas. - - The default \a mimeType is "image/png". - - \sa QtQuick2::Canvas::save - */ -QString QSGCanvasItem::toDataURL(const QString& mimeType) const -{ - QImage image = toImage(); - - if (!image.isNull()) { - QByteArray ba; - QBuffer buffer(&ba); - buffer.open(QIODevice::WriteOnly); - QString mime = mimeType.toLower(); - QString type; - if (mime == QLatin1Literal("image/png")) { - type = QLatin1Literal("PNG"); - } else if (mime == QLatin1Literal("image/bmp")) - type = QLatin1Literal("BMP"); - else if (mime == QLatin1Literal("image/jpeg")) - type = QLatin1Literal("JPEG"); - else if (mime == QLatin1Literal("image/x-portable-pixmap")) - type = QLatin1Literal("PPM"); - else if (mime == QLatin1Literal("image/tiff")) - type = QLatin1Literal("TIFF"); - else if (mime == QLatin1Literal("image/xpm")) - type = QLatin1Literal("XPM"); - else - return QLatin1Literal("data:,"); - - image.save(&buffer, type.toAscii()); - buffer.close(); - QString dataUrl = QLatin1Literal("data:%1;base64,%2"); - return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData())); - } - return QLatin1Literal("data:,"); -} - -/*! - \qmlsignal QtQuick2::Canvas::onPaint(QtQuick2::Context2D context, rect region) - - This handler is called before the given \c region needs to be rendered. - - This signal can be triggered by QtQuick2::Canvas::markdirty, QtQuick2::Canvas::requestPaint - or by changing the current canvas window. -*/ - -/*! - \qmlsignal QtQuick2::Canvas::onPainted() - - This handler is called after all context painting commands are executed and - the Canvas is actually rendered. -*/ - -QT_END_NAMESPACE diff --git a/src/declarative/items/context2d/qsgcanvasitem_p.h b/src/declarative/items/context2d/qsgcanvasitem_p.h deleted file mode 100644 index 131a3f5fff..0000000000 --- a/src/declarative/items/context2d/qsgcanvasitem_p.h +++ /dev/null @@ -1,148 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCANVASITEM_P_H -#define QSGCANVASITEM_P_H - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) -class QSGContext2D; -class QSGCanvasItemPrivate; -class Q_DECLARATIVE_EXPORT QSGCanvasItem : public QSGItem -{ - Q_OBJECT - Q_ENUMS(RenderTarget) - Q_ENUMS(ImageFilterMode) - - Q_PROPERTY(QSizeF canvasSize READ canvasSize WRITE setCanvasSize NOTIFY canvasSizeChanged) - Q_PROPERTY(QSize tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged) - Q_PROPERTY(QRectF canvasWindow READ canvasWindow WRITE setCanvasWindow NOTIFY canvasWindowChanged) - Q_PROPERTY(bool renderInThread READ renderInThread WRITE setRenderInThread NOTIFY renderInThreadChanged) - Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged) -public: - enum RenderTarget { - Image, - FramebufferObject - }; - - enum ImageFilterMode { - Threshold, - Mono, - GrayScale, - Brightness, - Invert, - Blur, - Opaque, - Convolute - }; - - QSGCanvasItem(QSGItem *parent = 0); - ~QSGCanvasItem(); - - QSizeF canvasSize() const; - void setCanvasSize(const QSizeF &); - - QSize tileSize() const; - void setTileSize(const QSize &); - - QRectF canvasWindow() const; - void setCanvasWindow(const QRectF& rect); - - bool renderInThread() const; - void setRenderInThread(bool renderInThread); - - RenderTarget renderTarget() const; - void setRenderTarget(RenderTarget target); - - QSGContext2D* context() const; - QImage toImage(const QRectF& region = QRectF()) const; - - QImage loadedImage(const QUrl& url); -Q_SIGNALS: - void paint(QDeclarativeV8Handle context, const QRect ®ion); - void painted(); - void canvasSizeChanged(); - void tileSizeChanged(); - void renderInThreadChanged(); - void textureChanged(); - void canvasWindowChanged(); - void renderTargetChanged(); - void imageLoaded(); -public Q_SLOTS: - QString toDataURL(const QString& type = QLatin1String("image/png")) const; - QDeclarativeV8Handle getContext(const QString & = QLatin1String("2d")); - void markDirty(const QRectF& region); - void requestPaint() {markDirty(canvasWindow());} - // Save current canvas to disk - bool save(const QString& filename) const; - void loadImage(const QUrl& url); - void unloadImage(const QUrl& url); - bool isImageLoaded(const QUrl& url) const; - bool isImageLoading(const QUrl& url) const; - bool isImageError(const QUrl& url) const; -private Q_SLOTS: - void _doPainting(const QRectF& region); -protected: - virtual void componentComplete(); - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - virtual void updatePolish(); -private: - void createContext(); - void createTexture(); - Q_DECLARE_PRIVATE(QSGCanvasItem) - friend class QSGContext2D; - friend class QSGContext2DTexture; -}; -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGCanvasItem) - -QT_END_HEADER - -#endif //QSGCANVASITEM_P_H diff --git a/src/declarative/items/context2d/qsgcontext2d.cpp b/src/declarative/items/context2d/qsgcontext2d.cpp deleted file mode 100644 index 5944a02658..0000000000 --- a/src/declarative/items/context2d/qsgcontext2d.cpp +++ /dev/null @@ -1,3534 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgcontext2d_p.h" -#include "qsgcontext2dcommandbuffer_p.h" -#include "qsgcanvasitem_p.h" -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE -/*! - \qmlclass Context2D QSGContext2D - \inqmlmodule QtQuick 2 - \since QtQuick 2.0 - \brief The Context2D API allows you to draw 2d graphic shapes on the \c Canvas item. - - The Context2D object can be created by \c Canvas item's \c getContext() method: - \code - Canvas { - id:canvas - onPaint:{ - var ctx = canvas.getContext('2d'); - //... - } - } - \endcode - The Context2D API implements the same \l {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard} - with some enhanced features. - - The Context2D API provides the rendering \bold{context} which defines the methods and attributes needed to draw - on the \c Canvas item. The following assigns the canvas rendering context to a \c{context} - variable: - \code - var context = mycanvas.getContext("2d") - \endcode - - The Context2D API renders the canvas as a coordinate system whose origin (0,0) is - at the top left corner, as shown in the figure below. Coordinates increase along - the \c{x} axis from left to right and along the \c{y} axis from top to bottom of - the canvas. - \image qml-item-canvas-context.gif -*/ -static const double Q_PI = 3.14159265358979323846; // pi - -#define DEGREES(t) ((t) * 180.0 / Q_PI) - -#define CHECK_CONTEXT(r) if (!r || !r->context || !r->context->buffer()) \ - V8THROW_ERROR("Not a Context2D object"); - -#define CHECK_CONTEXT_SETTER(r) if (!r || !r->context || !r->context->buffer()) \ - V8THROW_ERROR_SETTER("Not a Context2D object"); -#define qClamp(val, min, max) qMin(qMax(val, min), max) -#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9)) -QColor qt_color_from_string(v8::Local name) -{ - v8::String::AsciiValue str(name); - - char *p = *str; - int len = str.length(); - //rgb/hsl color string has at least 7 characters - if (!p || len > 255 || len <= 7) - return QColor(p); - else { - bool isRgb(false), isHsl(false), hasAlpha(false); - - while (isspace(*p)) p++; - if (strncmp(p, "rgb", 3) == 0) - isRgb = true; - else if (strncmp(p, "hsl", 3) == 0) - isHsl = true; - else - return QColor(p); - - p+=3; //skip "rgb" or "hsl" - hasAlpha = (*p == 'a') ? true : false; - - ++p; //skip "(" - - if (hasAlpha) ++p; //skip "a" - - int rh, gs, bl, alpha = 255; - - //red - while (isspace(*p)) p++; - rh = strtol(p, &p, 10); - if (*p == '%') { - rh = qRound(rh/100.0 * 255); - ++p; - } - if (*p++ != ',') return QColor(); - - //green - while (isspace(*p)) p++; - gs = strtol(p, &p, 10); - if (*p == '%') { - gs = qRound(gs/100.0 * 255); - ++p; - } - if (*p++ != ',') return QColor(); - - //blue - while (isspace(*p)) p++; - bl = strtol(p, &p, 10); - if (*p == '%') { - bl = qRound(bl/100.0 * 255); - ++p; - } - - if (hasAlpha) { - if (*p++!= ',') return QColor(); - while (isspace(*p)) p++; - alpha = qRound(strtod(p, &p) * 255); - } - - if (*p != ')') return QColor(); - if (isRgb) - return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255))); - else - return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)); - } - return QColor(); -} - -QFont qt_font_from_string(const QString& fontString) { - QFont font; - // ### this is simplified and incomplete - // ### TODO:get code from Qt webkit - QStringList tokens = fontString.split(QLatin1String(" ")); - foreach (const QString &token, tokens) { - if (token == QLatin1String("italic")) - font.setItalic(true); - else if (token == QLatin1String("bold")) - font.setBold(true); - else if (token.endsWith(QLatin1String("px"))) { - QString number = token; - number.remove(QLatin1String("px")); - //font.setPointSizeF(number.trimmed().toFloat()); - font.setPixelSize(number.trimmed().toInt()); - } else - font.setFamily(token); - } - - return font; -} - - - -class QSGContext2DEngineData : public QV8Engine::Deletable -{ -public: - QSGContext2DEngineData(QV8Engine *engine); - ~QSGContext2DEngineData(); - - v8::Persistent constructorContext; - v8::Persistent constructorGradient; - v8::Persistent constructorPattern; - v8::Persistent constructorPixelArray; - v8::Persistent constructorImageData; -}; - -V8_DEFINE_EXTENSION(QSGContext2DEngineData, engineData) - -class QV8Context2DResource : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(Context2DType) -public: - QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e) {} - QSGContext2D* context; -}; - -class QV8Context2DStyleResource : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(Context2DStyleType) -public: - QV8Context2DStyleResource(QV8Engine *e) - : QV8ObjectResource(e) - , patternRepeatX(false) - , patternRepeatY(false) - {} - QBrush brush; - bool patternRepeatX:1; - bool patternRepeatY:1; -}; - -class QV8Context2DPixelArrayResource : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(Context2DPixelArrayType) -public: - QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {} - - QImage image; -}; - -QImage qt_image_convolute_filter(const QImage& src, const QVector& weights, int radius = 0) -{ - int sides = radius ? radius : qRound(qSqrt(weights.size())); - int half = qFloor(sides/2); - - QImage dst = QImage(src.size(), src.format()); - int w = src.width(); - int h = src.height(); - for (int y = 0; y < dst.height(); ++y) { - QRgb *dr = (QRgb*)dst.scanLine(y); - for (int x = 0; x < dst.width(); ++x) { - unsigned char* dRgb = ((unsigned char*)&dr[x]); - unsigned char red=0, green=0, blue=0, alpha=0; - int sy = y; - int sx = x; - - for (int cy=0; cy= 0 && scy < w && scx >= 0 && scx < h) { - const QRgb *sr = (const QRgb*)(src.constScanLine(scy)); - const unsigned char* sRgb = ((const unsigned char*)&sr[scx]); - qreal wt = radius ? weights[0] : weights[cy*sides+cx]; - red += sRgb[0] * wt; - green += sRgb[1] * wt; - blue += sRgb[2] * wt; - alpha += sRgb[3] * wt; - } - } - } - dRgb[0] = red; - dRgb[1] = green; - dRgb[2] = blue; - dRgb[3] = alpha; - } - } - return dst; -} - -void qt_image_boxblur(QImage& image, int radius, bool quality) -{ - int passes = quality? 3: 1; - for (int i=0; i < passes; i++) { - image = qt_image_convolute_filter(image, QVector() << 1.0/(radius * radius * 1.0), radius); - } -} - -static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator) -{ - if (compositeOperator == QLatin1String("source-over")) { - return QPainter::CompositionMode_SourceOver; - } else if (compositeOperator == QLatin1String("source-out")) { - return QPainter::CompositionMode_SourceOut; - } else if (compositeOperator == QLatin1String("source-in")) { - return QPainter::CompositionMode_SourceIn; - } else if (compositeOperator == QLatin1String("source-atop")) { - return QPainter::CompositionMode_SourceAtop; - } else if (compositeOperator == QLatin1String("destination-atop")) { - return QPainter::CompositionMode_DestinationAtop; - } else if (compositeOperator == QLatin1String("destination-in")) { - return QPainter::CompositionMode_DestinationIn; - } else if (compositeOperator == QLatin1String("destination-out")) { - return QPainter::CompositionMode_DestinationOut; - } else if (compositeOperator == QLatin1String("destination-over")) { - return QPainter::CompositionMode_DestinationOver; - } else if (compositeOperator == QLatin1String("lighter")) { - return QPainter::CompositionMode_Lighten; - } else if (compositeOperator == QLatin1String("copy")) { - return QPainter::CompositionMode_Source; - } else if (compositeOperator == QLatin1String("xor")) { - return QPainter::CompositionMode_Xor; - } else if (compositeOperator == QLatin1String("qt-clear")) { - return QPainter::CompositionMode_Clear; - } else if (compositeOperator == QLatin1String("qt-destination")) { - return QPainter::CompositionMode_Destination; - } else if (compositeOperator == QLatin1String("qt-multiply")) { - return QPainter::CompositionMode_Multiply; - } else if (compositeOperator == QLatin1String("qt-screen")) { - return QPainter::CompositionMode_Screen; - } else if (compositeOperator == QLatin1String("qt-overlay")) { - return QPainter::CompositionMode_Overlay; - } else if (compositeOperator == QLatin1String("qt-darken")) { - return QPainter::CompositionMode_Darken; - } else if (compositeOperator == QLatin1String("qt-lighten")) { - return QPainter::CompositionMode_Lighten; - } else if (compositeOperator == QLatin1String("qt-color-dodge")) { - return QPainter::CompositionMode_ColorDodge; - } else if (compositeOperator == QLatin1String("qt-color-burn")) { - return QPainter::CompositionMode_ColorBurn; - } else if (compositeOperator == QLatin1String("qt-hard-light")) { - return QPainter::CompositionMode_HardLight; - } else if (compositeOperator == QLatin1String("qt-soft-light")) { - return QPainter::CompositionMode_SoftLight; - } else if (compositeOperator == QLatin1String("qt-difference")) { - return QPainter::CompositionMode_Difference; - } else if (compositeOperator == QLatin1String("qt-exclusion")) { - return QPainter::CompositionMode_Exclusion; - } - return QPainter::CompositionMode_SourceOver; -} - -static QString qt_composite_mode_to_string(QPainter::CompositionMode op) -{ - switch (op) { - case QPainter::CompositionMode_SourceOver: - return QLatin1String("source-over"); - case QPainter::CompositionMode_DestinationOver: - return QLatin1String("destination-over"); - case QPainter::CompositionMode_Clear: - return QLatin1String("qt-clear"); - case QPainter::CompositionMode_Source: - return QLatin1String("copy"); - case QPainter::CompositionMode_Destination: - return QLatin1String("qt-destination"); - case QPainter::CompositionMode_SourceIn: - return QLatin1String("source-in"); - case QPainter::CompositionMode_DestinationIn: - return QLatin1String("destination-in"); - case QPainter::CompositionMode_SourceOut: - return QLatin1String("source-out"); - case QPainter::CompositionMode_DestinationOut: - return QLatin1String("destination-out"); - case QPainter::CompositionMode_SourceAtop: - return QLatin1String("source-atop"); - case QPainter::CompositionMode_DestinationAtop: - return QLatin1String("destination-atop"); - case QPainter::CompositionMode_Xor: - return QLatin1String("xor"); - case QPainter::CompositionMode_Plus: - return QLatin1String("plus"); - case QPainter::CompositionMode_Multiply: - return QLatin1String("qt-multiply"); - case QPainter::CompositionMode_Screen: - return QLatin1String("qt-screen"); - case QPainter::CompositionMode_Overlay: - return QLatin1String("qt-overlay"); - case QPainter::CompositionMode_Darken: - return QLatin1String("qt-darken"); - case QPainter::CompositionMode_Lighten: - return QLatin1String("lighter"); - case QPainter::CompositionMode_ColorDodge: - return QLatin1String("qt-color-dodge"); - case QPainter::CompositionMode_ColorBurn: - return QLatin1String("qt-color-burn"); - case QPainter::CompositionMode_HardLight: - return QLatin1String("qt-hard-light"); - case QPainter::CompositionMode_SoftLight: - return QLatin1String("qt-soft-light"); - case QPainter::CompositionMode_Difference: - return QLatin1String("qt-difference"); - case QPainter::CompositionMode_Exclusion: - return QLatin1String("qt-exclusion"); - default: - break; - } - return QString(); -} - - -static v8::Local qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image) -{ - QSGContext2DEngineData *ed = engineData(engine); - v8::Local imageData = ed->constructorImageData->NewInstance(); - QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine); - if (image.isNull()) { - r->image = QImage(w, h, QImage::Format_ARGB32); - r->image.fill(0x00000000); - } else { - Q_ASSERT(image.width() == w && image.height() == h); - r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32); - } - v8::Local pixelData = ed->constructorPixelArray->NewInstance(); - pixelData->SetExternalResource(r); - - imageData->SetInternalField(0, pixelData); - return imageData; -} - -//static script functions - -/*! - \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas - Holds the canvas item that the context paints on. - - This property is read only. -*/ -static v8::Handle ctx2d_canvas(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - return engine->newQObject(r->context->canvas()); -} - -/*! - \qmlmethod object QtQuick2::Context2D::restore() - Pops the top state on the stack, restoring the context to that state. - - \sa QtQuick2::Context2D::save() -*/ -static v8::Handle ctx2d_restore(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - r->context->popState(); - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::reset() - Resets the context state and properties to the default values. -*/ -static v8::Handle ctx2d_reset(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - r->context->reset(); - r->context->m_path = QPainterPath(); - r->context->m_path.setFillRule(Qt::WindingFill); - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::save() - Pushes the current state onto the state stack. - - Before changing any state attributes, you should save the current state - for future reference. The context maintains a stack of drawing states. - Each state consists of the current transformation matrix, clipping region, - and values of the following attributes: - \list - \o\a QtQuick2::Context2D::strokeStyle - \o\a QtQuick2::Context2D::fillStyle - \o\a QtQuick2::Context2D::fillRule - \o\a QtQuick2::Context2D::globalAlpha - \o\a QtQuick2::Context2D::lineWidth - \o\a QtQuick2::Context2D::lineCap - \o\a QtQuick2::Context2D::lineJoin - \o\a QtQuick2::Context2D::miterLimit - \o\a QtQuick2::Context2D::shadowOffsetX - \o\a QtQuick2::Context2D::shadowOffsetY - \o\a QtQuick2::Context2D::shadowBlur - \o\a QtQuick2::Context2D::shadowColor - \o\a QtQuick2::Context2D::globalCompositeOperation - \o\a QtQuick2::Context2D::font - \o\a QtQuick2::Context2D::textAlign - \o\a QtQuick2::Context2D::textBaseline - \endlist - - The current path is NOT part of the drawing state. The path can be reset by - invoking the \a QtQuick2::Context2D::beginPath() method. -*/ -static v8::Handle ctx2d_save(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - r->context->pushState(); - - return args.This(); -} - -// transformations -/*! - \qmlmethod object QtQuick2::Context2D::rotate(real angle) - Rotate the canvas around the current origin by \c angle in radians and clockwise direction. - \code - ctx.rotate(Math.PI/2); - \endcode - \image qml-item-canvas-rotate.png - - The rotation transformation matrix is as follows: - - \image qml-item-canvas-math-rotate.png - - where the \c angle of rotation is in radians. - -*/ -static v8::Handle ctx2d_rotate(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - if (args.Length() == 1) { - qreal angle = args[0]->NumberValue(); - if (!qIsFinite(angle)) - return args.This(); - - r->context->state.matrix.rotate(DEGREES(angle)); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::scale(real x, real y) - Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors - to the current tranform matrix. - Where \c x is the scale factor in the horizontal direction and \c y is the scale factor in the - vertical direction. - The following code doubles the horizontal size of an object drawn on the canvas and half its - vertical size: - \code - ctx.scale(2.0, 0.5); - \endcode - \image qml-item-canvas-scale.png - -*/ -static v8::Handle ctx2d_scale(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 2) { - qreal x, y; - x = args[0]->NumberValue(); - y = args[1]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - - r->context->state.matrix.scale(x, y); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f) - Changes the transformation matrix to the matrix given by the arguments as described below. - - Modifying the transformation matrix directly enables you to perform scaling, - rotating, and translating transformations in a single step. - - Each point on the canvas is multiplied by the matrix before anything is - drawn. The \l{HTML5 Canvas API} defines the transformation matrix as: - - \image qml-item-canvas-math.png - where: - \list - \o \c{a} is the scale factor in the horizontal (x) direction - \image qml-item-canvas-scalex.png - \o \c{c} is the skew factor in the x direction - \image qml-item-canvas-canvas-skewx.png - \o \c{e} is the translation in the x direction - \image qml-item-canvas-canvas-translate.png - \o \c{b} is the skew factor in the y (vertical) direction - \image qml-item-canvas-canvas-skewy.png - \o \c{d} is the scale factor in the y direction - \image qml-item-canvas-canvas-scaley.png - \o \c{f} is the translation in the y direction - \image qml-item-canvas-canvas-translatey.png - \o the last row remains constant - \endlist - The scale factors and skew factors are multiples; \c{e} and \c{f} are - coordinate space units, just like the units in the \a QtQuick2::Context2D::translate(x,y) - method. - - \sa QtQuick2::Context2D::transform() -*/ -static v8::Handle ctx2d_setTransform(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 6) { - qreal a = args[0]->NumberValue(); - qreal b = args[1]->NumberValue(); - qreal c = args[2]->NumberValue(); - qreal d = args[3]->NumberValue(); - qreal e = args[4]->NumberValue(); - qreal f = args[5]->NumberValue(); - - if (!qIsFinite(a) - || !qIsFinite(b) - || !qIsFinite(c) - || !qIsFinite(d) - || !qIsFinite(e) - || !qIsFinite(f)) - return args.This(); - - r->context->state.matrix = QTransform(a, b, c, d, e, f); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f) - This method is very similar to \a QtQuick2::Context2D::setTransform(), but instead of replacing the old - tranform matrix, this method applies the given tranform matrix to the current matrix by mulitplying to it. - - The \a setTransform(a, b, c, d, e, f) method actually resets the current transform to the identity matrix, - and then invokes the transform(a, b, c, d, e, f) method with the same arguments. - - \sa QtQuick2::Context2D::setTransform() -*/ -static v8::Handle ctx2d_transform(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 6) { - qreal a = args[0]->NumberValue(); - qreal b = args[1]->NumberValue(); - qreal c = args[2]->NumberValue(); - qreal d = args[3]->NumberValue(); - qreal e = args[4]->NumberValue(); - qreal f = args[5]->NumberValue(); - - if (!qIsFinite(a) - || !qIsFinite(b) - || !qIsFinite(c) - || !qIsFinite(d) - || !qIsFinite(e) - || !qIsFinite(f)) - return args.This(); - - r->context->state.matrix *= QTransform(a, b, c, d, e, f); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::translate(real x, real y) - Translates the origin of the canvas to point (\c x, \c y). - - \c x is the horizontal distance that the origin is translated, in coordinate space units, - \c y is the vertical distance that the origin is translated, in coordinate space units. - Translating the origin enables you to draw patterns of different objects on the canvas - without having to measure the coordinates manually for each shape. -*/ -static v8::Handle ctx2d_translate(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 2) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - - r->context->state.matrix.translate(x, y); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - - return args.This(); -} - - -/*! - \qmlmethod object QtQuick2::Context2D::resetTransform() - Reset the transformation matrix to default value. - - \sa QtQuick2::Context2D::transform(), QtQuick2::Context2D::setTransform(), QtQuick2::Context2D::reset() -*/ -static v8::Handle ctx2d_resetTransform(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - r->context->state.matrix = QTransform(); - r->context->buffer()->updateMatrix(r->context->state.matrix); - - return args.This(); -} - - -/*! - \qmlmethod object QtQuick2::Context2D::shear(real sh, real sv ) - Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction. -*/ -static v8::Handle ctx2d_shear(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - if (args.Length() == 2) { - qreal sh = args[0]->NumberValue(); - qreal sv = args[1]->NumberValue(); - - if (!qIsFinite(sh) || !qIsFinite(sv)) - return args.This(); - - r->context->state.matrix.shear(sh, sv); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - return args.This(); -} -// compositing - -/*! - \qmlproperty real QtQuick2::Context2D::globalAlpha - Holds the the current alpha value applied to rendering operations. - The value must be in the range from 0.0 (fully transparent) to 1.0 (fully opque). - The default value is 1.0. -*/ -static v8::Handle ctx2d_globalAlpha(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - return v8::Number::New(r->context->state.globalAlpha); -} - -static void ctx2d_globalAlpha_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - qreal globalAlpha = value->NumberValue(); - - if (!qIsFinite(globalAlpha)) - return; - - if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) { - r->context->state.globalAlpha = globalAlpha; - r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha); - } -} - -/*! - \qmlproperty string QtQuick2::Context2D::globalCompositeOperation - Holds the the current the current composition operation, from the list below: - \list - \o source-atop - A atop B. Display the source image wherever both images are opaque. - Display the destination image wherever the destination image is opaque but the source image is transparent. - Display transparency elsewhere. - \o source-in - A in B. Display the source image wherever both the source image and destination image are opaque. - Display transparency elsewhere. - \o source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent. - Display transparency elsewhere. - \o source-over - (default) A over B. Display the source image wherever the source image is opaque. - Display the destination image elsewhere. - \o destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa. - \o destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa. - \o destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa. - \o destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa. - \o lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit. - \o copy - A (B is ignored). Display the source image instead of the destination image. - \o xor - A xor B. Exclusive OR of the source image and destination image. - \endlist - - Additionally, this property also accepts the compositon modes listed in \a {QPainter::CompositionMode}. According to the W3C standard, these - extension composition modes are provided as "vendorName-operationName" syntax, for example: \c {QPainter::CompositionMode_Exclusion} is porvided as - "qt-exclusion". -*/ -static v8::Handle ctx2d_globalCompositeOperation(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation)); -} - -static void ctx2d_globalCompositeOperation_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - - QString mode = engine->toString(value); - QPainter::CompositionMode cm = qt_composite_mode_from_string(mode); - if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over")) - return; - - if (cm != r->context->state.globalCompositeOperation) { - r->context->state.globalCompositeOperation = cm; - r->context->buffer()->setGlobalCompositeOperation(cm); - } -} - -// colors and styles -/*! - \qmlproperty variant QtQuick2::Context2D::fillStyle - Holds the current style used for filling shapes. - The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored. - This property accepts several color syntaxes: - \list - \o 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)' - \o 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)' - \o 'hsl(hue, saturation, lightness)' - \o 'hsla(hue, saturation, lightness, alpha)' - \o '#RRGGBB' - for example: '#00FFCC' - \o Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0) - \endlist - If the \a fillStyle or \a strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the - best performance, because it's already a valid QColor value, does not need to be parsed everytime. - - The default value is '#000000'. - \sa QtQuick2::Context2D::createLinearGradient - \sa QtQuick2::Context2D::createRadialGradient - \sa QtQuick2::Context2D::createPattern - \sa QtQuick2::Context2D::strokeStyle - */ -static v8::Handle ctx2d_fillStyle(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - QColor color = r->context->state.fillStyle.color(); - if (color.isValid()) { - if (color.alpha() == 255) - return engine->toString(color.name()); - QString alphaString = QString::number(color.alphaF(), 'f'); - while (alphaString.endsWith(QLatin1Char('0'))) - alphaString.chop(1); - if (alphaString.endsWith(QLatin1Char('.'))) - alphaString += QLatin1Char('0'); - return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString)); - } - return r->context->m_fillStyle; -} - -static void ctx2d_fillStyle_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - if (value->IsObject()) { - QColor color = engine->toVariant(value, qMetaTypeId()).value(); - if (color.isValid()) { - r->context->state.fillStyle = color; - r->context->buffer()->setFillStyle(color); - r->context->m_fillStyle = value; - } else { - QV8Context2DStyleResource *style = v8_resource_cast(value->ToObject()); - if (style && style->brush != r->context->state.fillStyle) { - r->context->state.fillStyle = style->brush; - r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY); - r->context->m_fillStyle = value; - r->context->state.fillPatternRepeatX = style->patternRepeatX; - r->context->state.fillPatternRepeatY = style->patternRepeatY; - } - } - } else if (value->IsString()) { - QColor color = qt_color_from_string(value); - if (color.isValid() && r->context->state.fillStyle != QBrush(color)) { - r->context->state.fillStyle = QBrush(color); - r->context->buffer()->setFillStyle(r->context->state.fillStyle); - r->context->m_fillStyle = value; - } - } -} -/*! - \qmlproperty enumeration QtQuick2::Context2D::fillRule - Holds the current fill rule used for filling shapes. The following fill rules supported: - \list - \o Qt.OddEvenFill - \o Qt.WindingFill - \endlist - Note: Unlike the \a QPainterPath, the Canvas API uses the winding fill as the default fill rule. - The fillRule property is part of the context rendering state. - - \sa QtQuick2::Context2D::fillStyle - */ -static v8::Handle ctx2d_fillRule(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - return engine->fromVariant(r->context->state.fillRule); -} - -static void ctx2d_fillRule_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - if ((value->IsString() && engine->toString(value) == QStringLiteral("WindingFill")) - ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) { - r->context->state.fillRule = Qt::WindingFill; - } else if ((value->IsString() && engine->toString(value) == QStringLiteral("OddEvenFill")) - ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) { - r->context->state.fillRule = Qt::OddEvenFill; - } else { - //error - } - r->context->m_path.setFillRule(r->context->state.fillRule); -} -/*! - \qmlproperty variant QtQuick2::Context2D::strokeStyle - Holds the current color or style to use for the lines around shapes, - The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. - Invalid values are ignored. - - The default value is '#000000'. - - \sa QtQuick2::Context2D::createLinearGradient - \sa QtQuick2::Context2D::createRadialGradient - \sa QtQuick2::Context2D::createPattern - \sa QtQuick2::Context2D::fillStyle - */ -v8::Handle ctx2d_strokeStyle(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - QColor color = r->context->state.strokeStyle.color(); - if (color.isValid()) { - if (color.alpha() == 255) - return engine->toString(color.name()); - QString alphaString = QString::number(color.alphaF(), 'f'); - while (alphaString.endsWith(QLatin1Char('0'))) - alphaString.chop(1); - if (alphaString.endsWith(QLatin1Char('.'))) - alphaString += QLatin1Char('0'); - return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString)); - } - return r->context->m_strokeStyle; -} - -static void ctx2d_strokeStyle_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - if (value->IsObject()) { - QColor color = engine->toVariant(value, qMetaTypeId()).value(); - if (color.isValid()) { - r->context->state.fillStyle = color; - r->context->buffer()->setStrokeStyle(color); - r->context->m_strokeStyle = value; - } else { - QV8Context2DStyleResource *style = v8_resource_cast(value->ToObject()); - if (style && style->brush != r->context->state.strokeStyle) { - r->context->state.strokeStyle = style->brush; - r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY); - r->context->m_strokeStyle = value; - r->context->state.strokePatternRepeatX = style->patternRepeatX; - r->context->state.strokePatternRepeatY = style->patternRepeatY; - - } - } - } else if (value->IsString()) { - QColor color = qt_color_from_string(value); - if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) { - r->context->state.strokeStyle = QBrush(color); - r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle); - r->context->m_strokeStyle = value; - } - } -} - -/*! - \qmlmethod object QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1) - Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between - the start point (\a x0, \a y0) and the end point (\a x1, \a y1). - - A gradient is a smooth transition between colors. There are two types of gradients: linear and radial. - Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between - to the gradient's starting and end points or circles. - - \sa QtQuick2::Context2D::CanvasGradient::addColorStop - \sa QtQuick2::Context2D::createRadialGradient - \sa QtQuick2::Context2D::ctx2d_createConicalGradient - \sa QtQuick2::Context2D::createPattern - \sa QtQuick2::Context2D::fillStyle - \sa QtQuick2::Context2D::strokeStyle - */ - -static v8::Handle ctx2d_createLinearGradient(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE(); - - if (args.Length() == 4) { - QSGContext2DEngineData *ed = engineData(engine); - v8::Local gradient = ed->constructorGradient->NewInstance(); - QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine); - qreal x0 = args[0]->NumberValue(); - qreal y0 = args[1]->NumberValue(); - qreal x1 = args[2]->NumberValue(); - qreal y1 = args[3]->NumberValue(); - - if (!qIsFinite(x0) - || !qIsFinite(y0) - || !qIsFinite(x1) - || !qIsFinite(y1)) - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments") - - r->brush = QLinearGradient(x0, y0, x1, y1); - gradient->SetExternalResource(r); - return gradient; - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1) - Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with - origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1. - - \sa QtQuick2::Context2D::CanvasGradient::addColorStop - \sa QtQuick2::Context2D::createLinearGradient - \sa QtQuick2::Context2D::ctx2d_createConicalGradient - \sa QtQuick2::Context2D::createPattern - \sa QtQuick2::Context2D::fillStyle - \sa QtQuick2::Context2D::strokeStyle - */ - -static v8::Handle ctx2d_createRadialGradient(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE(); - - if (args.Length() == 6) { - QSGContext2DEngineData *ed = engineData(engine); - v8::Local gradient = ed->constructorGradient->NewInstance(); - QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine); - - qreal x0 = args[0]->NumberValue(); - qreal y0 = args[1]->NumberValue(); - qreal r0 = args[2]->NumberValue(); - qreal x1 = args[3]->NumberValue(); - qreal y1 = args[4]->NumberValue(); - qreal r1 = args[5]->NumberValue(); - - if (!qIsFinite(x0) - || !qIsFinite(y0) - || !qIsFinite(x1) - || !qIsFinite(r0) - || !qIsFinite(r1) - || !qIsFinite(y1)) - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments") - - if (r0 < 0 || r1 < 0) - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments") - - - r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0)); - gradient->SetExternalResource(r); - return gradient; - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::createConicalGradient(real x, real y, real angle) - Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y) - with start angle \c angle in units of radians. - - \sa QtQuick2::Context2D::CanvasGradient::addColorStop - \sa QtQuick2::Context2D::createLinearGradient - \sa QtQuick2::Context2D::ctx2d_createRadialGradient - \sa QtQuick2::Context2D::createPattern - \sa QtQuick2::Context2D::fillStyle - \sa QtQuick2::Context2D::strokeStyle - */ - -static v8::Handle ctx2d_createConicalGradient(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE(); - - if (args.Length() == 6) { - QSGContext2DEngineData *ed = engineData(engine); - v8::Local gradient = ed->constructorGradient->NewInstance(); - QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine); - - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal angle = DEGREES(args[2]->NumberValue()); - if (!qIsFinite(x) || !qIsFinite(y)) - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments"); - - if (!qIsFinite(angle)) - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments"); - - r->brush = QConicalGradient(x, y, angle); - gradient->SetExternalResource(r); - return gradient; - } - - return args.This(); -} -/*! - \qmlmethod variant createPattern(Color color, enumeration patternMode) - This is a overload function. - Returns a CanvasPattern object that uses the given \c color and \c patternMode. - The valid pattern modes are: - \list - \o Qt.SolidPattern - \o Qt.Dense1Pattern - \o Qt.Dense2Pattern - \o Qt.Dense3Pattern - \o Qt.Dense4Pattern - \o Qt.Dense5Pattern - \o Qt.Dense6Pattern - \o Qt.Dense7Pattern - \o Qt.HorPattern - \o Qt.VerPattern - \o Qt.CrossPattern - \o Qt.BDiagPattern - \o Qt.FDiagPattern - \o Qt.DiagCrossPattern -\endlist - \sa Qt::BrushStyle - */ -/*! - \qmlmethod variant createPattern(Image image, string repetition) - Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument. - - The \a image parameter must be a valid Image item, a valid \a QtQuick2::CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception. - - The allowed values for \a repetition are: - - \list - \o "repeat" - both directions - \o "repeat-x - horizontal only - \o "repeat-y" - vertical only - \o "no-repeat" - neither - \endlist - - If the repetition argument is empty or null, the value "repeat" is used. - - \sa QtQuick2::Context2D::strokeStyle - \sa QtQuick2::Context2D::fillStyle - */ -static v8::Handle ctx2d_createPattern(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE(); - - if (args.Length() == 2) { - QSGContext2DEngineData *ed = engineData(engine); - QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine); - - QColor color = engine->toVariant(args[0], qMetaTypeId()).value(); - if (color.isValid()) { - int patternMode = args[1]->IntegerValue(); - Qt::BrushStyle style = Qt::SolidPattern; - if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) { - style = static_cast(patternMode); - } - styleResouce->brush = QBrush(color, style); - } else { - QImage patternTexture; - - if (args[0]->IsObject()) { - QV8Context2DPixelArrayResource *pixelData = v8_resource_cast(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject()); - if (pixelData) { - patternTexture = pixelData->image; - } - } else { - patternTexture = r->context->createImage(QUrl(engine->toString(args[0]->ToString()))); - } - - if (!patternTexture.isNull()) { - styleResouce->brush.setTextureImage(patternTexture); - - QString repetition = engine->toString(args[1]); - if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) { - styleResouce->patternRepeatX = true; - styleResouce->patternRepeatY = true; - } else if (repetition == QStringLiteral("repeat-x")) { - styleResouce->patternRepeatX = true; - } else if (repetition == QStringLiteral("repeat-y")) { - styleResouce->patternRepeatY = true; - } else if (repetition == QStringLiteral("no-repeat")) { - styleResouce->patternRepeatY = false; - styleResouce->patternRepeatY = false; - } else { - //TODO: exception: SYNTAX_ERR - } - - } - } - - v8::Local pattern = ed->constructorPattern->NewInstance(); - pattern->SetExternalResource(styleResouce); - return pattern; - - } - return v8::Undefined(); -} - -// line styles -/*! - \qmlproperty string QtQuick2::Context2D::lineCap - Holds the the current line cap style. - The possible line cap styles are: - \list - \o butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value. - \o round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line. - \o square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line. - \endlist - Other values are ignored. -*/ -v8::Handle ctx2d_lineCap(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - switch (r->context->state.lineCap) { - case Qt::RoundCap: - return engine->toString(QLatin1String("round")); - case Qt::FlatCap: - return engine->toString(QLatin1String("butt")); - case Qt::SquareCap: - return engine->toString(QLatin1String("square")); - default: - break; - } - return engine->toString(QLatin1String("butt"));; -} - -static void ctx2d_lineCap_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - QString lineCap = engine->toString(value); - Qt::PenCapStyle cap; - if (lineCap == QLatin1String("round")) - cap = Qt::RoundCap; - else if (lineCap == QLatin1String("butt")) - cap = Qt::FlatCap; - else if (lineCap == QLatin1String("square")) - cap = Qt::SquareCap; - else - return; - - if (cap != r->context->state.lineCap) { - r->context->state.lineCap = cap; - r->context->buffer()->setLineCap(cap); - } -} - -/*! - \qmlproperty string QtQuick2::Context2D::lineJoin - Holds the the current line join style. A join exists at any point in a subpath - shared by two consecutive lines. When a subpath is closed, then a join also exists - at its first point (equivalent to its last point) connecting the first and last lines in the subpath. - - The possible line join styles are: - \list - \o bevel - this is all that is rendered at joins. - \o round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins. - \o miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style. - \endlist - Other values are ignored. -*/ -v8::Handle ctx2d_lineJoin(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - switch (r->context->state.lineJoin) { - case Qt::RoundJoin: - return engine->toString(QLatin1String("round")); - case Qt::BevelJoin: - return engine->toString(QLatin1String("bevel")); - case Qt::MiterJoin: - return engine->toString(QLatin1String("miter")); - default: - break; - } - return engine->toString(QLatin1String("miter")); -} - -static void ctx2d_lineJoin_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - QString lineJoin = engine->toString(value); - Qt::PenJoinStyle join; - if (lineJoin == QLatin1String("round")) - join = Qt::RoundJoin; - else if (lineJoin == QLatin1String("bevel")) - join = Qt::BevelJoin; - else if (lineJoin == QLatin1String("miter")) - join = Qt::MiterJoin; - else - return; - - if (join != r->context->state.lineJoin) { - r->context->state.lineJoin = join; - r->context->buffer()->setLineJoin(join); - } -} - -/*! - \qmlproperty real QtQuick2::Context2D::lineWidth - Holds the the current line width. Values that are not finite values greater than zero are ignored. - */ -v8::Handle ctx2d_lineWidth(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - return v8::Number::New(r->context->state.lineWidth); -} - -static void ctx2d_lineWidth_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - qreal w = value->NumberValue(); - - if (w > 0 && qIsFinite(w) && w != r->context->state.lineWidth) { - r->context->state.lineWidth = w; - r->context->buffer()->setLineWidth(w); - } -} - -/*! - \qmlproperty real QtQuick2::Context2D::miterLimit - Holds the current miter limit ratio. - The default miter limit value is 10.0. - */ -v8::Handle ctx2d_miterLimit(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - return v8::Number::New(r->context->state.miterLimit); -} - -static void ctx2d_miterLimit_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - qreal ml = value->NumberValue(); - - if (ml > 0 && qIsFinite(ml) && ml != r->context->state.miterLimit) { - r->context->state.miterLimit = ml; - r->context->buffer()->setMiterLimit(ml); - } -} - -// shadows -/*! - \qmlproperty real QtQuick2::Context2D::shadowBlur - Holds the current level of blur applied to shadows - */ -v8::Handle ctx2d_shadowBlur(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - return v8::Number::New(r->context->state.shadowBlur); -} - -static void ctx2d_shadowBlur_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - qreal blur = value->NumberValue(); - - if (blur > 0 && qIsFinite(blur) && blur != r->context->state.shadowBlur) { - r->context->state.shadowBlur = blur; - r->context->buffer()->setShadowBlur(blur); - } -} - -/*! - \qmlproperty string QtQuick2::Context2D::shadowColor - Holds the current shadow color. - */ -v8::Handle ctx2d_shadowColor(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - return engine->toString(r->context->state.shadowColor.name()); -} - -static void ctx2d_shadowColor_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QColor color = qt_color_from_string(value); - - if (color.isValid() && color != r->context->state.shadowColor) { - r->context->state.shadowColor = color; - r->context->buffer()->setShadowColor(color); - } -} - - -/*! - \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX - Holds the current shadow offset in the positive horizontal distance. - - \sa QtQuick2::Context2D::shadowOffsetY - */ -v8::Handle ctx2d_shadowOffsetX(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - return v8::Number::New(r->context->state.shadowOffsetX); -} - -static void ctx2d_shadowOffsetX_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - qreal offsetX = value->NumberValue(); - if (qIsFinite(offsetX) && offsetX != r->context->state.shadowOffsetX) { - r->context->state.shadowOffsetX = offsetX; - r->context->buffer()->setShadowOffsetX(offsetX); - } -} -/*! - \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY - Holds the current shadow offset in the positive vertical distance. - - \sa QtQuick2::Context2D::shadowOffsetX - */ -v8::Handle ctx2d_shadowOffsetY(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - - return v8::Number::New(r->context->state.shadowOffsetY); -} - -static void ctx2d_shadowOffsetY_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - qreal offsetY = value->NumberValue(); - if (qIsFinite(offsetY) && offsetY != r->context->state.shadowOffsetY) { - r->context->state.shadowOffsetY = offsetY; - r->context->buffer()->setShadowOffsetY(offsetY); - } -} - -v8::Handle ctx2d_path(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - return r->context->m_v8path; -} - -static void ctx2d_path_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - r->context->beginPath(); - if (value->IsObject()) { - QDeclarativePath* path = qobject_cast(engine->toQObject(value)); - if (path) - r->context->m_path = path->path(); - } else { - QString path = engine->toString(value->ToString()); - QDeclarativeSvgParser::parsePathDataFast(path, r->context->m_path); - } - r->context->m_v8path = value; -} - -//rects -/*! - \qmlmethod object QtQuick2::Context2D::clearRect(real x, real y, real w, real h) - Clears all pixels on the canvas in the given rectangle to transparent black. - */ -static v8::Handle ctx2d_clearRect(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->buffer()->clearRect(x, y, w, h); - } - - return args.This(); -} -/*! - \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h) - Paint the specified rectangular area using the fillStyle. - - \sa QtQuick2::Context2D::fillStyle - */ -static v8::Handle ctx2d_fillRect(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->buffer()->fillRect(x, y, w, h); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h) - Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin, - and (if appropriate) miterLimit attributes. - - \sa QtQuick2::Context2D::strokeStyle - \sa QtQuick2::Context2D::lineWidth - \sa QtQuick2::Context2D::lineJoin - \sa QtQuick2::Context2D::miterLimit - */ -static v8::Handle ctx2d_strokeRect(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->buffer()->strokeRect(x, y, w, h); - } - - return args.This(); -} - -// Complex shapes (paths) API -/*! - \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise) - Adds an arc to the current subpath that lies on the circumference of the circle whose center is at the point (\c x,\cy) and whose radius is \c radius. - \image qml-item-canvas-arcTo2.png - \sa QtQuick2::Context2D::arcTo - See {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc} - */ -static v8::Handle ctx2d_arc(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - if (args.Length() >= 5) { - bool antiClockwise = false; - - if (args.Length() == 6) - antiClockwise = args[5]->BooleanValue(); - - qreal radius = args[2]->NumberValue(); - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal sa = args[3]->NumberValue(); - qreal ea = args[4]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(sa) || !qIsFinite(ea)) - return args.This(); - - if (radius < 0) - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius"); - - r->context->arc(args[0]->NumberValue(), - args[1]->NumberValue(), - radius, - args[3]->NumberValue(), - args[4]->NumberValue(), - antiClockwise); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius) - - Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line. - To draw an arc, you begin with the same steps your followed to create a line: - \list - \o Call the context.beginPath() method to set a new path. - \o Call the context.moveTo(\c x, \c y) method to set your starting position on the canvas at the point (\c x,\c y). - \o To draw an arc or circle, call the context.arcTo(\c x1, \c y1, \c x2, \c y2,\c radius) method. - This adds an arc with starting point (\c x1,\c y1), ending point (\c x2, \c y2), and radius \c radius to the current subpath and connects - it to the previous subpath by a straight line. - \endlist - \image qml-item-canvas-arcTo.png - Both startAngle and endAngle are measured from the x axis in units of radians. - - \image qml-item-canvas-startAngle.png - The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction. - \sa QtQuick2::Context2D::arc - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d context standard for arcTo} - */ -static v8::Handle ctx2d_arcTo(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - - if (args.Length() == 5) { - qreal x1 = args[0]->NumberValue(); - qreal y1 = args[1]->NumberValue(); - qreal x2 = args[2]->NumberValue(); - qreal y2 = args[3]->NumberValue(); - - if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2)) - return args.This(); - - qreal radius = args[4]->NumberValue(); - if (radius < 0) - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius"); - r->context->arcTo(args[0]->NumberValue(), - args[1]->NumberValue(), - args[2]->NumberValue(), - args[3]->NumberValue(), - args[4]->NumberValue()); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::beginPath() - - Resets the current path to a new path. - */ -static v8::Handle ctx2d_beginPath(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - r->context->beginPath(); - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y) - - Adds a cubic Bezier curve between the current position and the given endPoint using the control points specified by (\c cp1x, cp1y), - and (\c cp2x, \c cp2y). - After the curve is added, the current position is updated to be at the end point (\c x, \c y) of the curve. - The following code produces the path shown below: - \code - ctx.strokeStyle = Qt.rgba(0, 0, 0, 1); - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(20, 0);//start point - ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0); - ctx.stroke(); - \endcode - \image qml-item-canvas-bezierCurveTo.png - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo} - \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo} - */ -static v8::Handle ctx2d_bezierCurveTo(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 6) { - qreal cp1x = args[0]->NumberValue(); - qreal cp1y = args[1]->NumberValue(); - qreal cp2x = args[2]->NumberValue(); - qreal cp2y = args[3]->NumberValue(); - qreal x = args[4]->NumberValue(); - qreal y = args[5]->NumberValue(); - - if (!qIsFinite(cp1x) || !qIsFinite(cp1y) || !qIsFinite(cp2x) || !qIsFinite(cp2y) || !qIsFinite(x) || !qIsFinite(y)) - return args.This(); - - r->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::clip() - - Creates the clipping region from the current path. - Any parts of the shape outside the clipping path are not displayed. - To create a complex shape using the \a clip() method: - - \list 1 - \o Call the \c{context.beginPath()} method to set the clipping path. - \o Define the clipping path by calling any combination of the \c{lineTo}, - \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods. - \o Call the \c{context.clip()} method. - \endlist - - The new shape displays. The following shows how a clipping path can - modify how an image displays: - - \image qml-canvas-clip-complex.png - \sa QtQuick2::Context2D::beginPath() - \sa QtQuick2::Context2D::closePath() - \sa QtQuick2::Context2D::stroke() - \sa QtQuick2::Context2D::fill() - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip} - */ -static v8::Handle ctx2d_clip(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QPainterPath clipPath = r->context->m_path; - clipPath.closeSubpath(); - if (!r->context->state.clipPath.isEmpty()) - r->context->state.clipPath = clipPath.intersected(r->context->state.clipPath); - else - r->context->state.clipPath = clipPath; - r->context->buffer()->clip(r->context->state.clipPath); - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::closePath() - Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path. - The current point of the new path is the previous subpath's first point. - - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath} - */ -static v8::Handle ctx2d_closePath(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - r->context->closePath(); - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::fill() - - Fills the subpaths with the current fill style. - - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill} - - \sa QtQuick2::Context2D::fillStyle - */ -static v8::Handle ctx2d_fill(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r); - - r->context->buffer()->fill(r->context->m_path); - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::lineTo(real x, real y) - - Draws a line from the current position to the point (x, y). - */ -static v8::Handle ctx2d_lineTo(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 2) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - - r->context->lineTo(x, y); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::moveTo(real x, real y) - - Creates a new subpath with the given point. - */ -static v8::Handle ctx2d_moveTo(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - if (args.Length() == 2) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - r->context->moveTo(x, y); - } - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y) - - Adds a quadratic Bezier curve between the current point and the endpoint (\c x, \c y) with the control point specified by (\c cpx, \c cpy). - - See {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for for quadraticCurveTo} - */ -static v8::Handle ctx2d_quadraticCurveTo(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - if (args.Length() == 4) { - qreal cpx = args[0]->NumberValue(); - qreal cpy = args[1]->NumberValue(); - qreal x = args[2]->NumberValue(); - qreal y = args[3]->NumberValue(); - - if (!qIsFinite(cpx) || !qIsFinite(cpy) || !qIsFinite(x) || !qIsFinite(y)) - return args.This(); - - r->context->quadraticCurveTo(cpx, cpy, x, y); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::rect(real x, real y, real w, real h) - - Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath. - */ -static v8::Handle ctx2d_rect(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->rect(x, y, w, h); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius) - - Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the - ellipses defining the corners of the rounded rectangle. - */ -static v8::Handle ctx2d_roundedRect(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - if (args.Length() == 6) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - qreal xr = args[4]->NumberValue(); - qreal yr = args[5]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - if (!qIsFinite(xr) || !qIsFinite(yr)) - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "roundedRect(): Invalid arguments"); - - r->context->roundedRect(x, y, w, h, xr, yr); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::ellipse(real x, real y, real w, real h) - - Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h, - and adds it to the path as a closed subpath. - - The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position). - */ -static v8::Handle ctx2d_ellipse(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - - r->context->ellipse(x, y, w, h); - } - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::text(string text, real x, real y) - - Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied. - The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y). - */ -static v8::Handle ctx2d_text(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE(); - if (args.Length() == 3) { - qreal x = args[1]->NumberValue(); - qreal y = args[2]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - r->context->text(engine->toString(args[0]), x, y); - } - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::stroke() - - Strokes the subpaths with the current stroke style. - - See {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke} - - \sa QtQuick2::Context2D::strokeStyle - */ -static v8::Handle ctx2d_stroke(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - - r->context->buffer()->stroke(r->context->m_path); - - return args.This(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::isPointInPath(real x, real y) - - Returns true if the given point is in the current path. - - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath} - */ -static v8::Handle ctx2d_isPointInPath(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - bool pointInPath = false; - if (args.Length() == 2) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y)) - return v8::Boolean::New(false); - pointInPath = r->context->isPointInPath(x, y); - } - return v8::Boolean::New(pointInPath); -} - -static v8::Handle ctx2d_drawFocusRing(const v8::Arguments &args) -{ - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported"); - return args.This(); -} - -static v8::Handle ctx2d_setCaretSelectionRect(const v8::Arguments &args) -{ - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported"); - return args.This(); -} - -static v8::Handle ctx2d_caretBlinkRate(const v8::Arguments &args) -{ - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported"); - return args.This(); -} -// text -/*! - \qmlproperty string QtQuick2::Context2D::font - Holds the current font settings. - - The default font value is "10px sans-serif". - See {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font} - */ -v8::Handle ctx2d_font(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - return engine->toString(r->context->state.font.toString()); -} - -static void ctx2d_font_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - QString fs = engine->toString(value); - QFont font = qt_font_from_string(fs); - if (font != r->context->state.font) { - r->context->state.font = font; - } -} - -/*! - \qmlproperty string QtQuick2::Context2D::textAlign - - Holds the current text alignment settings. - The possible values are: - \list - \o start - \o end - \o left - \o right - \o center - \endlist - Other values are ignored. The default value is "start". - */ -v8::Handle ctx2d_textAlign(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - QV8Engine *engine = V8ENGINE_ACCESSOR(); - switch (r->context->state.textAlign) { - case QSGContext2D::Start: - return engine->toString(QLatin1String("start")); - case QSGContext2D::End: - return engine->toString(QLatin1String("end")); - case QSGContext2D::Left: - return engine->toString(QLatin1String("left")); - case QSGContext2D::Right: - return engine->toString(QLatin1String("right")); - case QSGContext2D::Center: - return engine->toString(QLatin1String("center")); - default: - break; - } - return engine->toString(QLatin1String("start")); -} - -static void ctx2d_textAlign_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - QV8Engine *engine = V8ENGINE_ACCESSOR(); - - QString textAlign = engine->toString(value); - - QSGContext2D::TextAlignType ta; - if (textAlign == QLatin1String("start")) - ta = QSGContext2D::Start; - else if (textAlign == QLatin1String("end")) - ta = QSGContext2D::End; - else if (textAlign == QLatin1String("left")) - ta = QSGContext2D::Left; - else if (textAlign == QLatin1String("right")) - ta = QSGContext2D::Right; - else if (textAlign == QLatin1String("center")) - ta = QSGContext2D::Center; - else - return; - - if (ta != r->context->state.textAlign) { - r->context->state.textAlign = ta; - } -} - -/*! - \qmlproperty string QtQuick2::Context2D::textBaseline - - Holds the current baseline alignment settings. - The possible values are: - \list - \o top - \o hanging - \o middle - \o alphabetic - \o ideographic - \o bottom - \endlist - Other values are ignored. The default value is "alphabetic". - */ -v8::Handle ctx2d_textBaseline(v8::Local, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE_ACCESSOR(); - switch (r->context->state.textBaseline) { - case QSGContext2D::Alphabetic: - return engine->toString(QLatin1String("alphabetic")); - case QSGContext2D::Hanging: - return engine->toString(QLatin1String("hanging")); - case QSGContext2D::Top: - return engine->toString(QLatin1String("top")); - case QSGContext2D::Bottom: - return engine->toString(QLatin1String("bottom")); - case QSGContext2D::Middle: - return engine->toString(QLatin1String("middle")); - default: - break; - } - return engine->toString(QLatin1String("alphabetic")); -} - -static void ctx2d_textBaseline_set(v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QV8Context2DResource *r = v8_resource_cast(info.This()); - CHECK_CONTEXT_SETTER(r) - QV8Engine *engine = V8ENGINE_ACCESSOR(); - QString textBaseline = engine->toString(value); - - QSGContext2D::TextBaseLineType tb; - if (textBaseline == QLatin1String("alphabetic")) - tb = QSGContext2D::Alphabetic; - else if (textBaseline == QLatin1String("hanging")) - tb = QSGContext2D::Hanging; - else if (textBaseline == QLatin1String("top")) - tb = QSGContext2D::Top; - else if (textBaseline == QLatin1String("bottom")) - tb = QSGContext2D::Bottom; - else if (textBaseline == QLatin1String("middle")) - tb = QSGContext2D::Middle; - else - return; - - if (tb != r->context->state.textBaseline) { - r->context->state.textBaseline = tb; - } -} - -/*! - \qmlmethod object QtQuick2::Context2D::fillText(text, x, y) - Fills the given text at the given position. - \sa QtQuick2::Context2D::font - \sa QtQuick2::Context2D::textAlign - \sa QtQuick2::Context2D::textBaseline - \sa QtQuick2::Context2D::strokeText - */ -static v8::Handle ctx2d_fillText(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE(); - if (args.Length() == 3) { - qreal x = args[1]->NumberValue(); - qreal y = args[2]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0])); - r->context->buffer()->fill(textPath); - } - return args.This(); -} -/*! - \qmlmethod object QtQuick2::Context2D::strokeText(text, x, y) - Strokes the given text at the given position. - \sa QtQuick2::Context2D::font - \sa QtQuick2::Context2D::textAlign - \sa QtQuick2::Context2D::textBaseline - \sa QtQuick2::Context2D::fillText - */ -static v8::Handle ctx2d_strokeText(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE(); - if (args.Length() == 3) { - qreal x = args[1]->NumberValue(); - qreal y = args[2]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0])); - r->context->buffer()->stroke(textPath); - } - return args.This(); -} -/*! - \qmlclass QtQuick2::TextMetrics - \inqmlmodule QtQuick 2 - \since QtQuick 2.0 - \brief The Context2D TextMetrics interface. - The TextMetrics object can be created by QtQuick2::Context2D::measureText method. - See {http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details. - - \sa QtQuick2::Context2D::measureText - \sa QtQuick2::TextMetrics::width - */ - -/*! - \qmlproperty int QtQuick2::TextMetrics::width - Holds the advance width of the text that was passed to the QtQuick2::Context2D::measureText() method. - This property is read only. - */ - -/*! - \qmlmethod variant QtQuick2::Context2D::measureText(text) - Returns a TextMetrics object with the metrics of the given text in the current font. - */ -static v8::Handle ctx2d_measureText(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE(); - - if (args.Length() == 1) { - QFontMetrics fm(r->context->state.font); - uint width = fm.width(engine->toString(args[0])); - v8::Local tm = v8::Object::New(); - tm->Set(v8::String::New("width"), v8::Number::New(width)); - return tm; - } - return v8::Undefined(); -} - -// drawing images -/*! - \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy) - Draws the given \a image on the canvas at position (\a dx, \a dy). - Note: - The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object. - When given as Image item, if the image isn't fully loaded, this method draws nothing. - When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first. - This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object. - - \sa QtQuick2::CanvasImageData - \sa QtQuick2::Image - \sa QtQuick2::Canvas::loadImage - \sa QtQuick2::Canvas::isImageLoaded - \sa QtQuick2::Canvas::imageLoaded - - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage} - */ -/*! - \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh) - This is an overloaded function. - Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw, - height \a dh. - - Note: - The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object. - When given as Image item, if the image isn't fully loaded, this method draws nothing. - When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first. - This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object. - - \sa QtQuick2::CanvasImageData - \sa QtQuick2::Image - \sa QtQuick2::Canvas::loadImage - \sa QtQuick2::Canvas::isImageLoaded - \sa QtQuick2::Canvas::imageLoaded - - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage} - */ -/*! - \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh) - This is an overloaded function. - Draws the given item as \a image from source point (\a sx, \a sy) and source width \sw, source height \sh - onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh. - - - Note: - The \a image type can be an Image or Canvas item, an image url or a \a {QtQuick2::CanvasImageData} object. - When given as Image item, if the image isn't fully loaded, this method draws nothing. - When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first. - This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object. - - \sa QtQuick2::CanvasImageData - \sa QtQuick2::Image - \sa QtQuick2::Canvas::loadImage - \sa QtQuick2::Canvas::isImageLoaded - \sa QtQuick2::Canvas::imageLoaded - - \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage} -*/ -static v8::Handle ctx2d_drawImage(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE(); - qreal sx, sy, sw, sh, dx, dy, dw, dh; - - if (!args.Length()) - return args.This(); - - QImage image; - if (args[0]->IsString()) { - QUrl url(engine->toString(args[0]->ToString())); - if (!url.isValid()) - V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); - - image = r->context->createImage(url); - } else if (args[0]->IsObject()) { - QSGImage *imageItem = qobject_cast(engine->toQObject(args[0]->ToObject())); - QSGCanvasItem *canvas = qobject_cast(engine->toQObject(args[0]->ToObject())); - - QV8Context2DPixelArrayResource *pix = v8_resource_cast(args[0]->ToObject()->GetInternalField(0)->ToObject()); - if (pix) { - image = pix->image; - } else if (imageItem) { - image = imageItem->pixmap().toImage(); - } else if (canvas) { - image = canvas->toImage(); - } else { - V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); - } - } else { - V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); - } - if (args.Length() == 3) { - dx = args[1]->NumberValue(); - dy = args[2]->NumberValue(); - sx = 0; - sy = 0; - sw = image.width(); - sh = image.height(); - dw = sw; - dh = sh; - } else if (args.Length() == 5) { - sx = 0; - sy = 0; - sw = image.width(); - sh = image.height(); - dx = args[1]->NumberValue(); - dy = args[2]->NumberValue(); - dw = args[3]->NumberValue(); - dh = args[4]->NumberValue(); - } else if (args.Length() == 9) { - sx = args[1]->NumberValue(); - sy = args[2]->NumberValue(); - sw = args[3]->NumberValue(); - sh = args[4]->NumberValue(); - dx = args[5]->NumberValue(); - dy = args[6]->NumberValue(); - dw = args[7]->NumberValue(); - dh = args[8]->NumberValue(); - } else { - return args.This(); - } - - if (!qIsFinite(sx) - || !qIsFinite(sy) - || !qIsFinite(sw) - || !qIsFinite(sh) - || !qIsFinite(dx) - || !qIsFinite(dy) - || !qIsFinite(dw) - || !qIsFinite(dh)) - return args.This(); - - if (!image.isNull()) { - if (sx < 0 || sy < 0 || sw == 0 || sh == 0 - || sx + sw > image.width() || sy + sh > image.height() - || sx + sw < 0 || sy + sh < 0) { - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error"); - } - - r->context->buffer()->drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh); - } - - return args.This(); -} - -// pixel manipulation -/*! - \qmlclass QtQuick2::CanvasImageData - The \a QtQuick2::CanvasImageData object holds the image pixel data. - - The \a QtQuick2::CanvasImageData object has the actual dimensions of the data stored in - this object and holds the one-dimensional array containing the data in RGBA order, - as integers in the range 0 to 255. - - \sa QtQuick2::CanvasImageData::width - \sa QtQuick2::CanvasImageData::height - \sa QtQuick2::CanvasImageData::data - \sa QtQuick2::Context2D::createImageData - \sa QtQuick2::Context2D::getImageData - \sa QtQuick2::Context2D::putImageData - */ -/*! - \qmlproperty QtQuick2::CanvasImageData::width - Holds the actual width dimension of the data in the ImageData object, in device pixels. - */ -v8::Handle ctx2d_imageData_width(v8::Local, const v8::AccessorInfo &args) -{ - QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); - if (!r) - return v8::Integer::New(0); - return v8::Integer::New(r->image.width()); -} - -/*! - \qmlproperty QtQuick2::CanvasImageData::height - Holds the actual height dimension of the data in the ImageData object, in device pixels. - */ -v8::Handle ctx2d_imageData_height(v8::Local, const v8::AccessorInfo &args) -{ - QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); - if (!r) - return v8::Integer::New(0); - - return v8::Integer::New(r->image.height()); -} - -/*! - \qmlproperty QtQuick2::CanvasImageData::data - Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255. - */ -v8::Handle ctx2d_imageData_data(v8::Local, const v8::AccessorInfo &args) -{ - return args.This()->GetInternalField(0); -} - -/*! - \qmlmethod void QtQuick2::CanvasImageData::mirrr( bool horizontal = false, bool vertical = true) - Mirrors the image data in place in the \c horizontal and/or the \c vertical direction depending on - whether horizontal and vertical are set to true or false. - The default \c horizontal value is false, the default \c vertical value is true. -*/ -static v8::Handle ctx2d_imageData_mirror(const v8::Arguments &args) -{ - bool horizontal = false, vertical = true; - QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); - - if (!r) { - //error - return v8::Undefined(); - } - - if (args.Length() > 2) { - //error - return v8::Undefined(); - } - - if (args.Length() == 1) { - horizontal = args[0]->BooleanValue(); - } else if (args.Length() == 2) { - horizontal = args[0]->BooleanValue(); - vertical = args[1]->BooleanValue(); - } - r->image = r->image.mirrored(horizontal, vertical); - return args.This(); -} - -/*! - \qmlmethod void QtQuick2::CanvasImageData::filter(enumeration mode, args) - Filters the image data as defined by one of the following modes: - \list - \o Canvas.Threshold - converts the image to black and white pixels depending - if they are above or below the threshold defined by the level parameter. - The level must be between 0.0 (black) and 1.0(white). - If no level is specified, 0.5 is used. - \o Canvas.Mono - converts the image to the 1-bit per pixel format. - \o Canvas.GrayScale - converts any colors in the image to grayscale equivalents. - \o Canvas.Brightness -increase/decrease a fixed \c adjustment value to each pixel's RGB channel value. - \o Canvas.Invert - sets each pixel to its inverse value. - \o Canvas.Blur - executes a box blur with the pixel \c radius parameter specifying the range of the blurring for each pixel. - the default blur \c radius is 3. This filter also accepts another \c quality parameter, if true, the filter will - execute 3-passes box blur to simulate the Guassian blur. The default \c quality value is false. - \o Canvas.Opaque - sets the alpha channel to entirely opaque. - \o Canvas.Convolute - executes a generic {http://en.wikipedia.org/wiki/Convolution}{Convolution} filter, the second - parameter contains the convoluton matrix data as a number array. - \endlist - -*/ -static v8::Handle ctx2d_imageData_filter(const v8::Arguments &args) -{ - QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()->GetInternalField(0)->ToObject()); - - if (!r) { - //error - return v8::Undefined(); - } - - if (args.Length() >= 1) { - int filterFlag = args[0]->IntegerValue(); - switch (filterFlag) { - case QSGCanvasItem::Mono : - { - r->image = r->image.convertToFormat(QImage::Format_Mono).convertToFormat(QImage::Format_ARGB32_Premultiplied); - } - break; - case QSGCanvasItem::GrayScale : - { - for (int y = 0; y < r->image.height(); ++y) { - QRgb *row = (QRgb*)r->image.scanLine(y); - for (int x = 0; x < r->image.width(); ++x) { - unsigned char* rgb = ((unsigned char*)&row[x]); - rgb[0] = rgb[1] = rgb[2] = qGray(rgb[0], rgb[1], rgb[2]); - } - } - } - break; - case QSGCanvasItem::Threshold : - { - qreal threshold = 0.5; - if (args.Length() > 1) - threshold = args[1]->NumberValue(); - - for (int y = 0; y < r->image.height(); ++y) { - QRgb *row = (QRgb*)r->image.scanLine(y); - for (int x = 0; x < r->image.width(); ++x) { - unsigned char* rgb = ((unsigned char*)&row[x]); - unsigned char v = qGray(rgb[0], rgb[1], rgb[2]) >= threshold*255 ? 255 : 0; - rgb[0] = rgb[1] = rgb[2] = v; - } - } - } - break; - case QSGCanvasItem::Brightness : - { - int adjustment = 1; - if (args.Length() > 1) - adjustment = args[1]->IntegerValue(); - - for (int y = 0; y < r->image.height(); ++y) { - QRgb *row = (QRgb*)r->image.scanLine(y); - for (int x = 0; x < r->image.width(); ++x) { - ((unsigned char*)&row[x])[0] += adjustment; - ((unsigned char*)&row[x])[1] += adjustment; - ((unsigned char*)&row[x])[2] += adjustment; - } - } - } - break; - case QSGCanvasItem::Invert : - { - r->image.invertPixels(); - } - break; - case QSGCanvasItem::Blur : - { - int radius = 3; - bool quality = false; - - if (args.Length() > 1) - radius = args[1]->IntegerValue() / 2; - if (args.Length() > 2) - quality = args[2]->BooleanValue(); - - qt_image_boxblur(r->image, radius, quality); - } - break; - case QSGCanvasItem::Opaque : - { - for (int y = 0; y < r->image.height(); ++y) { - QRgb *row = (QRgb*)r->image.scanLine(y); - for (int x = 0; x < r->image.width(); ++x) { - ((unsigned char*)&row[x])[3] = 255; - } - } - } - break; - case QSGCanvasItem::Convolute : - { - if (args.Length() > 1 && args[1]->IsArray()) { - v8::Local array = v8::Local::Cast(args[1]); - QVector weights; - for (uint32_t i = 0; i < array->Length(); ++i) - weights.append(array->Get(i)->NumberValue()); - r->image = qt_image_convolute_filter(r->image, weights); - } else { - //error - } - } - break; - default: - break; - } - } - - return args.This(); -} -/*! - \qmlclass QtQuick2::CanvasPixelArray - The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data. - The CanvasPixelArray can be accessed as normal Javascript array. - \sa QtQuick2::CanvasImageData - \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray} - */ - -/*! - \qmlproperty QtQuick2::CanvasPixelArray::length - The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData. - The length attribute of a CanvasPixelArray object must return this h×w×4 number value. - This property is read only. -*/ -v8::Handle ctx2d_pixelArray_length(v8::Local, const v8::AccessorInfo &args) -{ - QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()); - if (!r || r->image.isNull()) return v8::Undefined(); - - return v8::Integer::New(r->image.width() * r->image.height() * 4); -} - -v8::Handle ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args) -{ - QV8Context2DPixelArrayResource *r = v8_resource_cast(args.This()); - - if (r && index < static_cast(r->image.width() * r->image.height() * 4)) { - const quint32 w = r->image.width(); - const quint32 row = (index / 4) / w; - const quint32 col = (index / 4) % w; - const QRgb* pixel = reinterpret_cast(r->image.constScanLine(row)); - pixel += col; - switch (index % 4) { - case 0: - return v8::Integer::New(qRed(*pixel)); - case 1: - return v8::Integer::New(qGreen(*pixel)); - case 2: - return v8::Integer::New(qBlue(*pixel)); - case 3: - return v8::Integer::New(qAlpha(*pixel)); - } - } - return v8::Undefined(); -} - -v8::Handle ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local value, const v8::AccessorInfo& info) -{ - QV8Context2DPixelArrayResource *r = v8_resource_cast(info.This()); - - const int v = value->Uint32Value(); - if (r && index < static_cast(r->image.width() * r->image.height() * 4) && v > 0 && v <= 255) { - const quint32 w = r->image.width(); - const quint32 row = (index / 4) / w; - const quint32 col = (index / 4) % w; - - QRgb* pixel = reinterpret_cast(r->image.scanLine(row)); - pixel += col; - switch (index % 4) { - case 0: - *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel)); - break; - case 1: - *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel)); - break; - case 2: - *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel)); - break; - case 3: - *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v); - break; - } - } - return v8::Undefined(); -} -/*! - \qmlmethod QtQuick2::CanvasImageData createImageData(real sw, real sh) - Creates a CanvasImageData object with the given dimensions(\a sw, \a sh). - */ -/*! - \qmlmethod QtQuick2::CanvasImageData createImageData(QtQuick2::CanvasImageData imageData) - Creates a CanvasImageData object with the same dimensions as the argument. - */ -/*! - \qmlmethod QtQuick2::CanvasImageData createImageData(Url imageUrl) - Creates a CanvasImageData object with the given image loaded from \a imageUrl. - Note:The \a imageUrl must be already loaded before this function call, if not, an empty - CanvasImageData obect will be returned. - - \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::isImageLoaded - */ -static v8::Handle ctx2d_createImageData(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE(); - - if (args.Length() == 1) { - if (args[0]->IsObject()) { - v8::Local imgData = args[0]->ToObject(); - QV8Context2DPixelArrayResource *pa = v8_resource_cast(imgData->GetInternalField(0)->ToObject()); - if (pa) { - qreal w = imgData->Get(v8::String::New("width"))->NumberValue(); - qreal h = imgData->Get(v8::String::New("height"))->NumberValue(); - return qt_create_image_data(w, h, engine, QImage()); - } - } else if (args[0]->IsString()) { - QImage image = r->context->createImage(QUrl(engine->toString(args[0]->ToString()))); - return qt_create_image_data(image.width(), image.height(), engine, image); - } - } else if (args.Length() == 2) { - qreal w = args[0]->NumberValue(); - qreal h = args[1]->NumberValue(); - - if (!qIsFinite(w) || !qIsFinite(h)) - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments"); - - if (w > 0 && h > 0) - return qt_create_image_data(w, h, engine, QImage()); - else - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments"); - } - return v8::Undefined(); -} - -/*! - \qmlmethod QtQuick2::CanvasImageData getImageData(real sx, real sy, real sw, real sh) - Returns an CanvasImageData object containing the image data for the given rectangle of the canvas. - */ -static v8::Handle ctx2d_getImageData(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - - QV8Engine *engine = V8ENGINE(); - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(w)) - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments"); - - if (w <= 0 || h <= 0) - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments"); - - QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h)); - v8::Local imageData = qt_create_image_data(w, h, engine, image); - - return imageData; - } - return v8::Null(); -} - -/*! - \qmlmethod object QtQuick2::Context2D::putImageData(QtQuick2::CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight) - Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted. - */ -static v8::Handle ctx2d_putImageData(const v8::Arguments &args) -{ - QV8Context2DResource *r = v8_resource_cast(args.This()); - CHECK_CONTEXT(r) - if (args.Length() != 3 && args.Length() != 7) - return v8::Undefined(); - - if (args[0]->IsNull() || !args[0]->IsObject()) { - V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch"); - } - qreal dx = args[1]->NumberValue(); - qreal dy = args[2]->NumberValue(); - qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight; - - if (!qIsFinite(dx) || !qIsFinite(dy)) - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments"); - - v8::Local imageData = args[0]->ToObject(); - QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast(imageData->Get(v8::String::New("data"))->ToObject()); - if (pixelArray) { - w = imageData->Get(v8::String::New("width"))->NumberValue(); - h = imageData->Get(v8::String::New("height"))->NumberValue(); - - if (args.Length() == 7) { - dirtyX = args[3]->NumberValue(); - dirtyY = args[4]->NumberValue(); - dirtyWidth = args[5]->NumberValue(); - dirtyHeight = args[6]->NumberValue(); - - if (!qIsFinite(dirtyX) || !qIsFinite(dirtyY) || !qIsFinite(dirtyWidth) || !qIsFinite(dirtyHeight)) - V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments"); - - - if (dirtyWidth < 0) { - dirtyX = dirtyX+dirtyWidth; - dirtyWidth = -dirtyWidth; - } - - if (dirtyHeight < 0) { - dirtyY = dirtyY+dirtyHeight; - dirtyHeight = -dirtyHeight; - } - - if (dirtyX < 0) { - dirtyWidth = dirtyWidth+dirtyX; - dirtyX = 0; - } - - if (dirtyY < 0) { - dirtyHeight = dirtyHeight+dirtyY; - dirtyY = 0; - } - - if (dirtyX+dirtyWidth > w) { - dirtyWidth = w - dirtyX; - } - - if (dirtyY+dirtyHeight > h) { - dirtyHeight = h - dirtyY; - } - - if (dirtyWidth <=0 || dirtyHeight <= 0) - return args.This(); - } else { - dirtyX = 0; - dirtyY = 0; - dirtyWidth = w; - dirtyHeight = h; - } - - QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight); - r->context->buffer()->drawImage(image, dirtyX, dirtyY, dirtyWidth, dirtyHeight, dx, dy, dirtyWidth, dirtyHeight); - } - return args.This(); -} - -/*! - \qmlclass QtQuick2::CanvasGradient - \inqmlmodule QtQuick 2 - \since QtQuick 2.0 - \brief The Context2D opaque CanvasGradient interface. - */ - -/*! - \qmlmethod QtQuick2::CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color) - Adds a color stop with the given color to the gradient at the given offset. - 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end. - - For example: - \code - var gradient = ctx.createLinearGradient(0, 0, 100, 100); - gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1)); - gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1'); - \endcode - */ -static v8::Handle ctx2d_gradient_addColorStop(const v8::Arguments &args) -{ - QV8Context2DStyleResource *style = v8_resource_cast(args.This()); - if (!style) - V8THROW_ERROR("Not a CanvasGradient object"); - - QV8Engine *engine = V8ENGINE(); - - if (args.Length() == 2) { - - if (!style->brush.gradient()) - V8THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information"); - QGradient gradient = *(style->brush.gradient()); - qreal pos = args[0]->NumberValue(); - QColor color; - - if (args[1]->IsObject()) { - color = engine->toVariant(args[1], qMetaTypeId()).value(); - } else { - color = qt_color_from_string(args[1]); - } - if (pos < 0.0 || pos > 1.0 || !qIsFinite(pos)) { - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range"); - } - - if (color.isValid()) { - gradient.setColorAt(pos, color); - } else { - V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string"); - } - style->brush = gradient; - } - - return args.This(); -} - - -void QSGContext2D::beginPath() -{ - m_path = QPainterPath(); - m_path.setFillRule(state.fillRule); -} - -void QSGContext2D::closePath() -{ - if (m_path.isEmpty()) - return; - - QRectF boundRect = m_path.boundingRect(); - if (boundRect.width() || boundRect.height()) - m_path.closeSubpath(); - //FIXME:QPainterPath set the current point to (0,0) after close subpath - //should be the first point of the previous subpath -} - -void QSGContext2D::moveTo( qreal x, qreal y) -{ - //FIXME: moveTo should not close the previous subpath - m_path.moveTo(state.matrix.map(QPointF(x, y))); -} - -void QSGContext2D::lineTo( qreal x, qreal y) -{ - m_path.lineTo(state.matrix.map(QPointF(x, y))); -} - -void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy, - qreal x, qreal y) -{ - m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)), - state.matrix.map(QPointF(x, y))); -} - -void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y, - qreal cp2x, qreal cp2y, - qreal x, qreal y) -{ - m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)), - state.matrix.map(QPointF(cp2x, cp2y)), - state.matrix.map(QPointF(x, y))); -} - -void QSGContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius) -{ - QPointF p0(m_path.currentPosition()); - - QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y())); - QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y())); - float p1p0_length = qSqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); - float p1p2_length = qSqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); - - double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); - - // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8) - // We could have used areCollinear() here, but since we're reusing - // the variables computed above later on we keep this logic. - if (qFuzzyCompare(qAbs(cos_phi), 1.0)) { - m_path.lineTo(p1); - return; - } - - float tangent = radius / tan(acos(cos_phi) / 2); - float factor_p1p0 = tangent / p1p0_length; - QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); - - QPointF orth_p1p0(p1p0.y(), -p1p0.x()); - float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); - float factor_ra = radius / orth_p1p0_length; - - // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 - double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); - if (cos_alpha < 0.f) - orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y()); - - QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); - - // calculate angles for addArc - orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y()); - float sa = acos(orth_p1p0.x() / orth_p1p0_length); - if (orth_p1p0.y() < 0.f) - sa = 2 * Q_PI - sa; - - // anticlockwise logic - bool anticlockwise = false; - - float factor_p1p2 = tangent / p1p2_length; - QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); - QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y())); - float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); - float ea = acos(orth_p1p2.x() / orth_p1p2_length); - if (orth_p1p2.y() < 0) - ea = 2 * Q_PI - ea; - if ((sa > ea) && ((sa - ea) < Q_PI)) - anticlockwise = true; - if ((sa < ea) && ((ea - sa) > Q_PI)) - anticlockwise = true; - - arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false); -} - -void QSGContext2D::arcTo(qreal x1, qreal y1, - qreal x2, qreal y2, - qreal radius) -{ - QPointF st = state.matrix.map(QPointF(x1, y1)); - QPointF end = state.matrix.map(QPointF(x2, y2)); - - if (!m_path.elementCount()) { - m_path.moveTo(st); - } else if (st == m_path.currentPosition() || st == end || !radius) { - m_path.lineTo(st); - } else { - addArcTo(st, end, radius); - } -} - -void QSGContext2D::rect(qreal x, qreal y, - qreal w, qreal h) -{ - m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h))); -} - -void QSGContext2D::roundedRect(qreal x, qreal y, - qreal w, qreal h, - qreal xr, qreal yr) -{ - QPainterPath path; - path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize); - m_path.addPath(state.matrix.map(path)); -} - -void QSGContext2D::ellipse(qreal x, qreal y, - qreal w, qreal h) -{ - QPainterPath path; - path.addEllipse(x, y, w, h); - m_path.addPath(state.matrix.map(path)); -} - -void QSGContext2D::text(const QString& str, qreal x, qreal y) -{ - QPainterPath path; - path.addText(x, y, state.font, str); - m_path.addPath(state.matrix.map(path)); -} - -void QSGContext2D::arc(qreal xc, - qreal yc, - qreal radius, - qreal sar, - qreal ear, - bool antiClockWise, - bool transform) -{ - - if (transform) { - QPointF point = state.matrix.map(QPointF(xc, yc)); - xc = point.x(); - yc = point.y(); - } - //### HACK - - // In Qt we don't switch the coordinate system for degrees - // and still use the 0,0 as bottom left for degrees so we need - // to switch - sar = -sar; - ear = -ear; - antiClockWise = !antiClockWise; - //end hack - - float sa = DEGREES(sar); - float ea = DEGREES(ear); - - double span = 0; - - double xs = xc - radius; - double ys = yc - radius; - double width = radius*2; - double height = radius*2; - if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360))) - // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the - // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole - // circumference of this circle. - span = 360; - else { - if (!antiClockWise && (ea < sa)) { - span += 360; - } else if (antiClockWise && (sa < ea)) { - span -= 360; - } - //### this is also due to switched coordinate system - // we would end up with a 0 span instead of 360 - if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) && - qFuzzyCompare(qAbs(span), 360))) { - span += ea - sa; - } - if (!m_path.elementCount()) - m_path.moveTo(xs, ys); - } - - - if (transform) { - QPointF currentPos = m_path.currentPosition(); - QPointF startPos = QPointF(xc + radius * qCos(sar), - yc - radius * qSin(sar)); - if (currentPos != startPos) - m_path.lineTo(startPos); - } - - m_path.arcTo(xs, ys, width, height, sa, span); -} - -int baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics) -{ - int offset = 0; - switch (value) { - case QSGContext2D::Top: - break; - case QSGContext2D::Alphabetic: - case QSGContext2D::Middle: - case QSGContext2D::Hanging: - offset = metrics.ascent(); - break; - case QSGContext2D::Bottom: - offset = metrics.height(); - break; - } - return offset; -} - -static int textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text) -{ - int offset = 0; - if (value == QSGContext2D::Start) - value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right; - else if (value == QSGContext2D::End) - value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left; - switch (value) { - case QSGContext2D::Center: - offset = metrics.width(text)/2; - break; - case QSGContext2D::Right: - offset = metrics.width(text); - case QSGContext2D::Left: - default: - break; - } - return offset; -} - - -QImage QSGContext2D::createImage(const QUrl& url) -{ - return m_canvas->loadedImage(url); -} - -QPainterPath QSGContext2D::createTextGlyphs(qreal x, qreal y, const QString& text) -{ - const QFontMetrics metrics(state.font); - int yoffset = baseLineOffset(static_cast(state.textBaseline), metrics); - int xoffset = textAlignOffset(static_cast(state.textAlign), metrics, text); - - QPainterPath textPath; - - textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text); - textPath = state.matrix.map(textPath); - return textPath; -} - - -bool QSGContext2D::isPointInPath(qreal x, qreal y) const -{ - return m_path.contains(QPointF(x, y)); -} - -QSGContext2D::QSGContext2D(QSGCanvasItem* item) - : m_canvas(item) - , m_buffer(new QSGContext2DCommandBuffer) - , m_v8engine(0) -{ - reset(); -} - -QSGContext2D::~QSGContext2D() -{ -} - -v8::Handle QSGContext2D::v8value() const -{ - return m_v8value; -} - -QSGContext2DEngineData::QSGContext2DEngineData(QV8Engine *engine) -{ - v8::HandleScope handle_scope; - v8::Context::Scope scope(engine->context()); - - v8::Local ft = v8::FunctionTemplate::New(); - ft->InstanceTemplate()->SetHasExternalResource(true); - ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine)); - ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine)); - ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine)); - ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("roundedRect"), V8FUNCTION(ctx2d_roundedRect, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine)); - ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine)); - ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine)); - - constructorContext = qPersistentNew(ft->GetFunction()); - - v8::Local ftGradient = v8::FunctionTemplate::New(); - ftGradient->InstanceTemplate()->SetHasExternalResource(true); - ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine)); - constructorGradient = qPersistentNew(ftGradient->GetFunction()); - - v8::Local ftPattern = v8::FunctionTemplate::New(); - ftPattern->InstanceTemplate()->SetHasExternalResource(true); - constructorPattern = qPersistentNew(ftPattern->GetFunction()); - - v8::Local ftPixelArray = v8::FunctionTemplate::New(); - ftPixelArray->InstanceTemplate()->SetHasExternalResource(true); - ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine)); - ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine)); - constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction()); - - v8::Local ftImageData = v8::FunctionTemplate::New(); - ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine)); - ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine)); - ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine)); - ftImageData->PrototypeTemplate()->Set(v8::String::New("mirror"), V8FUNCTION(ctx2d_imageData_mirror, engine)); - ftImageData->PrototypeTemplate()->Set(v8::String::New("filter"), V8FUNCTION(ctx2d_imageData_filter, engine)); - ftImageData->InstanceTemplate()->SetInternalFieldCount(1); - constructorImageData = qPersistentNew(ftImageData->GetFunction()); -} - -QSGContext2DEngineData::~QSGContext2DEngineData() -{ - qPersistentDispose(constructorContext); - qPersistentDispose(constructorGradient); - qPersistentDispose(constructorPattern); - qPersistentDispose(constructorImageData); - qPersistentDispose(constructorPixelArray); -} - -void QSGContext2D::popState() -{ - if (m_stateStack.isEmpty()) - return; - - QSGContext2D::State newState = m_stateStack.pop(); - - if (state.matrix != newState.matrix) - buffer()->updateMatrix(newState.matrix); - - if (newState.globalAlpha != state.globalAlpha) - buffer()->setGlobalAlpha(newState.globalAlpha); - - if (newState.globalCompositeOperation != state.globalCompositeOperation) - buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation); - - if (newState.fillStyle != state.fillStyle) - buffer()->setFillStyle(newState.fillStyle); - - if (newState.strokeStyle != state.strokeStyle) - buffer()->setStrokeStyle(newState.strokeStyle); - - if (newState.lineWidth != state.lineWidth) - buffer()->setLineWidth(newState.lineWidth); - - if (newState.lineCap != state.lineCap) - buffer()->setLineCap(newState.lineCap); - - if (newState.lineJoin != state.lineJoin) - buffer()->setLineJoin(newState.lineJoin); - - if (newState.miterLimit != state.miterLimit) - buffer()->setMiterLimit(newState.miterLimit); - - if (newState.clipPath != state.clipPath) { - buffer()->clip(newState.clipPath); - } - - if (newState.shadowBlur != state.shadowBlur) - buffer()->setShadowBlur(newState.shadowBlur); - - if (newState.shadowColor != state.shadowColor) - buffer()->setShadowColor(newState.shadowColor); - - if (newState.shadowOffsetX != state.shadowOffsetX) - buffer()->setShadowOffsetX(newState.shadowOffsetX); - - if (newState.shadowOffsetY != state.shadowOffsetY) - buffer()->setShadowOffsetY(newState.shadowOffsetY); - state = newState; -} -void QSGContext2D::pushState() -{ - m_stateStack.push(state); -} - -void QSGContext2D::reset() -{ - QSGContext2D::State newState; - newState.matrix = QTransform(); - - QPainterPath defaultClipPath; - - QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height()); - r = r.united(m_canvas->canvasWindow().toRect()); - defaultClipPath.addRect(r); - newState.clipPath = defaultClipPath; - newState.clipPath.setFillRule(Qt::WindingFill); - - newState.strokeStyle = QColor("#000000"); - newState.fillStyle = QColor("#000000"); - newState.fillPatternRepeatX = false; - newState.fillPatternRepeatY = false; - newState.strokePatternRepeatX = false; - newState.strokePatternRepeatY = false; - newState.fillRule = Qt::WindingFill; - newState.globalAlpha = 1.0; - newState.lineWidth = 1; - newState.lineCap = Qt::FlatCap; - newState.lineJoin = Qt::MiterJoin; - newState.miterLimit = 10; - newState.shadowOffsetX = 0; - newState.shadowOffsetY = 0; - newState.shadowBlur = 0; - newState.shadowColor = qRgba(0, 0, 0, 0); - newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver; - newState.font = QFont(QLatin1String("sans-serif"), 10); - newState.textAlign = QSGContext2D::Start; - newState.textBaseline = QSGContext2D::Alphabetic; - - m_stateStack.clear(); - m_stateStack.push(newState); - popState(); - m_buffer->clearRect(0, 0, m_canvas->width(), m_canvas->height()); -} - -void QSGContext2D::setV8Engine(QV8Engine *engine) -{ - v8::HandleScope handle_scope; - v8::Context::Scope scope(engine->context()); - - if (m_v8engine != engine) { - m_v8engine = engine; - - qPersistentDispose(m_v8value); - - if (m_v8engine == 0) - return; - - QSGContext2DEngineData *ed = engineData(engine); - m_v8value = qPersistentNew(ed->constructorContext->NewInstance()); - QV8Context2DResource *r = new QV8Context2DResource(engine); - r->context = this; - m_v8value->SetExternalResource(r); - } -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/context2d/qsgcontext2d_p.h b/src/declarative/items/context2d/qsgcontext2d_p.h deleted file mode 100644 index 10c1e331f9..0000000000 --- a/src/declarative/items/context2d/qsgcontext2d_p.h +++ /dev/null @@ -1,187 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCONTEXT2D_P_H -#define QSGCONTEXT2D_P_H - -#include -#include - -#include -#include -#include -#include -#include - - - -#define QSGCONTEXT2D_DEBUG //enable this for just DEBUG purpose! - -#ifdef QSGCONTEXT2D_DEBUG -#include -#endif - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGCanvasItem; -class QSGContext2DCommandBuffer; -class QDeclarativePixmap; - -class Q_DECLARATIVE_EXPORT QSGContext2D -{ -public: - enum TextBaseLineType { Alphabetic=0, Top, Middle, Bottom, Hanging}; - enum TextAlignType { Start=0, End, Left, Right, Center}; - enum PaintCommand { - Invalid = 0, - UpdateMatrix, - ClearRect, - FillRect, - StrokeRect, - Fill, - Stroke, - Clip, - UpdateBrush, - GlobalAlpha, - GlobalCompositeOperation, - StrokeStyle, - FillStyle, - LineWidth, - LineCap, - LineJoin, - MiterLimit, - ShadowOffsetX, - ShadowOffsetY, - ShadowBlur, - ShadowColor, - Font, - TextBaseline, - TextAlign, - FillText, - StrokeText, - DrawImage, - GetImageData - }; - - - struct State { - QTransform matrix; - QPainterPath clipPath; - QBrush strokeStyle; - QBrush fillStyle; - bool fillPatternRepeatX:1; - bool fillPatternRepeatY:1; - bool strokePatternRepeatX:1; - bool strokePatternRepeatY:1; - Qt::FillRule fillRule; - qreal globalAlpha; - qreal lineWidth; - Qt::PenCapStyle lineCap; - Qt::PenJoinStyle lineJoin; - qreal miterLimit; - qreal shadowOffsetX; - qreal shadowOffsetY; - qreal shadowBlur; - QColor shadowColor; - QPainter::CompositionMode globalCompositeOperation; - QFont font; - QSGContext2D::TextAlignType textAlign; - QSGContext2D::TextBaseLineType textBaseline; - }; - - QSGContext2D(QSGCanvasItem* item); - ~QSGContext2D(); - - inline QSGCanvasItem* canvas() const {return m_canvas;} - inline QSGContext2DCommandBuffer* buffer() const {return m_buffer;} - - v8::Handle v8value() const; - void setV8Engine(QV8Engine *eng); - void popState(); - void pushState(); - void reset(); - - // path API - void beginPath(); - void closePath(); - void moveTo(qreal x, qreal y); - void lineTo(qreal x, qreal y); - void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y); - void bezierCurveTo(qreal cp1x, qreal cp1y, - qreal cp2x, qreal cp2y, qreal x, qreal y); - void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius); - void rect(qreal x, qreal y, qreal w, qreal h); - void roundedRect(qreal x, qreal y,qreal w, qreal h, qreal xr, qreal yr); - void ellipse(qreal x, qreal y,qreal w, qreal h); - void text(const QString& str, qreal x, qreal y); - void arc(qreal x, qreal y, qreal radius, - qreal startAngle, qreal endAngle, - bool anticlockwise, bool transform=true); - void addArcTo(const QPointF& p1, const QPointF& p2, float radius); - - bool isPointInPath(qreal x, qreal y) const; - - QPainterPath createTextGlyphs(qreal x, qreal y, const QString& text); - QImage createImage(const QUrl& url); - - State state; - QStack m_stateStack; - QSGCanvasItem* m_canvas; - QSGContext2DCommandBuffer* m_buffer; - QPainterPath m_path; - v8::Local m_fillStyle; - v8::Local m_strokeStyle; - v8::Handle m_v8path; - QV8Engine *m_v8engine; - v8::Persistent m_v8value; -}; - - -QT_END_NAMESPACE -QML_DECLARE_TYPE(QSGContext2D) - -QT_END_HEADER - -#endif // QSGCONTEXT2D_P_H diff --git a/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp b/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp deleted file mode 100644 index 29ed5fbc12..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp +++ /dev/null @@ -1,470 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgcontext2dcommandbuffer_p.h" -#include "qsgcanvasitem_p.h" -#include -#include - -#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY)) - -QT_BEGIN_NAMESPACE - -void qt_image_boxblur(QImage& image, int radius, bool quality); - -static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) -{ - QImage shadowImg(image.width() + blur + qAbs(offsetX), - image.height() + blur + qAbs(offsetY), - QImage::Format_ARGB32_Premultiplied); - shadowImg.fill(0); - QPainter tmpPainter(&shadowImg); - tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); - qreal shadowX = offsetX > 0? offsetX : 0; - qreal shadowY = offsetY > 0? offsetY : 0; - - tmpPainter.drawImage(shadowX, shadowY, image); - tmpPainter.end(); - - if (blur > 0) - qt_image_boxblur(shadowImg, blur/2, true); - - // blacken the image with shadow color... - tmpPainter.begin(&shadowImg); - tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); - tmpPainter.fillRect(shadowImg.rect(), color); - tmpPainter.end(); - return shadowImg; -} - -static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) -{ - QRectF r = shadowRect; - r.moveTo(0, 0); - - QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied); - QPainter tp; - tp.begin(&shadowImage); - tp.fillRect(r, p->brush()); - tp.end(); - shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color); - - qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0); - qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0); - - p->drawImage(dx, dy, shadowImage); - p->fillRect(shadowRect, p->brush()); -} - -static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) -{ - QRectF r = path.boundingRect(); - QImage img(r.size().width() + r.left() + 1, - r.size().height() + r.top() + 1, - QImage::Format_ARGB32_Premultiplied); - img.fill(0); - QPainter tp(&img); - tp.fillPath(path.translated(0, 0), p->brush()); - tp.end(); - - QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); - qreal dx = r.left() + (offsetX < 0? offsetX:0); - qreal dy = r.top() + (offsetY < 0? offsetY:0); - - p->drawImage(dx, dy, shadowImage); - p->fillPath(path, p->brush()); -} - -static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color) -{ - QRectF r = path.boundingRect(); - QImage img(r.size().width() + r.left() + 1, - r.size().height() + r.top() + 1, - QImage::Format_ARGB32_Premultiplied); - img.fill(0); - QPainter tp(&img); - tp.strokePath(path, p->pen()); - tp.end(); - - QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color); - qreal dx = r.left() + (offsetX < 0? offsetX:0); - qreal dy = r.top() + (offsetY < 0? offsetY:0); - p->drawImage(dx, dy, shadowImage); - p->strokePath(path, p->pen()); -} -static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY) -{ - // Patterns must be painted so that the top left of the first image is anchored at - // the origin of the coordinate space - if (!image.isNull()) { - int w = image.width(); - int h = image.height(); - int startX, startY; - QRect r(static_cast(rect.x()), static_cast(rect.y()), static_cast(rect.width()), static_cast(rect.height())); - - // startX, startY is the coordinate of the first image we need to put on the left-top of the rect - if (repeatX && repeatY) { - // repeat - // startX, startY is at the left top side of the left-top of the rect - startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); - startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); - } else { - if (!repeatX && !repeatY) { - // no-repeat - // only draw the image once at orgin once, check if need to draw - QRect imageRect(0, 0, w, h); - if (imageRect.intersects(r)) { - startX = 0; - startY = 0; - } else - return; - } else if (repeatX && !repeatY) { - // repeat-x - // startY is fixed, but startX change based on the left-top of the rect - QRect imageRect(r.x(), 0, r.width(), h); - if (imageRect.intersects(r)) { - startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w); - startY = 0; - } else - return; - } else { - // repeat-y - // startX is fixed, but startY change based on the left-top of the rect - QRect imageRect(0, r.y(), w, r.height()); - if (imageRect.intersects(r)) { - startX = 0; - startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h); - } else - return; - } - } - - int x = startX; - int y = startY; - do { - // repeat Y - do { - // repeat X - QRect imageRect(x, y, w, h); - QRect intersectRect = imageRect.intersected(r); - QPoint destStart(intersectRect.x(), intersectRect.y()); - QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height()); - - p->drawImage(destStart, image, sourceRect); - x += w; - } while (repeatX && x < r.x() + r.width()); - x = startX; - y += h; - } while (repeatY && y < r.y() + r.height()); - } -} - -QPen QSGContext2DCommandBuffer::makePen(QSGContext2D::State state) -{ - QPen pen; - pen.setWidthF(state.lineWidth); - pen.setCapStyle(state.lineCap); - pen.setJoinStyle(state.lineJoin); - pen.setMiterLimit(state.miterLimit); - pen.setBrush(state.strokeStyle); - return pen; -} - -void QSGContext2DCommandBuffer::setPainterState(QPainter* p, QSGContext2D::State state, const QPen& pen) -{ - p->setTransform(state.matrix * p->transform()); - - if (pen != p->pen()) - p->setPen(pen); - - if (state.fillStyle != p->brush()) - p->setBrush(state.fillStyle); - - if (state.font != p->font()) - p->setFont(state.font); - - if (state.globalAlpha != p->opacity()) { - p->setOpacity(state.globalAlpha); - } - - if (state.globalCompositeOperation != p->compositionMode()) - p->setCompositionMode(state.globalCompositeOperation); -} - -QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D::State state) -{ - if (!p) - return state; - - reset(); - - QTransform originMatrix = p->transform(); - - QPen pen = makePen(state); - setPainterState(p, state, pen); - - while (hasNext()) { - QSGContext2D::PaintCommand cmd = takeNextCommand(); - switch (cmd) { - case QSGContext2D::UpdateMatrix: - { - state.matrix = takeMatrix(); - p->setTransform(state.matrix * originMatrix); - break; - } - case QSGContext2D::ClearRect: - { - QPainter::CompositionMode cm = p->compositionMode(); - qreal alpha = p->opacity(); - p->setCompositionMode(QPainter::CompositionMode_Source); - p->setOpacity(0); - p->fillRect(takeRect(), QColor(qRgba(0, 0, 0, 0))); - p->setCompositionMode(cm); - p->setOpacity(alpha); - break; - } - case QSGContext2D::FillRect: - { - QRectF r = takeRect(); - if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) - fillRectShadow(p, r, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); - else - p->fillRect(r, p->brush()); - break; - } - case QSGContext2D::ShadowColor: - { - state.shadowColor = takeColor(); - break; - } - case QSGContext2D::ShadowBlur: - { - state.shadowBlur = takeShadowBlur(); - break; - } - case QSGContext2D::ShadowOffsetX: - { - state.shadowOffsetX = takeShadowOffsetX(); - break; - } - case QSGContext2D::ShadowOffsetY: - { - state.shadowOffsetY = takeShadowOffsetY(); - break; - } - case QSGContext2D::FillStyle: - { - state.fillStyle = takeFillStyle(); - state.fillPatternRepeatX = takeBool(); - state.fillPatternRepeatY = takeBool(); - p->setBrush(state.fillStyle); - break; - } - case QSGContext2D::StrokeStyle: - { - state.strokeStyle = takeStrokeStyle(); - state.strokePatternRepeatX = takeBool(); - state.strokePatternRepeatY = takeBool(); - pen.setBrush(state.strokeStyle); - p->setPen(pen); - break; - } - case QSGContext2D::LineWidth: - { - state.lineWidth = takeLineWidth(); - pen.setWidth(state.lineWidth); - p->setPen(pen); - break; - } - case QSGContext2D::LineCap: - { - state.lineCap = takeLineCap(); - pen.setCapStyle(state.lineCap); - p->setPen(pen); - break; - } - case QSGContext2D::LineJoin: - { - state.lineJoin = takeLineJoin(); - pen.setJoinStyle(state.lineJoin); - p->setPen(pen); - break; - } - case QSGContext2D::MiterLimit: - { - state.miterLimit = takeMiterLimit(); - pen.setMiterLimit(state.miterLimit); - p->setPen(pen); - break; - } - case QSGContext2D::TextAlign: - case QSGContext2D::TextBaseline: - break; - case QSGContext2D::Fill: - { - QPainterPath path = takePath(); - path.closeSubpath(); - if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) - fillShadowPath(p,path, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); - else - p->fillPath(path, p->brush()); - break; - } - case QSGContext2D::Stroke: - { - if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) - strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); - else - p->strokePath(takePath(), p->pen()); - break; - } - case QSGContext2D::Clip: - { - state.clipPath = takePath(); - p->setClipping(true); - p->setClipPath(state.clipPath); - break; - } - case QSGContext2D::GlobalAlpha: - { - state.globalAlpha = takeGlobalAlpha(); - p->setOpacity(state.globalAlpha); - break; - } - case QSGContext2D::GlobalCompositeOperation: - { - state.globalCompositeOperation = takeGlobalCompositeOperation(); - p->setCompositionMode(state.globalCompositeOperation); - break; - } - case QSGContext2D::DrawImage: - { - qreal sx = takeReal(); - qreal sy = takeReal(); - qreal sw = takeReal(); - qreal sh = takeReal(); - qreal dx = takeReal(); - qreal dy = takeReal(); - qreal dw = takeReal(); - qreal dh = takeReal(); - QImage image = takeImage(); - - if (!image.isNull()) { - if (sw == -1 || sh == -1) { - sw = image.width(); - sh = image.height(); - } - if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height()) - image = image.copy(sx, sy, sw, sh); - - image = image.scaled(dw, dh); - - if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) { - QImage shadow = makeShadowImage(image, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); - qreal shadow_dx = dx + (state.shadowOffsetX < 0? state.shadowOffsetY:0); - qreal shadow_dy = dy + (state.shadowOffsetX < 0? state.shadowOffsetY:0); - p->drawImage(shadow_dx, shadow_dy, shadow); - } - p->drawImage(dx, dy, image); - } - break; - } - case QSGContext2D::GetImageData: - { - //TODO: - break; - } - default: - break; - } - } - - p->end(); - return state; -} - -QSGContext2DCommandBuffer::QSGContext2DCommandBuffer() - : cmdIdx(0) - , intIdx(0) - , boolIdx(0) - , realIdx(0) - , colorIdx(0) - , matrixIdx(0) - , brushIdx(0) - , pathIdx(0) - , imageIdx(0) -{ -} - - -QSGContext2DCommandBuffer::~QSGContext2DCommandBuffer() -{ -} - -void QSGContext2DCommandBuffer::clear() -{ - commands.clear(); - ints.clear(); - bools.clear(); - reals.clear(); - colors.clear(); - matrixes.clear(); - brushes.clear(); - pathes.clear(); - images.clear(); - reset(); -} - -void QSGContext2DCommandBuffer::reset() -{ - cmdIdx = 0; - intIdx = 0; - boolIdx = 0; - realIdx = 0; - colorIdx = 0; - matrixIdx = 0; - brushIdx = 0; - pathIdx = 0; - imageIdx = 0; -} - -QT_END_NAMESPACE - diff --git a/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h b/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h deleted file mode 100644 index cab82e2f67..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h +++ /dev/null @@ -1,268 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCONTEXT2DCOMMANDBUFFER_P_H -#define QSGCONTEXT2DCOMMANDBUFFER_P_H - -#include "qsgcontext2d_p.h" -#include - - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGCanvasItem; -class QMutex; - -class QSGContext2DCommandBuffer -{ -public: - QSGContext2DCommandBuffer(); - ~QSGContext2DCommandBuffer(); - void reset(); - void clear(); - inline int size() {return commands.size();} - inline bool isEmpty() const {return commands.isEmpty(); } - inline bool hasNext() const {return cmdIdx < commands.size(); } - inline QSGContext2D::PaintCommand takeNextCommand() { return commands[cmdIdx++]; } - - inline qreal takeGlobalAlpha() { return takeReal(); } - inline QPainter::CompositionMode takeGlobalCompositeOperation(){ return static_cast(takeInt()); } - inline QBrush takeStrokeStyle() { return takeBrush(); } - inline QBrush takeFillStyle() { return takeBrush(); } - - inline qreal takeLineWidth() { return takeReal(); } - inline Qt::PenCapStyle takeLineCap() { return static_cast(takeInt());} - inline Qt::PenJoinStyle takeLineJoin(){ return static_cast(takeInt());} - inline qreal takeMiterLimit() { return takeReal(); } - - inline void setGlobalAlpha( qreal alpha) - { - commands << QSGContext2D::GlobalAlpha; - reals << alpha; - } - - inline void setGlobalCompositeOperation(QPainter::CompositionMode cm) - { - commands << QSGContext2D::GlobalCompositeOperation; - ints << cm; - } - - inline void setStrokeStyle(const QBrush &style, bool repeatX = false, bool repeatY = false) - { - commands << QSGContext2D::StrokeStyle; - brushes << style; - bools << repeatX << repeatY; - } - - inline void drawImage(const QImage& image, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh) - { - commands << QSGContext2D::DrawImage; - images << image; - reals << sx << sy << sw << sh << dx << dy << dw << dh; - } - - inline qreal takeShadowOffsetX() { return takeReal(); } - inline qreal takeShadowOffsetY() { return takeReal(); } - inline qreal takeShadowBlur() { return takeReal(); } - inline QColor takeShadowColor() { return takeColor(); } - - - inline void updateMatrix(const QTransform& matrix) - { - commands << QSGContext2D::UpdateMatrix; - matrixes << matrix; - } - - inline void clearRect(qreal x, qreal y, qreal w, qreal h) - { - commands << QSGContext2D::ClearRect; - reals << x << y << w << h; - } - - inline void fillRect(qreal x, qreal y, qreal w, qreal h) - { - commands << QSGContext2D::FillRect; - reals << x << y << w << h; - } - - inline void strokeRect(qreal x, qreal y, qreal w, qreal h) - { - QPainterPath p; - p.addRect(x, y, w, h); - - commands << QSGContext2D::Stroke; - pathes << p; - } - - - inline void fill(const QPainterPath& path) - { - commands << QSGContext2D::Fill; - pathes << path; - - } - - inline void stroke(const QPainterPath& path) - { - commands << QSGContext2D::Stroke; - pathes << path; - } - - inline void clip(const QPainterPath& path) - { - commands << QSGContext2D::Clip; - pathes << path; - } - - - - inline void setFillStyle(const QBrush &style, bool repeatX = false, bool repeatY = false) - { - commands << QSGContext2D::FillStyle; - brushes << style; - bools << repeatX << repeatY; - } - - - inline void setLineWidth( qreal w) - { - commands << QSGContext2D::LineWidth; - reals << w; - } - - inline void setLineCap(Qt::PenCapStyle cap) - { - commands << QSGContext2D::LineCap; - ints << cap; - } - - inline void setLineJoin(Qt::PenJoinStyle join) - { - commands << QSGContext2D::LineJoin; - ints << join; - } - - inline void setMiterLimit( qreal limit) - { - commands << QSGContext2D::MiterLimit; - reals << limit; - } - - inline void setShadowOffsetX( qreal x) - { - commands << QSGContext2D::ShadowOffsetX; - reals << x; - } - - inline void setShadowOffsetY( qreal y) - { - commands << QSGContext2D::ShadowOffsetY; - reals << y; - } - - inline void setShadowBlur( qreal b) - { - commands << QSGContext2D::ShadowBlur; - reals << b; - } - - inline void setShadowColor(const QColor &color) - { - commands << QSGContext2D::ShadowColor; - colors << color; - } - - inline QTransform takeMatrix() { return matrixes[matrixIdx++]; } - - // rects - inline QRectF takeRect() { - qreal x, y, w, h; - x = takeReal(); - y = takeReal(); - w = takeReal(); - h = takeReal(); - return QRectF(x, y, w ,h); - } - - inline QPainterPath takePath() { return pathes[pathIdx++]; } - - inline const QImage& takeImage() { return images[imageIdx++]; } - - inline int takeInt() { return ints[intIdx++]; } - inline bool takeBool() {return bools[boolIdx++]; } - inline qreal takeReal() { return reals[realIdx++]; } - inline QColor takeColor() { return colors[colorIdx++]; } - inline QBrush takeBrush() { return brushes[brushIdx++]; } - - QSGContext2D::State replay(QPainter* painter, QSGContext2D::State state); -private: - QPen makePen(QSGContext2D::State state); - void setPainterState(QPainter* painter, QSGContext2D::State state, const QPen& pen); - int cmdIdx; - int intIdx; - int boolIdx; - int realIdx; - int colorIdx; - int matrixIdx; - int brushIdx; - int pathIdx; - int imageIdx; - QVector commands; - - QVector ints; - QVector bools; - QVector reals; - QVector colors; - QVector matrixes; - QVector brushes; - QVector pathes; - QVector images; -}; - -QT_END_HEADER - -QT_END_NAMESPACE - -#endif // QSGCONTEXT2DCOMMANDBUFFER_P_H diff --git a/src/declarative/items/context2d/qsgcontext2dnode.cpp b/src/declarative/items/context2d/qsgcontext2dnode.cpp deleted file mode 100644 index 87de8cf157..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dnode.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgcontext2dnode_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - - -QSGContext2DNode::QSGContext2DNode(QSGCanvasItem* item) - : QSGGeometryNode() - , m_item(item) - , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) - , m_texture(0) - , m_size(1, 1) - , m_dirtyGeometry(false) - , m_dirtyTexture(false) -{ - setMaterial(&m_materialO); - setOpaqueMaterial(&m_material); - setGeometry(&m_geometry); - setFlag(UsePreprocess, true); -} - -QSGContext2DNode::~QSGContext2DNode() -{ - delete m_texture; -} - -void QSGContext2DNode::setSize(const QSizeF& size) -{ - if (m_size != size) { - m_dirtyGeometry = true; - m_size = size; - } -} - -void QSGContext2DNode::preprocess() -{ - bool doDirty = false; - QSGDynamicTexture *t = qobject_cast(m_material.texture()); - if (t) { - doDirty = t->updateTexture(); - } - if (doDirty) { - m_dirtyTexture = true; - markDirty(DirtyMaterial); - } -} -void QSGContext2DNode::setTexture(QSGContext2DTexture* texture) -{ - if (texture != m_texture) { - m_dirtyTexture = true; - m_texture = texture; - } -} - -void QSGContext2DNode::update() -{ - if (m_dirtyGeometry) - updateGeometry(); - if (m_dirtyTexture) - updateTexture(); - - m_dirtyGeometry = false; - m_dirtyTexture = false; -} - -void QSGContext2DNode::updateTexture() -{ - m_material.setTexture(m_texture); - m_materialO.setTexture(m_texture); - markDirty(DirtyMaterial); -} - -void QSGContext2DNode::updateGeometry() -{ - QRectF source = m_texture->textureSubRect(); - QSGGeometry::updateTexturedRectGeometry(&m_geometry, - QRectF(0, 0, m_size.width(), m_size.height()), - source); - markDirty(DirtyGeometry); -} -QT_END_NAMESPACE diff --git a/src/declarative/items/context2d/qsgcontext2dnode_p.h b/src/declarative/items/context2d/qsgcontext2dnode_p.h deleted file mode 100644 index 88c3619095..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dnode_p.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCONTEXT2DNODE_P_H -#define QSGCONTEXT2DNODE_P_H - -#include -#include - -#include "qsgcanvasitem_p.h" -#include "qsgcontext2dtexture_p.h" -#include "qsgcontext2d_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGContext2DNode : public QSGGeometryNode -{ -public: - QSGContext2DNode(QSGCanvasItem* item); - virtual ~QSGContext2DNode(); - void setTexture(QSGContext2DTexture* texture); - void update(); - void preprocess(); - void setSize(const QSizeF& size); -private: - void updateTexture(); - void updateGeometry(); - - QSGCanvasItem* m_item; - QSGOpaqueTextureMaterial m_material; - QSGTextureMaterial m_materialO; - QSGGeometry m_geometry; - QSGContext2DTexture* m_texture; - QSizeF m_size; - - bool m_dirtyGeometry; - bool m_dirtyTexture; -}; - -QT_END_HEADER - -QT_END_NAMESPACE - -#endif // QSGCONTEXT2DNODE_P_H diff --git a/src/declarative/items/context2d/qsgcontext2dtexture.cpp b/src/declarative/items/context2d/qsgcontext2dtexture.cpp deleted file mode 100644 index 721f847978..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dtexture.cpp +++ /dev/null @@ -1,777 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgcontext2dtexture_p.h" -#include "qsgcontext2dtile_p.h" -#include "qsgcanvasitem_p.h" -#include -#include -#include "qsgcontext2dcommandbuffer_p.h" -#include - -#include -#include -#include - -#define QT_MINIMUM_FBO_SIZE 64 - -static inline int qt_next_power_of_two(int v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - ++v; - return v; -} - - -Q_GLOBAL_STATIC(QThread, globalCanvasThreadRenderInstance) - - -QSGContext2DTexture::QSGContext2DTexture() - : QSGDynamicTexture() - , m_context(0) - , m_canvasSize(QSize(1, 1)) - , m_tileSize(QSize(1, 1)) - , m_canvasWindow(QRect(0, 0, 1, 1)) - , m_dirtyCanvas(false) - , m_dirtyTexture(false) - , m_threadRendering(false) - , m_smooth(false) - , m_tiledCanvas(false) - , m_doGrabImage(false) - , m_painting(false) -{ -} - -QSGContext2DTexture::~QSGContext2DTexture() -{ - clearTiles(); -} - -QSize QSGContext2DTexture::textureSize() const -{ - return m_canvasWindow.size(); -} - -void QSGContext2DTexture::markDirtyTexture() -{ - lock(); - m_dirtyTexture = true; - unlock(); - emit textureChanged(); -} - -bool QSGContext2DTexture::setCanvasSize(const QSize &size) -{ - if (m_canvasSize != size) { - m_canvasSize = size; - m_dirtyCanvas = true; - return true; - } - return false; -} - -bool QSGContext2DTexture::setTileSize(const QSize &size) -{ - if (m_tileSize != size) { - m_tileSize = size; - m_dirtyCanvas = true; - return true; - } - return false; -} - -void QSGContext2DTexture::setSmooth(bool smooth) -{ - m_smooth = smooth; -} - -void QSGContext2DTexture::setItem(QSGCanvasItem* item) -{ - if (!item) { - lock(); - m_item = 0; - m_context = 0; - unlock(); - wake(); - } else if (m_item != item) { - lock(); - m_item = item; - m_context = item->context(); - m_state = m_context->state; - unlock(); - connect(this, SIGNAL(textureChanged()), m_item, SIGNAL(painted())); - } -} - -bool QSGContext2DTexture::setCanvasWindow(const QRect& r) -{ - if (m_canvasWindow != r) { - m_canvasWindow = r; - return true; - } - return false; -} - -bool QSGContext2DTexture::setDirtyRect(const QRect &r) -{ - bool doDirty = false; - if (m_tiledCanvas) { - foreach (QSGContext2DTile* t, m_tiles) { - bool dirty = t->rect().intersected(r).isValid(); - t->markDirty(dirty); - if (dirty) - doDirty = true; - } - } else { - doDirty = m_canvasWindow.intersected(r).isValid(); - } - return doDirty; -} - -void QSGContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth) -{ - lock(); - - QSize ts = tileSize; - if (ts.width() > canvasSize.width()) - ts.setWidth(canvasSize.width()); - - if (ts.height() > canvasSize.height()) - ts.setHeight(canvasSize.height()); - - setCanvasSize(canvasSize); - setTileSize(ts); - - if (canvasSize == canvasWindow.size()) { - m_tiledCanvas = false; - m_dirtyCanvas = false; - } else { - m_tiledCanvas = true; - } - - bool doDirty = false; - if (dirtyRect.isValid()) - doDirty = setDirtyRect(dirtyRect); - - bool windowChanged = setCanvasWindow(canvasWindow); - if (windowChanged || doDirty) { - if (m_threadRendering) - QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection); - else if (supportDirectRendering()) { - QMetaObject::invokeMethod(this, "paint", Qt::DirectConnection); - } - } - - setSmooth(smooth); - unlock(); -} - -void QSGContext2DTexture::paintWithoutTiles() -{ - QSGContext2DCommandBuffer* ccb = m_context->buffer(); - - if (ccb->isEmpty() && m_threadRendering && !m_doGrabImage) { - lock(); - if (m_item) - QMetaObject::invokeMethod(m_item, "_doPainting", Qt::QueuedConnection, Q_ARG(QRectF, QRectF(0, 0, m_canvasSize.width(), m_canvasSize.height()))); - wait(); - unlock(); - } - if (ccb->isEmpty()) { - return; - } - - QPaintDevice* device = beginPainting(); - if (!device) { - endPainting(); - return; - } - - QPainter p; - p.begin(device); - if (m_smooth) - p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing - | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); - else - p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing - | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, false); - p.setCompositionMode(QPainter::CompositionMode_SourceOver); - m_state = ccb->replay(&p, m_state); - - ccb->clear(); - markDirtyTexture(); - endPainting(); -} - -bool QSGContext2DTexture::canvasDestroyed() -{ - bool noCanvas = false; - lock(); - noCanvas = m_item == 0; - unlock(); - return noCanvas; -} - -void QSGContext2DTexture::paint() -{ - if (canvasDestroyed()) - return; - - if (!m_tiledCanvas) { - paintWithoutTiles(); - } else { - QSGContext2D::State oldState = m_state; - QSGContext2DCommandBuffer* ccb = m_context->buffer(); - - lock(); - QRect tiledRegion = createTiles(m_canvasWindow.intersected(QRect(QPoint(0, 0), m_canvasSize))); - unlock(); - - if (!tiledRegion.isEmpty()) { - if (m_threadRendering && !m_doGrabImage) { - QRect dirtyRect; - - lock(); - foreach (QSGContext2DTile* tile, m_tiles) { - if (tile->dirty()) { - if (dirtyRect.isEmpty()) - dirtyRect = tile->rect(); - else - dirtyRect |= tile->rect(); - } - } - unlock(); - - if (dirtyRect.isValid()) { - lock(); - if (m_item) - QMetaObject::invokeMethod(m_item, "_doPainting", Qt::QueuedConnection, Q_ARG(QRectF, dirtyRect)); - wait(); - unlock(); - } - } - - if (beginPainting()) { - foreach (QSGContext2DTile* tile, m_tiles) { - bool dirtyTile = false, dirtyCanvas = false, smooth = false; - - lock(); - dirtyTile = tile->dirty(); - smooth = m_smooth; - dirtyCanvas = m_dirtyCanvas; - unlock(); - - //canvas size or tile size may change during painting tiles - if (dirtyCanvas) { - if (m_threadRendering) - QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection); - endPainting(); - return; - } else if (dirtyTile) { - m_state = ccb->replay(tile->createPainter(smooth), oldState); - tile->drawFinished(); - lock(); - tile->markDirty(false); - unlock(); - } - - compositeTile(tile); - } - ccb->clear(); - endPainting(); - markDirtyTexture(); - } - } - } -} - -QRect QSGContext2DTexture::tiledRect(const QRectF& window, const QSize& tileSize) -{ - if (window.isEmpty()) - return QRect(); - - const int tw = tileSize.width(); - const int th = tileSize.height(); - const int h1 = window.left() / tw; - const int v1 = window.top() / th; - - const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw; - const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th; - - return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th); -} - -QRect QSGContext2DTexture::createTiles(const QRect& window) -{ - QList oldTiles = m_tiles; - m_tiles.clear(); - - if (window.isEmpty()) { - m_dirtyCanvas = false; - return QRect(); - } - - QRect r = tiledRect(window, m_tileSize); - - const int tw = m_tileSize.width(); - const int th = m_tileSize.height(); - const int h1 = window.left() / tw; - const int v1 = window.top() / th; - - - const int htiles = r.width() / tw; - const int vtiles = r.height() / th; - - for (int yy = 0; yy < vtiles; ++yy) { - for (int xx = 0; xx < htiles; ++xx) { - int ht = xx + h1; - int vt = yy + v1; - - QSGContext2DTile* tile = 0; - - QPoint pos(ht * tw, vt * th); - QRect rect(pos, m_tileSize); - - for (int i = 0; i < oldTiles.size(); i++) { - if (oldTiles[i]->rect() == rect) { - tile = oldTiles.takeAt(i); - break; - } - } - - if (!tile) - tile = createTile(); - - tile->setRect(rect); - m_tiles.append(tile); - } - } - - qDeleteAll(oldTiles); - - m_dirtyCanvas = false; - return r; -} - -void QSGContext2DTexture::clearTiles() -{ - qDeleteAll(m_tiles); - m_tiles.clear(); -} - -QSGContext2DFBOTexture::QSGContext2DFBOTexture() - : QSGContext2DTexture() - , m_fbo(0) - , m_multisampledFbo(0) - , m_paint_device(0) -{ - m_threadRendering = false; -} - -QSGContext2DFBOTexture::~QSGContext2DFBOTexture() -{ - delete m_fbo; - delete m_multisampledFbo; - delete m_paint_device; -} - -bool QSGContext2DFBOTexture::setCanvasSize(const QSize &size) -{ - QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.width())) - , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.height()))); - - if (m_canvasSize != s) { - m_canvasSize = s; - m_dirtyCanvas = true; - return true; - } - return false; -} - -bool QSGContext2DFBOTexture::setTileSize(const QSize &size) -{ - QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.width())) - , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.height()))); - if (m_tileSize != s) { - m_tileSize = s; - m_dirtyCanvas = true; - return true; - } - return false; -} - -bool QSGContext2DFBOTexture::setCanvasWindow(const QRect& canvasWindow) -{ - QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(canvasWindow.size().width())) - , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(canvasWindow.size().height()))); - - - bool doChanged = false; - if (m_fboSize != s) { - m_fboSize = s; - doChanged = true; - } - - if (m_canvasWindow != canvasWindow) - m_canvasWindow = canvasWindow; - - return doChanged; -} - -void QSGContext2DFBOTexture::bind() -{ - glBindTexture(GL_TEXTURE_2D, textureId()); - updateBindOptions(); -} - -QRectF QSGContext2DFBOTexture::textureSubRect() const -{ - return QRectF(0 - , 0 - , qreal(m_canvasWindow.width()) / m_fboSize.width() - , qreal(m_canvasWindow.height()) / m_fboSize.height()); -} - - -int QSGContext2DFBOTexture::textureId() const -{ - return m_fbo? m_fbo->texture() : 0; -} - - -bool QSGContext2DFBOTexture::updateTexture() -{ - if (!m_context->buffer()->isEmpty()) { - paint(); - } - - bool textureUpdated = m_dirtyTexture; - - m_dirtyTexture = false; - - if (m_doGrabImage) { - grabImage(); - m_condition.wakeOne(); - m_doGrabImage = false; - } - return textureUpdated; -} - -QSGContext2DTile* QSGContext2DFBOTexture::createTile() const -{ - return new QSGContext2DFBOTile(); -} - -void QSGContext2DFBOTexture::grabImage() -{ - if (m_fbo) { - m_grabedImage = m_fbo->toImage(); - } -} - -bool QSGContext2DFBOTexture::doMultisampling() const -{ - static bool extensionsChecked = false; - static bool multisamplingSupported = false; - - if (!extensionsChecked) { - QList extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' '); - multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample") - && extensions.contains("GL_EXT_framebuffer_blit"); - extensionsChecked = true; - } - - return multisamplingSupported && m_smooth; -} - -QImage QSGContext2DFBOTexture::toImage(const QRectF& region) -{ -#define QML_CONTEXT2D_WAIT_MAX 5000 - - m_doGrabImage = true; - if (m_item) - m_item->update(); - - QImage grabbed; - m_mutex.lock(); - bool ok = m_condition.wait(&m_mutex, QML_CONTEXT2D_WAIT_MAX); - - if (!ok) - grabbed = QImage(); - - if (region.isValid()) - grabbed = m_grabedImage.copy(region.toRect()); - else - grabbed = m_grabedImage; - m_grabedImage = QImage(); - return grabbed; -} - -void QSGContext2DFBOTexture::compositeTile(QSGContext2DTile* tile) -{ - QSGContext2DFBOTile* t = static_cast(tile); - QRect target = t->rect().intersect(m_canvasWindow); - if (target.isValid()) { - QRect source = target; - - source.moveTo(source.topLeft() - t->rect().topLeft()); - target.moveTo(target.topLeft() - m_canvasWindow.topLeft()); - - QOpenGLFramebufferObject::blitFramebuffer(m_fbo, target, t->fbo(), source); - } -} -QSGCanvasItem::RenderTarget QSGContext2DFBOTexture::renderTarget() const -{ - return QSGCanvasItem::FramebufferObject; -} -QPaintDevice* QSGContext2DFBOTexture::beginPainting() -{ - QSGContext2DTexture::beginPainting(); - - if (m_canvasWindow.size().isEmpty() && !m_threadRendering) { - delete m_fbo; - delete m_multisampledFbo; - m_fbo = 0; - m_multisampledFbo = 0; - return 0; - } else if (!m_fbo || m_fbo->size() != m_fboSize) { - delete m_fbo; - delete m_multisampledFbo; - if (doMultisampling()) { - { - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - format.setSamples(8); - m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format); - } - { - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::NoAttachment); - m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); - } - } else { - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - - m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); - } - } - - if (doMultisampling()) - m_multisampledFbo->bind(); - else - m_fbo->bind(); - - - if (!m_paint_device) { - QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(m_fbo->size()); - gl_device->setPaintFlipped(true); - m_paint_device = gl_device; - } - - return m_paint_device; -} - -void QSGContext2DFBOTexture::endPainting() -{ - QSGContext2DTexture::endPainting(); - if (m_multisampledFbo) { - QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); - m_multisampledFbo->release(); - } else if (m_fbo) - m_fbo->release(); -} -void qt_quit_context2d_render_thread() -{ - QThread* thread = globalCanvasThreadRenderInstance(); - - if (thread->isRunning()) { - thread->exit(0); - thread->wait(1000); - } -} - -QSGContext2DImageTexture::QSGContext2DImageTexture(bool threadRendering) - : QSGContext2DTexture() - , m_texture(new QSGPlainTexture()) -{ - m_texture->setOwnsTexture(true); - m_texture->setHasMipmaps(false); - - m_threadRendering = threadRendering; - - if (m_threadRendering) { - QThread* thread = globalCanvasThreadRenderInstance(); - moveToThread(thread); - - if (!thread->isRunning()) { - qAddPostRoutine(qt_quit_context2d_render_thread); - thread->start(); - } - } -} - -QSGContext2DImageTexture::~QSGContext2DImageTexture() -{ - delete m_texture; -} - -int QSGContext2DImageTexture::textureId() const -{ - return m_texture->textureId(); -} - -void QSGContext2DImageTexture::lock() -{ - if (m_threadRendering) - m_mutex.lock(); -} -void QSGContext2DImageTexture::unlock() -{ - if (m_threadRendering) - m_mutex.unlock(); -} - -void QSGContext2DImageTexture::wait() -{ - if (m_threadRendering) - m_waitCondition.wait(&m_mutex); -} - -void QSGContext2DImageTexture::wake() -{ - if (m_threadRendering) - m_waitCondition.wakeOne(); -} - -bool QSGContext2DImageTexture::supportDirectRendering() const -{ - return !m_threadRendering; -} - -QSGCanvasItem::RenderTarget QSGContext2DImageTexture::renderTarget() const -{ - return QSGCanvasItem::Image; -} - -void QSGContext2DImageTexture::bind() -{ - m_texture->bind(); -} - -bool QSGContext2DImageTexture::updateTexture() -{ - lock(); - bool textureUpdated = m_dirtyTexture; - if (m_dirtyTexture) { - m_texture->setImage(m_image); - m_dirtyTexture = false; - } - unlock(); - return textureUpdated; -} - -QSGContext2DTile* QSGContext2DImageTexture::createTile() const -{ - return new QSGContext2DImageTile(); -} - -void QSGContext2DImageTexture::grabImage(const QRect& r) -{ - m_doGrabImage = true; - paint(); - m_doGrabImage = false; - m_grabedImage = m_image.copy(r); -} - -QImage QSGContext2DImageTexture::toImage(const QRectF& region) -{ - QRect r = region.isValid() ? region.toRect() : QRect(QPoint(0, 0), m_canvasWindow.size()); - if (threadRendering()) { - wake(); - QMetaObject::invokeMethod(this, "grabImage", Qt::BlockingQueuedConnection, Q_ARG(QRect, r)); - } else { - QMetaObject::invokeMethod(this, "grabImage", Qt::DirectConnection, Q_ARG(QRect, r)); - } - QImage image = m_grabedImage; - m_grabedImage = QImage(); - return image; -} - -QPaintDevice* QSGContext2DImageTexture::beginPainting() -{ - QSGContext2DTexture::beginPainting(); - - if (m_canvasWindow.size().isEmpty()) - return 0; - - lock(); - if (m_image.size() != m_canvasWindow.size()) { - m_image = QImage(m_canvasWindow.size(), QImage::Format_ARGB32_Premultiplied); - m_image.fill(0x00000000); - } - unlock(); - return &m_image; -} - -void QSGContext2DImageTexture::compositeTile(QSGContext2DTile* tile) -{ - Q_ASSERT(!tile->dirty()); - QSGContext2DImageTile* t = static_cast(tile); - QRect target = t->rect().intersect(m_canvasWindow); - if (target.isValid()) { - QRect source = target; - source.moveTo(source.topLeft() - t->rect().topLeft()); - target.moveTo(target.topLeft() - m_canvasWindow.topLeft()); - - lock(); - m_painter.begin(&m_image); - m_painter.setCompositionMode(QPainter::CompositionMode_Source); - m_painter.drawImage(target, t->image(), source); - m_painter.end(); - unlock(); - } -} diff --git a/src/declarative/items/context2d/qsgcontext2dtexture_p.h b/src/declarative/items/context2d/qsgcontext2dtexture_p.h deleted file mode 100644 index dbc383d7fc..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dtexture_p.h +++ /dev/null @@ -1,205 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCONTEXT2DTEXTURE_P_H -#define QSGCONTEXT2DTEXTURE_P_H - -#include -#include "qsgcanvasitem_p.h" -#include "qsgcontext2d_p.h" - -#include -#include - -#include -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGContext2DTile; -class QSGContext2DCommandBuffer; - -class QSGContext2DTexture : public QSGDynamicTexture -{ - Q_OBJECT -public: - QSGContext2DTexture(); - ~QSGContext2DTexture(); - - virtual bool hasAlphaChannel() const {return true;} - virtual bool hasMipmaps() const {return false;} - virtual QSize textureSize() const; - virtual void lock() {} - virtual void unlock() {} - virtual void wait() {} - virtual void wake() {} - bool threadRendering() const {return m_threadRendering;} - virtual bool supportThreadRendering() const = 0; - virtual bool supportDirectRendering() const = 0; - virtual QSGCanvasItem::RenderTarget renderTarget() const = 0; - virtual QImage toImage(const QRectF& region = QRectF()) = 0; - static QRect tiledRect(const QRectF& window, const QSize& tileSize); - - virtual bool setCanvasSize(const QSize &size); - virtual bool setTileSize(const QSize &size); - virtual bool setCanvasWindow(const QRect& canvasWindow); - void setSmooth(bool smooth); - bool setDirtyRect(const QRect &dirtyRect); - virtual void canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth); - bool canvasDestroyed(); -Q_SIGNALS: - void textureChanged(); - -public Q_SLOTS: - void markDirtyTexture(); - void setItem(QSGCanvasItem* item); - void paint(); - -protected: - void paintWithoutTiles(); - virtual QPaintDevice* beginPainting() {m_painting = true; return 0; } - virtual void endPainting() {m_painting = false;} - virtual QSGContext2DTile* createTile() const = 0; - virtual void compositeTile(QSGContext2DTile* tile) = 0; - - void clearTiles(); - QRect createTiles(const QRect& window); - - QList m_tiles; - QSGContext2D* m_context; - - QSGContext2D::State m_state; - - QSGCanvasItem* m_item; - QSize m_canvasSize; - QSize m_tileSize; - QRect m_canvasWindow; - - uint m_dirtyCanvas : 1; - uint m_dirtyTexture : 1; - uint m_threadRendering : 1; - uint m_smooth : 1; - uint m_tiledCanvas : 1; - uint m_doGrabImage : 1; - uint m_painting : 1; -}; - -class QSGContext2DFBOTexture : public QSGContext2DTexture -{ - Q_OBJECT - -public: - QSGContext2DFBOTexture(); - ~QSGContext2DFBOTexture(); - virtual int textureId() const; - virtual bool updateTexture(); - virtual QSGContext2DTile* createTile() const; - virtual QImage toImage(const QRectF& region = QRectF()); - virtual QPaintDevice* beginPainting(); - virtual void endPainting(); - QRectF textureSubRect() const; - virtual bool supportThreadRendering() const {return false;} - virtual bool supportDirectRendering() const {return false;} - virtual QSGCanvasItem::RenderTarget renderTarget() const; - virtual void compositeTile(QSGContext2DTile* tile); - virtual void bind(); - virtual bool setCanvasSize(const QSize &size); - virtual bool setTileSize(const QSize &size); - virtual bool setCanvasWindow(const QRect& canvasWindow); -private Q_SLOTS: - void grabImage(); - -private: - bool doMultisampling() const; - QImage m_grabedImage; - QOpenGLFramebufferObject *m_fbo; - QOpenGLFramebufferObject *m_multisampledFbo; - QMutex m_mutex; - QWaitCondition m_condition; - QSize m_fboSize; - QPaintDevice *m_paint_device; -}; - -class QSGPlainTexture; -class QSGContext2DImageTexture : public QSGContext2DTexture -{ - Q_OBJECT - -public: - QSGContext2DImageTexture(bool threadRendering = true); - ~QSGContext2DImageTexture(); - virtual int textureId() const; - virtual void bind(); - virtual bool supportThreadRendering() const {return true;} - virtual bool supportDirectRendering() const; - virtual QSGCanvasItem::RenderTarget renderTarget() const; - virtual void lock(); - virtual void unlock(); - virtual void wait(); - virtual void wake(); - - virtual bool updateTexture(); - virtual QSGContext2DTile* createTile() const; - virtual QImage toImage(const QRectF& region = QRectF()); - virtual QPaintDevice* beginPainting(); - virtual void compositeTile(QSGContext2DTile* tile); - -private Q_SLOTS: - void grabImage(const QRect& r); -private: - QImage m_image; - QImage m_grabedImage; - QMutex m_mutex; - QWaitCondition m_waitCondition; - QPainter m_painter; - QSGPlainTexture* m_texture; -}; - -QT_END_HEADER - -QT_END_NAMESPACE - -#endif // QSGCONTEXT2DTEXTURE_P_H diff --git a/src/declarative/items/context2d/qsgcontext2dtile.cpp b/src/declarative/items/context2d/qsgcontext2dtile.cpp deleted file mode 100644 index 8c8ef836b0..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dtile.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgcontext2dtile_p.h" - -#include -#include -#include - -QSGContext2DTile::QSGContext2DTile() - : m_dirty(true) - , m_rect(QRect(0, 0, 1, 1)) - , m_device(0) -{ -} - -QSGContext2DTile::~QSGContext2DTile() -{ - if (m_painter.isActive()) - m_painter.end(); -} - -QPainter* QSGContext2DTile::createPainter(bool smooth) -{ - if (m_painter.isActive()) - m_painter.end(); - - if (m_device) { - aboutToDraw(); - m_painter.begin(m_device); - m_painter.resetTransform(); - m_painter.setCompositionMode(QPainter::CompositionMode_Source); - -#ifdef QSGCONTEXT2D_DEBUG - int v = 100; - int gray = (m_rect.x() / m_rect.width() + m_rect.y() / m_rect.height()) % 2; - if (gray) - v = 150; - m_painter.fillRect(QRect(0, 0, m_rect.width(), m_rect.height()), QColor(v, v, v, 255)); -#endif - if (smooth) - m_painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing - | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); - else - m_painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing - | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, false); - - m_painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - m_painter.translate(-m_rect.left(), -m_rect.top()); - m_painter.setClipRect(m_rect); - m_painter.setClipping(false); - return &m_painter; - } - - return 0; -} - -QSGContext2DFBOTile::QSGContext2DFBOTile() - : QSGContext2DTile() - , m_fbo(0) -{ -} - - -QSGContext2DFBOTile::~QSGContext2DFBOTile() -{ - delete m_fbo; -} - -void QSGContext2DFBOTile::aboutToDraw() -{ - m_fbo->bind(); - if (!m_device) { - QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(rect().size()); - m_device = gl_device; - QPainter p(m_device); - p.fillRect(QRectF(0, 0, m_fbo->width(), m_fbo->height()), QColor(qRgba(0, 0, 0, 0))); - p.end(); - } -} - -void QSGContext2DFBOTile::drawFinished() -{ - m_fbo->release(); -} - -void QSGContext2DFBOTile::setRect(const QRect& r) -{ - if (m_rect == r) - return; - m_rect = r; - m_dirty = true; - if (!m_fbo || m_fbo->size() != r.size()) { - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - format.setInternalTextureFormat(GL_RGBA); - format.setMipmap(false); - - if (m_painter.isActive()) - m_painter.end(); - - delete m_fbo; - m_fbo = new QOpenGLFramebufferObject(r.size(), format); - } -} - - -QSGContext2DImageTile::QSGContext2DImageTile() - : QSGContext2DTile() -{ -} - -QSGContext2DImageTile::~QSGContext2DImageTile() -{ -} - -void QSGContext2DImageTile::setRect(const QRect& r) -{ - if (m_rect == r) - return; - m_rect = r; - m_dirty = true; - if (m_image.size() != r.size()) { - m_image = QImage(r.size(), QImage::Format_ARGB32_Premultiplied); - } - m_device = &m_image; -} \ No newline at end of file diff --git a/src/declarative/items/context2d/qsgcontext2dtile_p.h b/src/declarative/items/context2d/qsgcontext2dtile_p.h deleted file mode 100644 index 57b68ef67d..0000000000 --- a/src/declarative/items/context2d/qsgcontext2dtile_p.h +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCONTEXT2DTILE_P_H -#define QSGCONTEXT2DTILE_P_H - -#include "qsgcontext2d_p.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGContext2DTexture; -class QSGContext2DCommandBuffer; - -class QSGContext2DTile -{ -public: - QSGContext2DTile(); - ~QSGContext2DTile(); - - bool dirty() const {return m_dirty;} - void markDirty(bool dirty) {m_dirty = dirty;} - - QRect rect() const {return m_rect;} - - virtual void setRect(const QRect& r) = 0; - virtual QPainter* createPainter(bool smooth = false); - virtual void drawFinished() {} - -protected: - virtual void aboutToDraw() {} - uint m_dirty : 1; - QRect m_rect; - QPaintDevice* m_device; - QPainter m_painter; -}; - - -class QSGContext2DFBOTile : public QSGContext2DTile -{ -public: - QSGContext2DFBOTile(); - ~QSGContext2DFBOTile(); - virtual void setRect(const QRect& r); - QOpenGLFramebufferObject* fbo() const {return m_fbo;} - void drawFinished(); - -protected: - void aboutToDraw(); -private: - - - QOpenGLFramebufferObject *m_fbo; -}; - -class QSGContext2DImageTile : public QSGContext2DTile -{ -public: - QSGContext2DImageTile(); - ~QSGContext2DImageTile(); - void setRect(const QRect& r); - const QImage& image() const {return m_image;} -private: - QImage m_image; -}; -QT_END_HEADER - -QT_END_NAMESPACE - -#endif // QSGCONTEXT2DTILE_P_H diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index 025f67ab90..c3d6a2ab73 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -1,127 +1,127 @@ HEADERS += \ - $$PWD/qsgevents_p_p.h \ - $$PWD/qsgitemchangelistener_p.h \ - $$PWD/qsganchors_p.h \ - $$PWD/qsganchors_p_p.h \ - $$PWD/qsgitem.h \ - $$PWD/qsgitem_p.h \ - $$PWD/qsgrectangle_p.h \ - $$PWD/qsgrectangle_p_p.h \ - $$PWD/qsgcanvas.h \ - $$PWD/qsgcanvas_p.h \ - $$PWD/qsgfocusscope_p.h \ - $$PWD/qsgitemsmodule_p.h \ - $$PWD/qsgpainteditem.h \ - $$PWD/qsgpainteditem_p.h \ - $$PWD/qsgtext_p.h \ - $$PWD/qsgtext_p_p.h \ - $$PWD/qsgtextnode_p.h \ - $$PWD/qsgtextinput_p.h \ - $$PWD/qsgtextinput_p_p.h \ - $$PWD/qsgtextedit_p.h \ - $$PWD/qsgtextedit_p_p.h \ - $$PWD/qsgimagebase_p.h \ - $$PWD/qsgimagebase_p_p.h \ - $$PWD/qsgimage_p.h \ - $$PWD/qsgimage_p_p.h \ - $$PWD/qsgborderimage_p.h \ - $$PWD/qsgborderimage_p_p.h \ - $$PWD/qsgninepatchnode_p.h \ - $$PWD/qsgscalegrid_p_p.h \ - $$PWD/qsgmousearea_p.h \ - $$PWD/qsgmousearea_p_p.h \ - $$PWD/qsgpincharea_p.h \ - $$PWD/qsgpincharea_p_p.h \ - $$PWD/qsgflickable_p.h \ - $$PWD/qsgflickable_p_p.h \ - $$PWD/qsglistview_p.h \ - $$PWD/qsgvisualadaptormodel_p.h \ - $$PWD/qsgvisualdatamodel_p.h \ - $$PWD/qsgvisualitemmodel_p.h \ - $$PWD/qsgrepeater_p.h \ - $$PWD/qsgrepeater_p_p.h \ - $$PWD/qsggridview_p.h \ - $$PWD/qsgpathview_p.h \ - $$PWD/qsgpathview_p_p.h \ - $$PWD/qsgpositioners_p.h \ - $$PWD/qsgpositioners_p_p.h \ - $$PWD/qsgloader_p.h \ - $$PWD/qsgloader_p_p.h \ - $$PWD/qsganimatedimage_p.h \ - $$PWD/qsganimatedimage_p_p.h \ - $$PWD/qsgflipable_p.h \ - $$PWD/qsgtranslate_p.h \ - $$PWD/qsgclipnode_p.h \ - $$PWD/qsgview.h \ - $$PWD/qsgview_p.h \ - $$PWD/qsganimation_p.h \ - $$PWD/qsganimation_p_p.h \ - $$PWD/qsgstateoperations_p.h \ - $$PWD/qsgimplicitsizeitem_p.h \ - $$PWD/qsgimplicitsizeitem_p_p.h \ - $$PWD/qsgspriteengine_p.h \ - $$PWD/qsgsprite_p.h \ - $$PWD/qsgspriteimage_p.h \ - $$PWD/qsgdrag_p.h \ - $$PWD/qsgdroparea_p.h \ - $$PWD/qsgitemview_p.h \ - $$PWD/qsgitemview_p_p.h + $$PWD/qquickevents_p_p.h \ + $$PWD/qquickitemchangelistener_p.h \ + $$PWD/qquickanchors_p.h \ + $$PWD/qquickanchors_p_p.h \ + $$PWD/qquickitem.h \ + $$PWD/qquickitem_p.h \ + $$PWD/qquickrectangle_p.h \ + $$PWD/qquickrectangle_p_p.h \ + $$PWD/qquickcanvas.h \ + $$PWD/qquickcanvas_p.h \ + $$PWD/qquickfocusscope_p.h \ + $$PWD/qquickitemsmodule_p.h \ + $$PWD/qquickpainteditem.h \ + $$PWD/qquickpainteditem_p.h \ + $$PWD/qquicktext_p.h \ + $$PWD/qquicktext_p_p.h \ + $$PWD/qquicktextnode_p.h \ + $$PWD/qquicktextinput_p.h \ + $$PWD/qquicktextinput_p_p.h \ + $$PWD/qquicktextedit_p.h \ + $$PWD/qquicktextedit_p_p.h \ + $$PWD/qquickimagebase_p.h \ + $$PWD/qquickimagebase_p_p.h \ + $$PWD/qquickimage_p.h \ + $$PWD/qquickimage_p_p.h \ + $$PWD/qquickborderimage_p.h \ + $$PWD/qquickborderimage_p_p.h \ + $$PWD/qquickninepatchnode_p.h \ + $$PWD/qquickscalegrid_p_p.h \ + $$PWD/qquickmousearea_p.h \ + $$PWD/qquickmousearea_p_p.h \ + $$PWD/qquickpincharea_p.h \ + $$PWD/qquickpincharea_p_p.h \ + $$PWD/qquickflickable_p.h \ + $$PWD/qquickflickable_p_p.h \ + $$PWD/qquicklistview_p.h \ + $$PWD/qquickvisualadaptormodel_p.h \ + $$PWD/qquickvisualdatamodel_p.h \ + $$PWD/qquickvisualitemmodel_p.h \ + $$PWD/qquickrepeater_p.h \ + $$PWD/qquickrepeater_p_p.h \ + $$PWD/qquickgridview_p.h \ + $$PWD/qquickpathview_p.h \ + $$PWD/qquickpathview_p_p.h \ + $$PWD/qquickpositioners_p.h \ + $$PWD/qquickpositioners_p_p.h \ + $$PWD/qquickloader_p.h \ + $$PWD/qquickloader_p_p.h \ + $$PWD/qquickanimatedimage_p.h \ + $$PWD/qquickanimatedimage_p_p.h \ + $$PWD/qquickflipable_p.h \ + $$PWD/qquicktranslate_p.h \ + $$PWD/qquickclipnode_p.h \ + $$PWD/qquickview.h \ + $$PWD/qquickview_p.h \ + $$PWD/qquickanimation_p.h \ + $$PWD/qquickanimation_p_p.h \ + $$PWD/qquickstateoperations_p.h \ + $$PWD/qquickimplicitsizeitem_p.h \ + $$PWD/qquickimplicitsizeitem_p_p.h \ + $$PWD/qquickspriteengine_p.h \ + $$PWD/qquicksprite_p.h \ + $$PWD/qquickspriteimage_p.h \ + $$PWD/qquickdrag_p.h \ + $$PWD/qquickdroparea_p.h \ + $$PWD/qquickitemview_p.h \ + $$PWD/qquickitemview_p_p.h SOURCES += \ - $$PWD/qsgevents.cpp \ - $$PWD/qsganchors.cpp \ - $$PWD/qsgitem.cpp \ - $$PWD/qsgrectangle.cpp \ - $$PWD/qsgcanvas.cpp \ - $$PWD/qsgfocusscope.cpp \ - $$PWD/qsgitemsmodule.cpp \ - $$PWD/qsgpainteditem.cpp \ - $$PWD/qsgtext.cpp \ - $$PWD/qsgtextnode.cpp \ - $$PWD/qsgtextinput.cpp \ - $$PWD/qsgtextedit.cpp \ - $$PWD/qsgimagebase.cpp \ - $$PWD/qsgimage.cpp \ - $$PWD/qsgborderimage.cpp \ - $$PWD/qsgninepatchnode.cpp \ - $$PWD/qsgscalegrid.cpp \ - $$PWD/qsgmousearea.cpp \ - $$PWD/qsgpincharea.cpp \ - $$PWD/qsgflickable.cpp \ - $$PWD/qsglistview.cpp \ - $$PWD/qsgvisualadaptormodel.cpp \ - $$PWD/qsgvisualdatamodel.cpp \ - $$PWD/qsgvisualitemmodel.cpp \ - $$PWD/qsgrepeater.cpp \ - $$PWD/qsggridview.cpp \ - $$PWD/qsgpathview.cpp \ - $$PWD/qsgpositioners.cpp \ - $$PWD/qsgloader.cpp \ - $$PWD/qsganimatedimage.cpp \ - $$PWD/qsgflipable.cpp \ - $$PWD/qsgtranslate.cpp \ - $$PWD/qsgclipnode.cpp \ - $$PWD/qsgview.cpp \ - $$PWD/qsganimation.cpp \ - $$PWD/qsgstateoperations.cpp \ - $$PWD/qsgimplicitsizeitem.cpp \ - $$PWD/qsgspriteengine.cpp \ - $$PWD/qsgsprite.cpp \ - $$PWD/qsgspriteimage.cpp \ - $$PWD/qsgdrag.cpp \ - $$PWD/qsgdroparea.cpp \ - $$PWD/qsgitemview.cpp + $$PWD/qquickevents.cpp \ + $$PWD/qquickanchors.cpp \ + $$PWD/qquickitem.cpp \ + $$PWD/qquickrectangle.cpp \ + $$PWD/qquickcanvas.cpp \ + $$PWD/qquickfocusscope.cpp \ + $$PWD/qquickitemsmodule.cpp \ + $$PWD/qquickpainteditem.cpp \ + $$PWD/qquicktext.cpp \ + $$PWD/qquicktextnode.cpp \ + $$PWD/qquicktextinput.cpp \ + $$PWD/qquicktextedit.cpp \ + $$PWD/qquickimagebase.cpp \ + $$PWD/qquickimage.cpp \ + $$PWD/qquickborderimage.cpp \ + $$PWD/qquickninepatchnode.cpp \ + $$PWD/qquickscalegrid.cpp \ + $$PWD/qquickmousearea.cpp \ + $$PWD/qquickpincharea.cpp \ + $$PWD/qquickflickable.cpp \ + $$PWD/qquicklistview.cpp \ + $$PWD/qquickvisualadaptormodel.cpp \ + $$PWD/qquickvisualdatamodel.cpp \ + $$PWD/qquickvisualitemmodel.cpp \ + $$PWD/qquickrepeater.cpp \ + $$PWD/qquickgridview.cpp \ + $$PWD/qquickpathview.cpp \ + $$PWD/qquickpositioners.cpp \ + $$PWD/qquickloader.cpp \ + $$PWD/qquickanimatedimage.cpp \ + $$PWD/qquickflipable.cpp \ + $$PWD/qquicktranslate.cpp \ + $$PWD/qquickclipnode.cpp \ + $$PWD/qquickview.cpp \ + $$PWD/qquickanimation.cpp \ + $$PWD/qquickstateoperations.cpp \ + $$PWD/qquickimplicitsizeitem.cpp \ + $$PWD/qquickspriteengine.cpp \ + $$PWD/qquicksprite.cpp \ + $$PWD/qquickspriteimage.cpp \ + $$PWD/qquickdrag.cpp \ + $$PWD/qquickdroparea.cpp \ + $$PWD/qquickitemview.cpp SOURCES += \ - $$PWD/qsgshadereffect.cpp \ - $$PWD/qsgshadereffectmesh.cpp \ - $$PWD/qsgshadereffectnode.cpp \ - $$PWD/qsgshadereffectsource.cpp \ + $$PWD/qquickshadereffect.cpp \ + $$PWD/qquickshadereffectmesh.cpp \ + $$PWD/qquickshadereffectnode.cpp \ + $$PWD/qquickshadereffectsource.cpp \ HEADERS += \ - $$PWD/qsgshadereffect_p.h \ - $$PWD/qsgshadereffectmesh_p.h \ - $$PWD/qsgshadereffectnode_p.h \ - $$PWD/qsgshadereffectsource_p.h \ + $$PWD/qquickshadereffect_p.h \ + $$PWD/qquickshadereffectmesh_p.h \ + $$PWD/qquickshadereffectnode_p.h \ + $$PWD/qquickshadereffectsource_p.h \ include(context2d/context2d.pri) diff --git a/src/declarative/items/qquickanchors.cpp b/src/declarative/items/qquickanchors.cpp new file mode 100644 index 0000000000..678347cc73 --- /dev/null +++ b/src/declarative/items/qquickanchors.cpp @@ -0,0 +1,1110 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickanchors_p_p.h" + +#include "qquickitem.h" +#include "qquickitem_p.h" + +#include + +QT_BEGIN_NAMESPACE + +//TODO: should we cache relationships, so we don't have to check each time (parent-child or sibling)? +//TODO: support non-parent, non-sibling (need to find lowest common ancestor) + +static qreal hcenter(QQuickItem *item) +{ + qreal width = item->width(); + int iw = width; + if (iw % 2) + return (width + 1) / 2; + else + return width / 2; +} + +static qreal vcenter(QQuickItem *item) +{ + qreal height = item->height(); + int ih = height; + if (ih % 2) + return (height + 1) / 2; + else + return height / 2; +} + +//### const item? +//local position +static qreal position(QQuickItem *item, QQuickAnchorLine::AnchorLine anchorLine) +{ + qreal ret = 0.0; + switch (anchorLine) { + case QQuickAnchorLine::Left: + ret = item->x(); + break; + case QQuickAnchorLine::Right: + ret = item->x() + item->width(); + break; + case QQuickAnchorLine::Top: + ret = item->y(); + break; + case QQuickAnchorLine::Bottom: + ret = item->y() + item->height(); + break; + case QQuickAnchorLine::HCenter: + ret = item->x() + hcenter(item); + break; + case QQuickAnchorLine::VCenter: + ret = item->y() + vcenter(item); + break; + case QQuickAnchorLine::Baseline: + ret = item->y() + item->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +//position when origin is 0,0 +static qreal adjustedPosition(QQuickItem *item, QQuickAnchorLine::AnchorLine anchorLine) +{ + qreal ret = 0.0; + switch (anchorLine) { + case QQuickAnchorLine::Left: + ret = 0.0; + break; + case QQuickAnchorLine::Right: + ret = item->width(); + break; + case QQuickAnchorLine::Top: + ret = 0.0; + break; + case QQuickAnchorLine::Bottom: + ret = item->height(); + break; + case QQuickAnchorLine::HCenter: + ret = hcenter(item); + break; + case QQuickAnchorLine::VCenter: + ret = vcenter(item); + break; + case QQuickAnchorLine::Baseline: + ret = item->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +QQuickAnchors::QQuickAnchors(QQuickItem *item, QObject *parent) +: QObject(*new QQuickAnchorsPrivate(item), parent) +{ +} + +QQuickAnchors::~QQuickAnchors() +{ + Q_D(QQuickAnchors); + d->remDepend(d->fill); + d->remDepend(d->centerIn); + d->remDepend(d->left.item); + d->remDepend(d->right.item); + d->remDepend(d->top.item); + d->remDepend(d->bottom.item); + d->remDepend(d->vCenter.item); + d->remDepend(d->hCenter.item); + d->remDepend(d->baseline.item); +} + +void QQuickAnchorsPrivate::fillChanged() +{ + Q_Q(QQuickAnchors); + if (!fill || !isItemComplete()) + return; + + if (updatingFill < 2) { + ++updatingFill; + + qreal horizontalMargin = q->mirrored() ? rightMargin : leftMargin; + + if (fill == item->parentItem()) { //child-parent + setItemPos(QPointF(horizontalMargin, topMargin)); + } else if (fill->parentItem() == item->parentItem()) { //siblings + setItemPos(QPointF(fill->x()+horizontalMargin, fill->y()+topMargin)); + } + setItemSize(QSizeF(fill->width()-leftMargin-rightMargin, fill->height()-topMargin-bottomMargin)); + + --updatingFill; + } else { + // ### Make this certain :) + qmlInfo(item) << QQuickAnchors::tr("Possible anchor loop detected on fill."); + } + +} + +void QQuickAnchorsPrivate::centerInChanged() +{ + Q_Q(QQuickAnchors); + if (!centerIn || fill || !isItemComplete()) + return; + + if (updatingCenterIn < 2) { + ++updatingCenterIn; + + qreal effectiveHCenterOffset = q->mirrored() ? -hCenterOffset : hCenterOffset; + if (centerIn == item->parentItem()) { + QPointF p(hcenter(item->parentItem()) - hcenter(item) + effectiveHCenterOffset, + vcenter(item->parentItem()) - vcenter(item) + vCenterOffset); + setItemPos(p); + + } else if (centerIn->parentItem() == item->parentItem()) { + QPointF p(centerIn->x() + hcenter(centerIn) - hcenter(item) + effectiveHCenterOffset, + centerIn->y() + vcenter(centerIn) - vcenter(item) + vCenterOffset); + setItemPos(p); + } + + --updatingCenterIn; + } else { + // ### Make this certain :) + qmlInfo(item) << QQuickAnchors::tr("Possible anchor loop detected on centerIn."); + } +} + +void QQuickAnchorsPrivate::clearItem(QQuickItem *item) +{ + if (!item) + return; + if (fill == item) + fill = 0; + if (centerIn == item) + centerIn = 0; + if (left.item == item) { + left.item = 0; + usedAnchors &= ~QQuickAnchors::LeftAnchor; + } + if (right.item == item) { + right.item = 0; + usedAnchors &= ~QQuickAnchors::RightAnchor; + } + if (top.item == item) { + top.item = 0; + usedAnchors &= ~QQuickAnchors::TopAnchor; + } + if (bottom.item == item) { + bottom.item = 0; + usedAnchors &= ~QQuickAnchors::BottomAnchor; + } + if (vCenter.item == item) { + vCenter.item = 0; + usedAnchors &= ~QQuickAnchors::VCenterAnchor; + } + if (hCenter.item == item) { + hCenter.item = 0; + usedAnchors &= ~QQuickAnchors::HCenterAnchor; + } + if (baseline.item == item) { + baseline.item = 0; + usedAnchors &= ~QQuickAnchors::BaselineAnchor; + } +} + +void QQuickAnchorsPrivate::addDepend(QQuickItem *item) +{ + if (!item) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + p->addItemChangeListener(this, QQuickItemPrivate::Geometry); +} + +void QQuickAnchorsPrivate::remDepend(QQuickItem *item) +{ + if (!item) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + p->removeItemChangeListener(this, QQuickItemPrivate::Geometry); +} + +bool QQuickAnchors::mirrored() +{ + Q_D(QQuickAnchors); + return QQuickItemPrivate::get(d->item)->effectiveLayoutMirror; +} + +bool QQuickAnchorsPrivate::isItemComplete() const +{ + return componentComplete; +} + +void QQuickAnchors::classBegin() +{ + Q_D(QQuickAnchors); + d->componentComplete = false; +} + +void QQuickAnchors::componentComplete() +{ + Q_D(QQuickAnchors); + d->componentComplete = true; +} + +void QQuickAnchorsPrivate::setItemHeight(qreal v) +{ + updatingMe = true; + item->setHeight(v); + updatingMe = false; +} + +void QQuickAnchorsPrivate::setItemWidth(qreal v) +{ + updatingMe = true; + item->setWidth(v); + updatingMe = false; +} + +void QQuickAnchorsPrivate::setItemX(qreal v) +{ + updatingMe = true; + item->setX(v); + updatingMe = false; +} + +void QQuickAnchorsPrivate::setItemY(qreal v) +{ + updatingMe = true; + item->setY(v); + updatingMe = false; +} + +void QQuickAnchorsPrivate::setItemPos(const QPointF &v) +{ + updatingMe = true; + item->setPos(v); + updatingMe = false; +} + +void QQuickAnchorsPrivate::setItemSize(const QSizeF &v) +{ + updatingMe = true; + item->setSize(v); + updatingMe = false; +} + +void QQuickAnchorsPrivate::updateMe() +{ + if (updatingMe) { + updatingMe = false; + return; + } + + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QQuickAnchorsPrivate::updateOnComplete() +{ + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG, const QRectF &oldG) +{ + fillChanged(); + centerInChanged(); + if (newG.x() != oldG.x() || newG.width() != oldG.width()) + updateHorizontalAnchors(); + if (newG.y() != oldG.y() || newG.height() != oldG.height()) + updateVerticalAnchors(); +} + +QQuickItem *QQuickAnchors::fill() const +{ + Q_D(const QQuickAnchors); + return d->fill; +} + +void QQuickAnchors::setFill(QQuickItem *f) +{ + Q_D(QQuickAnchors); + if (d->fill == f) + return; + + if (!f) { + d->remDepend(d->fill); + d->fill = f; + emit fillChanged(); + return; + } + if (f != d->item->parentItem() && f->parentItem() != d->item->parentItem()){ + qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); + return; + } + d->remDepend(d->fill); + d->fill = f; + d->addDepend(d->fill); + emit fillChanged(); + d->fillChanged(); +} + +void QQuickAnchors::resetFill() +{ + setFill(0); +} + +QQuickItem *QQuickAnchors::centerIn() const +{ + Q_D(const QQuickAnchors); + return d->centerIn; +} + +void QQuickAnchors::setCenterIn(QQuickItem* c) +{ + Q_D(QQuickAnchors); + if (d->centerIn == c) + return; + + if (!c) { + d->remDepend(d->centerIn); + d->centerIn = c; + emit centerInChanged(); + return; + } + if (c != d->item->parentItem() && c->parentItem() != d->item->parentItem()){ + qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); + return; + } + + d->remDepend(d->centerIn); + d->centerIn = c; + d->addDepend(d->centerIn); + emit centerInChanged(); + d->centerInChanged(); +} + +void QQuickAnchors::resetCenterIn() +{ + setCenterIn(0); +} + +bool QQuickAnchorsPrivate::calcStretch(const QQuickAnchorLine &edge1, + const QQuickAnchorLine &edge2, + qreal offset1, + qreal offset2, + QQuickAnchorLine::AnchorLine line, + qreal &stretch) +{ + bool edge1IsParent = (edge1.item == item->parentItem()); + bool edge2IsParent = (edge2.item == item->parentItem()); + bool edge1IsSibling = (edge1.item->parentItem() == item->parentItem()); + bool edge2IsSibling = (edge2.item->parentItem() == item->parentItem()); + + bool invalid = false; + if ((edge2IsParent && edge1IsParent) || (edge2IsSibling && edge1IsSibling)) { + stretch = (position(edge2.item, edge2.anchorLine) + offset2) + - (position(edge1.item, edge1.anchorLine) + offset1); + } else if (edge2IsParent && edge1IsSibling) { + stretch = (position(edge2.item, edge2.anchorLine) + offset2) + - (position(item->parentItem(), line) + + position(edge1.item, edge1.anchorLine) + offset1); + } else if (edge2IsSibling && edge1IsParent) { + stretch = (position(item->parentItem(), line) + position(edge2.item, edge2.anchorLine) + offset2) + - (position(edge1.item, edge1.anchorLine) + offset1); + } else + invalid = true; + + return invalid; +} + +void QQuickAnchorsPrivate::updateVerticalAnchors() +{ + if (fill || centerIn || !isItemComplete()) + return; + + if (updatingVerticalAnchor < 2) { + ++updatingVerticalAnchor; + if (usedAnchors & QQuickAnchors::TopAnchor) { + //Handle stretching + bool invalid = true; + qreal height = 0.0; + if (usedAnchors & QQuickAnchors::BottomAnchor) { + invalid = calcStretch(top, bottom, topMargin, -bottomMargin, QQuickAnchorLine::Top, height); + } else if (usedAnchors & QQuickAnchors::VCenterAnchor) { + invalid = calcStretch(top, vCenter, topMargin, vCenterOffset, QQuickAnchorLine::Top, height); + height *= 2; + } + if (!invalid) + setItemHeight(height); + + //Handle top + if (top.item == item->parentItem()) { + setItemY(adjustedPosition(top.item, top.anchorLine) + topMargin); + } else if (top.item->parentItem() == item->parentItem()) { + setItemY(position(top.item, top.anchorLine) + topMargin); + } + } else if (usedAnchors & QQuickAnchors::BottomAnchor) { + //Handle stretching (top + bottom case is handled above) + if (usedAnchors & QQuickAnchors::VCenterAnchor) { + qreal height = 0.0; + bool invalid = calcStretch(vCenter, bottom, vCenterOffset, -bottomMargin, + QQuickAnchorLine::Top, height); + if (!invalid) + setItemHeight(height*2); + } + + //Handle bottom + if (bottom.item == item->parentItem()) { + setItemY(adjustedPosition(bottom.item, bottom.anchorLine) - item->height() - bottomMargin); + } else if (bottom.item->parentItem() == item->parentItem()) { + setItemY(position(bottom.item, bottom.anchorLine) - item->height() - bottomMargin); + } + } else if (usedAnchors & QQuickAnchors::VCenterAnchor) { + //(stetching handled above) + + //Handle vCenter + if (vCenter.item == item->parentItem()) { + setItemY(adjustedPosition(vCenter.item, vCenter.anchorLine) + - vcenter(item) + vCenterOffset); + } else if (vCenter.item->parentItem() == item->parentItem()) { + setItemY(position(vCenter.item, vCenter.anchorLine) - vcenter(item) + vCenterOffset); + } + } else if (usedAnchors & QQuickAnchors::BaselineAnchor) { + //Handle baseline + if (baseline.item == item->parentItem()) { + setItemY(adjustedPosition(baseline.item, baseline.anchorLine) - item->baselineOffset() + baselineOffset); + } else if (baseline.item->parentItem() == item->parentItem()) { + setItemY(position(baseline.item, baseline.anchorLine) - item->baselineOffset() + baselineOffset); + } + } + --updatingVerticalAnchor; + } else { + // ### Make this certain :) + qmlInfo(item) << QQuickAnchors::tr("Possible anchor loop detected on vertical anchor."); + } +} + +inline QQuickAnchorLine::AnchorLine reverseAnchorLine(QQuickAnchorLine::AnchorLine anchorLine) +{ + if (anchorLine == QQuickAnchorLine::Left) { + return QQuickAnchorLine::Right; + } else if (anchorLine == QQuickAnchorLine::Right) { + return QQuickAnchorLine::Left; + } else { + return anchorLine; + } +} + +void QQuickAnchorsPrivate::updateHorizontalAnchors() +{ + Q_Q(QQuickAnchors); + if (fill || centerIn || !isItemComplete()) + return; + + if (updatingHorizontalAnchor < 3) { + ++updatingHorizontalAnchor; + qreal effectiveRightMargin, effectiveLeftMargin, effectiveHorizontalCenterOffset; + QQuickAnchorLine effectiveLeft, effectiveRight, effectiveHorizontalCenter; + QQuickAnchors::Anchor effectiveLeftAnchor, effectiveRightAnchor; + if (q->mirrored()) { + effectiveLeftAnchor = QQuickAnchors::RightAnchor; + effectiveRightAnchor = QQuickAnchors::LeftAnchor; + effectiveLeft.item = right.item; + effectiveLeft.anchorLine = reverseAnchorLine(right.anchorLine); + effectiveRight.item = left.item; + effectiveRight.anchorLine = reverseAnchorLine(left.anchorLine); + effectiveHorizontalCenter.item = hCenter.item; + effectiveHorizontalCenter.anchorLine = reverseAnchorLine(hCenter.anchorLine); + effectiveLeftMargin = rightMargin; + effectiveRightMargin = leftMargin; + effectiveHorizontalCenterOffset = -hCenterOffset; + } else { + effectiveLeftAnchor = QQuickAnchors::LeftAnchor; + effectiveRightAnchor = QQuickAnchors::RightAnchor; + effectiveLeft = left; + effectiveRight = right; + effectiveHorizontalCenter = hCenter; + effectiveLeftMargin = leftMargin; + effectiveRightMargin = rightMargin; + effectiveHorizontalCenterOffset = hCenterOffset; + } + + if (usedAnchors & effectiveLeftAnchor) { + //Handle stretching + bool invalid = true; + qreal width = 0.0; + if (usedAnchors & effectiveRightAnchor) { + invalid = calcStretch(effectiveLeft, effectiveRight, effectiveLeftMargin, -effectiveRightMargin, QQuickAnchorLine::Left, width); + } else if (usedAnchors & QQuickAnchors::HCenterAnchor) { + invalid = calcStretch(effectiveLeft, effectiveHorizontalCenter, effectiveLeftMargin, effectiveHorizontalCenterOffset, QQuickAnchorLine::Left, width); + width *= 2; + } + if (!invalid) + setItemWidth(width); + + //Handle left + if (effectiveLeft.item == item->parentItem()) { + setItemX(adjustedPosition(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin); + } else if (effectiveLeft.item->parentItem() == item->parentItem()) { + setItemX(position(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin); + } + } else if (usedAnchors & effectiveRightAnchor) { + //Handle stretching (left + right case is handled in updateLeftAnchor) + if (usedAnchors & QQuickAnchors::HCenterAnchor) { + qreal width = 0.0; + bool invalid = calcStretch(effectiveHorizontalCenter, effectiveRight, effectiveHorizontalCenterOffset, -effectiveRightMargin, + QQuickAnchorLine::Left, width); + if (!invalid) + setItemWidth(width*2); + } + + //Handle right + if (effectiveRight.item == item->parentItem()) { + setItemX(adjustedPosition(effectiveRight.item, effectiveRight.anchorLine) - item->width() - effectiveRightMargin); + } else if (effectiveRight.item->parentItem() == item->parentItem()) { + setItemX(position(effectiveRight.item, effectiveRight.anchorLine) - item->width() - effectiveRightMargin); + } + } else if (usedAnchors & QQuickAnchors::HCenterAnchor) { + //Handle hCenter + if (effectiveHorizontalCenter.item == item->parentItem()) { + setItemX(adjustedPosition(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset); + } else if (effectiveHorizontalCenter.item->parentItem() == item->parentItem()) { + setItemX(position(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset); + } + } + --updatingHorizontalAnchor; + } else { + // ### Make this certain :) + qmlInfo(item) << QQuickAnchors::tr("Possible anchor loop detected on horizontal anchor."); + } +} + +QQuickAnchorLine QQuickAnchors::top() const +{ + Q_D(const QQuickAnchors); + return d->top; +} + +void QQuickAnchors::setTop(const QQuickAnchorLine &edge) +{ + Q_D(QQuickAnchors); + if (!d->checkVAnchorValid(edge) || d->top == edge) + return; + + d->usedAnchors |= TopAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~TopAnchor; + return; + } + + d->remDepend(d->top.item); + d->top = edge; + d->addDepend(d->top.item); + emit topChanged(); + d->updateVerticalAnchors(); +} + +void QQuickAnchors::resetTop() +{ + Q_D(QQuickAnchors); + d->usedAnchors &= ~TopAnchor; + d->remDepend(d->top.item); + d->top = QQuickAnchorLine(); + emit topChanged(); + d->updateVerticalAnchors(); +} + +QQuickAnchorLine QQuickAnchors::bottom() const +{ + Q_D(const QQuickAnchors); + return d->bottom; +} + +void QQuickAnchors::setBottom(const QQuickAnchorLine &edge) +{ + Q_D(QQuickAnchors); + if (!d->checkVAnchorValid(edge) || d->bottom == edge) + return; + + d->usedAnchors |= BottomAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~BottomAnchor; + return; + } + + d->remDepend(d->bottom.item); + d->bottom = edge; + d->addDepend(d->bottom.item); + emit bottomChanged(); + d->updateVerticalAnchors(); +} + +void QQuickAnchors::resetBottom() +{ + Q_D(QQuickAnchors); + d->usedAnchors &= ~BottomAnchor; + d->remDepend(d->bottom.item); + d->bottom = QQuickAnchorLine(); + emit bottomChanged(); + d->updateVerticalAnchors(); +} + +QQuickAnchorLine QQuickAnchors::verticalCenter() const +{ + Q_D(const QQuickAnchors); + return d->vCenter; +} + +void QQuickAnchors::setVerticalCenter(const QQuickAnchorLine &edge) +{ + Q_D(QQuickAnchors); + if (!d->checkVAnchorValid(edge) || d->vCenter == edge) + return; + + d->usedAnchors |= VCenterAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~VCenterAnchor; + return; + } + + d->remDepend(d->vCenter.item); + d->vCenter = edge; + d->addDepend(d->vCenter.item); + emit verticalCenterChanged(); + d->updateVerticalAnchors(); +} + +void QQuickAnchors::resetVerticalCenter() +{ + Q_D(QQuickAnchors); + d->usedAnchors &= ~VCenterAnchor; + d->remDepend(d->vCenter.item); + d->vCenter = QQuickAnchorLine(); + emit verticalCenterChanged(); + d->updateVerticalAnchors(); +} + +QQuickAnchorLine QQuickAnchors::baseline() const +{ + Q_D(const QQuickAnchors); + return d->baseline; +} + +void QQuickAnchors::setBaseline(const QQuickAnchorLine &edge) +{ + Q_D(QQuickAnchors); + if (!d->checkVAnchorValid(edge) || d->baseline == edge) + return; + + d->usedAnchors |= BaselineAnchor; + + if (!d->checkVValid()) { + d->usedAnchors &= ~BaselineAnchor; + return; + } + + d->remDepend(d->baseline.item); + d->baseline = edge; + d->addDepend(d->baseline.item); + emit baselineChanged(); + d->updateVerticalAnchors(); +} + +void QQuickAnchors::resetBaseline() +{ + Q_D(QQuickAnchors); + d->usedAnchors &= ~BaselineAnchor; + d->remDepend(d->baseline.item); + d->baseline = QQuickAnchorLine(); + emit baselineChanged(); + d->updateVerticalAnchors(); +} + +QQuickAnchorLine QQuickAnchors::left() const +{ + Q_D(const QQuickAnchors); + return d->left; +} + +void QQuickAnchors::setLeft(const QQuickAnchorLine &edge) +{ + Q_D(QQuickAnchors); + if (!d->checkHAnchorValid(edge) || d->left == edge) + return; + + d->usedAnchors |= LeftAnchor; + + if (!d->checkHValid()) { + d->usedAnchors &= ~LeftAnchor; + return; + } + + d->remDepend(d->left.item); + d->left = edge; + d->addDepend(d->left.item); + emit leftChanged(); + d->updateHorizontalAnchors(); +} + +void QQuickAnchors::resetLeft() +{ + Q_D(QQuickAnchors); + d->usedAnchors &= ~LeftAnchor; + d->remDepend(d->left.item); + d->left = QQuickAnchorLine(); + emit leftChanged(); + d->updateHorizontalAnchors(); +} + +QQuickAnchorLine QQuickAnchors::right() const +{ + Q_D(const QQuickAnchors); + return d->right; +} + +void QQuickAnchors::setRight(const QQuickAnchorLine &edge) +{ + Q_D(QQuickAnchors); + if (!d->checkHAnchorValid(edge) || d->right == edge) + return; + + d->usedAnchors |= RightAnchor; + + if (!d->checkHValid()) { + d->usedAnchors &= ~RightAnchor; + return; + } + + d->remDepend(d->right.item); + d->right = edge; + d->addDepend(d->right.item); + emit rightChanged(); + d->updateHorizontalAnchors(); +} + +void QQuickAnchors::resetRight() +{ + Q_D(QQuickAnchors); + d->usedAnchors &= ~RightAnchor; + d->remDepend(d->right.item); + d->right = QQuickAnchorLine(); + emit rightChanged(); + d->updateHorizontalAnchors(); +} + +QQuickAnchorLine QQuickAnchors::horizontalCenter() const +{ + Q_D(const QQuickAnchors); + return d->hCenter; +} + +void QQuickAnchors::setHorizontalCenter(const QQuickAnchorLine &edge) +{ + Q_D(QQuickAnchors); + if (!d->checkHAnchorValid(edge) || d->hCenter == edge) + return; + + d->usedAnchors |= HCenterAnchor; + + if (!d->checkHValid()) { + d->usedAnchors &= ~HCenterAnchor; + return; + } + + d->remDepend(d->hCenter.item); + d->hCenter = edge; + d->addDepend(d->hCenter.item); + emit horizontalCenterChanged(); + d->updateHorizontalAnchors(); +} + +void QQuickAnchors::resetHorizontalCenter() +{ + Q_D(QQuickAnchors); + d->usedAnchors &= ~HCenterAnchor; + d->remDepend(d->hCenter.item); + d->hCenter = QQuickAnchorLine(); + emit horizontalCenterChanged(); + d->updateHorizontalAnchors(); +} + +qreal QQuickAnchors::leftMargin() const +{ + Q_D(const QQuickAnchors); + return d->leftMargin; +} + +void QQuickAnchors::setLeftMargin(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->leftMargin == offset) + return; + d->leftMargin = offset; + if (d->fill) + d->fillChanged(); + else + d->updateHorizontalAnchors(); + emit leftMarginChanged(); +} + +qreal QQuickAnchors::rightMargin() const +{ + Q_D(const QQuickAnchors); + return d->rightMargin; +} + +void QQuickAnchors::setRightMargin(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->rightMargin == offset) + return; + d->rightMargin = offset; + if (d->fill) + d->fillChanged(); + else + d->updateHorizontalAnchors(); + emit rightMarginChanged(); +} + +qreal QQuickAnchors::margins() const +{ + Q_D(const QQuickAnchors); + return d->margins; +} + +void QQuickAnchors::setMargins(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->margins == offset) + return; + //###Is it significantly faster to set them directly so we can call fillChanged only once? + if (!d->rightMargin || d->rightMargin == d->margins) + setRightMargin(offset); + if (!d->leftMargin || d->leftMargin == d->margins) + setLeftMargin(offset); + if (!d->topMargin || d->topMargin == d->margins) + setTopMargin(offset); + if (!d->bottomMargin || d->bottomMargin == d->margins) + setBottomMargin(offset); + d->margins = offset; + emit marginsChanged(); + +} + +qreal QQuickAnchors::horizontalCenterOffset() const +{ + Q_D(const QQuickAnchors); + return d->hCenterOffset; +} + +void QQuickAnchors::setHorizontalCenterOffset(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->hCenterOffset == offset) + return; + d->hCenterOffset = offset; + if (d->centerIn) + d->centerInChanged(); + else + d->updateHorizontalAnchors(); + emit horizontalCenterOffsetChanged(); +} + +qreal QQuickAnchors::topMargin() const +{ + Q_D(const QQuickAnchors); + return d->topMargin; +} + +void QQuickAnchors::setTopMargin(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->topMargin == offset) + return; + d->topMargin = offset; + if (d->fill) + d->fillChanged(); + else + d->updateVerticalAnchors(); + emit topMarginChanged(); +} + +qreal QQuickAnchors::bottomMargin() const +{ + Q_D(const QQuickAnchors); + return d->bottomMargin; +} + +void QQuickAnchors::setBottomMargin(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->bottomMargin == offset) + return; + d->bottomMargin = offset; + if (d->fill) + d->fillChanged(); + else + d->updateVerticalAnchors(); + emit bottomMarginChanged(); +} + +qreal QQuickAnchors::verticalCenterOffset() const +{ + Q_D(const QQuickAnchors); + return d->vCenterOffset; +} + +void QQuickAnchors::setVerticalCenterOffset(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->vCenterOffset == offset) + return; + d->vCenterOffset = offset; + if (d->centerIn) + d->centerInChanged(); + else + d->updateVerticalAnchors(); + emit verticalCenterOffsetChanged(); +} + +qreal QQuickAnchors::baselineOffset() const +{ + Q_D(const QQuickAnchors); + return d->baselineOffset; +} + +void QQuickAnchors::setBaselineOffset(qreal offset) +{ + Q_D(QQuickAnchors); + if (d->baselineOffset == offset) + return; + d->baselineOffset = offset; + d->updateVerticalAnchors(); + emit baselineOffsetChanged(); +} + +QQuickAnchors::Anchors QQuickAnchors::usedAnchors() const +{ + Q_D(const QQuickAnchors); + return d->usedAnchors; +} + +bool QQuickAnchorsPrivate::checkHValid() const +{ + if (usedAnchors & QQuickAnchors::LeftAnchor && + usedAnchors & QQuickAnchors::RightAnchor && + usedAnchors & QQuickAnchors::HCenterAnchor) { + qmlInfo(item) << QQuickAnchors::tr("Cannot specify left, right, and hcenter anchors."); + return false; + } + + return true; +} + +bool QQuickAnchorsPrivate::checkHAnchorValid(QQuickAnchorLine anchor) const +{ + if (!anchor.item) { + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor to a null item."); + return false; + } else if (anchor.anchorLine & QQuickAnchorLine::Vertical_Mask) { + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor a horizontal edge to a vertical edge."); + return false; + } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){ + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); + return false; + } else if (anchor.item == item) { + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor item to self."); + return false; + } + + return true; +} + +bool QQuickAnchorsPrivate::checkVValid() const +{ + if (usedAnchors & QQuickAnchors::TopAnchor && + usedAnchors & QQuickAnchors::BottomAnchor && + usedAnchors & QQuickAnchors::VCenterAnchor) { + qmlInfo(item) << QQuickAnchors::tr("Cannot specify top, bottom, and vcenter anchors."); + return false; + } else if (usedAnchors & QQuickAnchors::BaselineAnchor && + (usedAnchors & QQuickAnchors::TopAnchor || + usedAnchors & QQuickAnchors::BottomAnchor || + usedAnchors & QQuickAnchors::VCenterAnchor)) { + qmlInfo(item) << QQuickAnchors::tr("Baseline anchor cannot be used in conjunction with top, bottom, or vcenter anchors."); + return false; + } + + return true; +} + +bool QQuickAnchorsPrivate::checkVAnchorValid(QQuickAnchorLine anchor) const +{ + if (!anchor.item) { + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor to a null item."); + return false; + } else if (anchor.anchorLine & QQuickAnchorLine::Horizontal_Mask) { + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor a vertical edge to a horizontal edge."); + return false; + } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){ + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); + return false; + } else if (anchor.item == item){ + qmlInfo(item) << QQuickAnchors::tr("Cannot anchor item to self."); + return false; + } + + return true; +} + +QT_END_NAMESPACE + +#include + diff --git a/src/declarative/items/qquickanchors_p.h b/src/declarative/items/qquickanchors_p.h new file mode 100644 index 0000000000..5c66c2e17d --- /dev/null +++ b/src/declarative/items/qquickanchors_p.h @@ -0,0 +1,201 @@ +// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKANCHORS_P_H +#define QQUICKANCHORS_P_H + +#include + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickItem; +class QQuickAnchorsPrivate; +class QQuickAnchorLine; +class Q_DECLARATIVE_PRIVATE_EXPORT QQuickAnchors : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QQuickAnchorLine left READ left WRITE setLeft RESET resetLeft NOTIFY leftChanged) + Q_PROPERTY(QQuickAnchorLine right READ right WRITE setRight RESET resetRight NOTIFY rightChanged) + Q_PROPERTY(QQuickAnchorLine horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter NOTIFY horizontalCenterChanged) + Q_PROPERTY(QQuickAnchorLine top READ top WRITE setTop RESET resetTop NOTIFY topChanged) + Q_PROPERTY(QQuickAnchorLine bottom READ bottom WRITE setBottom RESET resetBottom NOTIFY bottomChanged) + Q_PROPERTY(QQuickAnchorLine verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter NOTIFY verticalCenterChanged) + Q_PROPERTY(QQuickAnchorLine baseline READ baseline WRITE setBaseline RESET resetBaseline NOTIFY baselineChanged) + Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) + Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged) + Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged) + Q_PROPERTY(QQuickItem *fill READ fill WRITE setFill RESET resetFill NOTIFY fillChanged) + Q_PROPERTY(QQuickItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged) + Q_PROPERTY(bool mirrored READ mirrored NOTIFY mirroredChanged) + +public: + QQuickAnchors(QQuickItem *item, QObject *parent=0); + virtual ~QQuickAnchors(); + + enum Anchor { + LeftAnchor = 0x01, + RightAnchor = 0x02, + TopAnchor = 0x04, + BottomAnchor = 0x08, + HCenterAnchor = 0x10, + VCenterAnchor = 0x20, + BaselineAnchor = 0x40, + Horizontal_Mask = LeftAnchor | RightAnchor | HCenterAnchor, + Vertical_Mask = TopAnchor | BottomAnchor | VCenterAnchor | BaselineAnchor + }; + Q_DECLARE_FLAGS(Anchors, Anchor) + + QQuickAnchorLine left() const; + void setLeft(const QQuickAnchorLine &edge); + void resetLeft(); + + QQuickAnchorLine right() const; + void setRight(const QQuickAnchorLine &edge); + void resetRight(); + + QQuickAnchorLine horizontalCenter() const; + void setHorizontalCenter(const QQuickAnchorLine &edge); + void resetHorizontalCenter(); + + QQuickAnchorLine top() const; + void setTop(const QQuickAnchorLine &edge); + void resetTop(); + + QQuickAnchorLine bottom() const; + void setBottom(const QQuickAnchorLine &edge); + void resetBottom(); + + QQuickAnchorLine verticalCenter() const; + void setVerticalCenter(const QQuickAnchorLine &edge); + void resetVerticalCenter(); + + QQuickAnchorLine baseline() const; + void setBaseline(const QQuickAnchorLine &edge); + void resetBaseline(); + + qreal leftMargin() const; + void setLeftMargin(qreal); + + qreal rightMargin() const; + void setRightMargin(qreal); + + qreal horizontalCenterOffset() const; + void setHorizontalCenterOffset(qreal); + + qreal topMargin() const; + void setTopMargin(qreal); + + qreal bottomMargin() const; + void setBottomMargin(qreal); + + qreal margins() const; + void setMargins(qreal); + + qreal verticalCenterOffset() const; + void setVerticalCenterOffset(qreal); + + qreal baselineOffset() const; + void setBaselineOffset(qreal); + + QQuickItem *fill() const; + void setFill(QQuickItem *); + void resetFill(); + + QQuickItem *centerIn() const; + void setCenterIn(QQuickItem *); + void resetCenterIn(); + + Anchors usedAnchors() const; + + bool mirrored(); + + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void leftChanged(); + void rightChanged(); + void topChanged(); + void bottomChanged(); + void verticalCenterChanged(); + void horizontalCenterChanged(); + void baselineChanged(); + void fillChanged(); + void centerInChanged(); + void leftMarginChanged(); + void rightMarginChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void marginsChanged(); + void verticalCenterOffsetChanged(); + void horizontalCenterOffsetChanged(); + void baselineOffsetChanged(); + void mirroredChanged(); + +private: + friend class QQuickItemPrivate; + Q_DISABLE_COPY(QQuickAnchors) + Q_DECLARE_PRIVATE(QQuickAnchors) +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAnchors::Anchors) + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickAnchors) + +QT_END_HEADER + +#endif // QQUICKANCHORS_P_H diff --git a/src/declarative/items/qquickanchors_p_p.h b/src/declarative/items/qquickanchors_p_p.h new file mode 100644 index 0000000000..eae7981617 --- /dev/null +++ b/src/declarative/items/qquickanchors_p_p.h @@ -0,0 +1,173 @@ +// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKANCHORS_P_P_H +#define QQUICKANCHORS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickanchors_p.h" +#include "qquickitemchangelistener_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QQuickAnchorLine +{ +public: + QQuickAnchorLine() : item(0), anchorLine(Invalid) {} + + enum AnchorLine { + Invalid = 0x0, + Left = 0x01, + Right = 0x02, + Top = 0x04, + Bottom = 0x08, + HCenter = 0x10, + VCenter = 0x20, + Baseline = 0x40, + Horizontal_Mask = Left | Right | HCenter, + Vertical_Mask = Top | Bottom | VCenter | Baseline + }; + + QQuickItem *item; + AnchorLine anchorLine; +}; + +inline bool operator==(const QQuickAnchorLine& a, const QQuickAnchorLine& b) +{ + return a.item == b.item && a.anchorLine == b.anchorLine; +} + +class QQuickAnchorsPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickAnchors) +public: + QQuickAnchorsPrivate(QQuickItem *i) + : componentComplete(true), updatingMe(false), updatingHorizontalAnchor(0), + updatingVerticalAnchor(0), updatingFill(0), updatingCenterIn(0), item(i), usedAnchors(0), fill(0), + centerIn(0), leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), + margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0) + { + } + + void clearItem(QQuickItem *); + + void addDepend(QQuickItem *); + void remDepend(QQuickItem *); + bool isItemComplete() const; + + bool componentComplete:1; + bool updatingMe:1; + uint updatingHorizontalAnchor:2; + uint updatingVerticalAnchor:2; + uint updatingFill:2; + uint updatingCenterIn:2; + + void setItemHeight(qreal); + void setItemWidth(qreal); + void setItemX(qreal); + void setItemY(qreal); + void setItemPos(const QPointF &); + void setItemSize(const QSizeF &); + + void updateOnComplete(); + void updateMe(); + + // QQuickItemGeometryListener interface + void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &); + QQuickAnchorsPrivate *anchorPrivate() { return this; } + + bool checkHValid() const; + bool checkVValid() const; + bool checkHAnchorValid(QQuickAnchorLine anchor) const; + bool checkVAnchorValid(QQuickAnchorLine anchor) const; + bool calcStretch(const QQuickAnchorLine &edge1, const QQuickAnchorLine &edge2, qreal offset1, qreal offset2, QQuickAnchorLine::AnchorLine line, qreal &stretch); + + bool isMirrored() const; + void updateHorizontalAnchors(); + void updateVerticalAnchors(); + void fillChanged(); + void centerInChanged(); + + QQuickItem *item; + QQuickAnchors::Anchors usedAnchors; + + QQuickItem *fill; + QQuickItem *centerIn; + + QQuickAnchorLine left; + QQuickAnchorLine right; + QQuickAnchorLine top; + QQuickAnchorLine bottom; + QQuickAnchorLine vCenter; + QQuickAnchorLine hCenter; + QQuickAnchorLine baseline; + + qreal leftMargin; + qreal rightMargin; + qreal topMargin; + qreal bottomMargin; + qreal margins; + qreal vCenterOffset; + qreal hCenterOffset; + qreal baselineOffset; + + static inline QQuickAnchorsPrivate *get(QQuickAnchors *o) { + return static_cast(QObjectPrivate::get(o)); + } +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQuickAnchorLine) + +#endif diff --git a/src/declarative/items/qquickanimatedimage.cpp b/src/declarative/items/qquickanimatedimage.cpp new file mode 100644 index 0000000000..d4b08dd4f4 --- /dev/null +++ b/src/declarative/items/qquickanimatedimage.cpp @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickanimatedimage_p.h" +#include "qquickanimatedimage_p_p.h" + +#ifndef QT_NO_MOVIE + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +/*! + \qmlclass AnimatedImage QQuickAnimatedImage + \inqmlmodule QtQuick 2 + \inherits Image + \ingroup basic-visual-elements + + The AnimatedImage element extends the features of the \l Image element, providing + a way to play animations stored as images containing a series of frames, + such as those stored in GIF files. + + Information about the current frame and totla length of the animation can be + obtained using the \l currentFrame and \l frameCount properties. You can + start, pause and stop the animation by changing the values of the \l playing + and \l paused properties. + + The full list of supported formats can be determined with QMovie::supportedFormats(). + + \section1 Example Usage + + \beginfloatleft + \image animatedimageitem.gif + \endfloat + + The following QML shows how to display an animated image and obtain information + about its state, such as the current frame and total number of frames. + The result is an animated image with a simple progress indicator underneath it. + + \bold Note: Unlike images, animated images are not cached or shared internally. + + \clearfloat + \snippet doc/src/snippets/declarative/animatedimage.qml document + + \sa BorderImage, Image +*/ + +/*! + \qmlproperty url QtQuick2::AnimatedImage::source + + This property holds the URL that refers to the source image. + + AnimatedImage can handle any image format supported by Qt, loaded from any + URL scheme supported by Qt. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty bool QtQuick2::AnimatedImage::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ + +/*! + \qmlproperty bool QtQuick2::AnimatedImage::mirror + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + +QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent) + : QQuickImage(*(new QQuickAnimatedImagePrivate), parent) +{ +} + +QQuickAnimatedImage::~QQuickAnimatedImage() +{ + Q_D(QQuickAnimatedImage); + delete d->_movie; +} + +/*! + \qmlproperty bool QtQuick2::AnimatedImage::paused + This property holds whether the animated image is paused. + + By default, this property is false. Set it to true when you want to pause + the animation. +*/ + +bool QQuickAnimatedImage::isPaused() const +{ + Q_D(const QQuickAnimatedImage); + if (!d->_movie) + return false; + return d->_movie->state()==QMovie::Paused; +} + +void QQuickAnimatedImage::setPaused(bool pause) +{ + Q_D(QQuickAnimatedImage); + if (pause == d->paused) + return; + d->paused = pause; + if (!d->_movie) + return; + d->_movie->setPaused(pause); +} + +/*! + \qmlproperty bool QtQuick2::AnimatedImage::playing + This property holds whether the animated image is playing. + + By default, this property is true, meaning that the animation + will start playing immediately. +*/ + +bool QQuickAnimatedImage::isPlaying() const +{ + Q_D(const QQuickAnimatedImage); + if (!d->_movie) + return false; + return d->_movie->state()!=QMovie::NotRunning; +} + +void QQuickAnimatedImage::setPlaying(bool play) +{ + Q_D(QQuickAnimatedImage); + if (play == d->playing) + return; + d->playing = play; + if (!d->_movie) + return; + if (play) + d->_movie->start(); + else + d->_movie->stop(); +} + +/*! + \qmlproperty int QtQuick2::AnimatedImage::currentFrame + \qmlproperty int QtQuick2::AnimatedImage::frameCount + + currentFrame is the frame that is currently visible. By monitoring this property + for changes, you can animate other items at the same time as the image. + + frameCount is the number of frames in the animation. For some animation formats, + frameCount is unknown and has a value of zero. +*/ +int QQuickAnimatedImage::currentFrame() const +{ + Q_D(const QQuickAnimatedImage); + if (!d->_movie) + return d->preset_currentframe; + return d->_movie->currentFrameNumber(); +} + +void QQuickAnimatedImage::setCurrentFrame(int frame) +{ + Q_D(QQuickAnimatedImage); + if (!d->_movie) { + d->preset_currentframe = frame; + return; + } + d->_movie->jumpToFrame(frame); +} + +int QQuickAnimatedImage::frameCount() const +{ + Q_D(const QQuickAnimatedImage); + if (!d->_movie) + return 0; + return d->_movie->frameCount(); +} + +void QQuickAnimatedImage::setSource(const QUrl &url) +{ + Q_D(QQuickAnimatedImage); + if (url == d->url) + return; + + delete d->_movie; + d->_movie = 0; + + if (d->reply) { + d->reply->deleteLater(); + d->reply = 0; + } + + d->url = url; + emit sourceChanged(d->url); + + if (isComponentComplete()) + load(); +} + +void QQuickAnimatedImage::load() +{ + Q_D(QQuickAnimatedImage); + + QQuickImageBase::Status oldStatus = d->status; + qreal oldProgress = d->progress; + + if (d->url.isEmpty()) { + delete d->_movie; + d->setPixmap(QPixmap()); + d->progress = 0; + d->status = Null; + if (d->status != oldStatus) + emit statusChanged(d->status); + if (d->progress != oldProgress) + emit progressChanged(d->progress); + } else { + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!lf.isEmpty()) { + //### should be unified with movieRequestFinished + d->_movie = new QMovie(lf); + if (!d->_movie->isValid()){ + qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString(); + delete d->_movie; + d->_movie = 0; + d->status = Error; + if (d->status != oldStatus) + emit statusChanged(d->status); + return; + } + connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), + this, SLOT(playingStatusChanged())); + connect(d->_movie, SIGNAL(frameChanged(int)), + this, SLOT(movieUpdate())); + d->_movie->setCacheMode(QMovie::CacheAll); + if (d->playing) + d->_movie->start(); + else + d->_movie->jumpToFrame(0); + if (d->paused) + d->_movie->setPaused(true); + d->setPixmap(d->_movie->currentPixmap()); + d->status = Ready; + d->progress = 1.0; + if (d->status != oldStatus) + emit statusChanged(d->status); + if (d->progress != oldProgress) + emit progressChanged(d->progress); + return; + } + + d->status = Loading; + d->progress = 0; + emit statusChanged(d->status); + emit progressChanged(d->progress); + QNetworkRequest req(d->url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + d->reply = qmlEngine(this)->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), + this, SLOT(movieRequestFinished())); + QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(requestProgress(qint64,qint64))); + } +} + +#define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16 + +void QQuickAnimatedImage::movieRequestFinished() +{ + Q_D(QQuickAnimatedImage); + + d->redirectCount++; + if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->reply->url().resolved(redirect.toUrl()); + d->reply->deleteLater(); + d->reply = 0; + setSource(url); + return; + } + } + d->redirectCount=0; + + d->_movie = new QMovie(d->reply); + if (!d->_movie->isValid()){ +#ifndef QT_NO_DEBUG_STREAM + qmlInfo(this) << "Error Reading Animated Image File " << d->url; +#endif + delete d->_movie; + d->_movie = 0; + d->status = Error; + emit statusChanged(d->status); + return; + } + connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), + this, SLOT(playingStatusChanged())); + connect(d->_movie, SIGNAL(frameChanged(int)), + this, SLOT(movieUpdate())); + d->_movie->setCacheMode(QMovie::CacheAll); + if (d->playing) + d->_movie->start(); + if (d->paused || !d->playing) { + d->_movie->jumpToFrame(d->preset_currentframe); + d->preset_currentframe = 0; + } + if (d->paused) + d->_movie->setPaused(true); + d->setPixmap(d->_movie->currentPixmap()); + d->status = Ready; + emit statusChanged(d->status); +} + +void QQuickAnimatedImage::movieUpdate() +{ + Q_D(QQuickAnimatedImage); + d->setPixmap(d->_movie->currentPixmap()); + emit frameChanged(); +} + +void QQuickAnimatedImage::playingStatusChanged() +{ + Q_D(QQuickAnimatedImage); + if ((d->_movie->state() != QMovie::NotRunning) != d->playing) { + d->playing = (d->_movie->state() != QMovie::NotRunning); + emit playingChanged(); + } + if ((d->_movie->state() == QMovie::Paused) != d->paused) { + d->playing = (d->_movie->state() == QMovie::Paused); + emit pausedChanged(); + } +} + +void QQuickAnimatedImage::componentComplete() +{ + Q_D(QQuickAnimatedImage); + QQuickItem::componentComplete(); // NOT QQuickImage + if (d->url.isValid()) + load(); + if (!d->reply) { + setCurrentFrame(d->preset_currentframe); + d->preset_currentframe = 0; + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_MOVIE diff --git a/src/declarative/items/qquickanimatedimage_p.h b/src/declarative/items/qquickanimatedimage_p.h new file mode 100644 index 0000000000..ae96e4cc15 --- /dev/null +++ b/src/declarative/items/qquickanimatedimage_p.h @@ -0,0 +1,117 @@ +// Commit: 80d0fe9cbd92288a08d5ced8767f1edb651dae37 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKANIMATEDIMAGE_P_H +#define QQUICKANIMATEDIMAGE_P_H + +#include "qquickimage_p.h" + +#ifndef QT_NO_MOVIE + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QMovie; +class QQuickAnimatedImagePrivate; + +class Q_AUTOTEST_EXPORT QQuickAnimatedImage : public QQuickImage +{ + Q_OBJECT + + Q_PROPERTY(bool playing READ isPlaying WRITE setPlaying NOTIFY playingChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY frameChanged) + Q_PROPERTY(int frameCount READ frameCount) + + // read-only for AnimatedImage + Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) + +public: + QQuickAnimatedImage(QQuickItem *parent=0); + ~QQuickAnimatedImage(); + + bool isPlaying() const; + void setPlaying(bool play); + + bool isPaused() const; + void setPaused(bool pause); + + int currentFrame() const; + void setCurrentFrame(int frame); + + int frameCount() const; + + // Extends QQuickImage's src property*/ + virtual void setSource(const QUrl&); + +Q_SIGNALS: + void playingChanged(); + void pausedChanged(); + void frameChanged(); + void sourceSizeChanged(); + +private Q_SLOTS: + void movieUpdate(); + void movieRequestFinished(); + void playingStatusChanged(); + +protected: + virtual void load(); + void componentComplete(); + +private: + Q_DISABLE_COPY(QQuickAnimatedImage) + Q_DECLARE_PRIVATE(QQuickAnimatedImage) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickAnimatedImage) + +QT_END_HEADER + +#endif // QT_NO_MOVIE + +#endif // QQUICKANIMATEDIMAGE_P_H diff --git a/src/declarative/items/qquickanimatedimage_p_p.h b/src/declarative/items/qquickanimatedimage_p_p.h new file mode 100644 index 0000000000..0d5dd4a3b7 --- /dev/null +++ b/src/declarative/items/qquickanimatedimage_p_p.h @@ -0,0 +1,88 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKANIMATEDIMAGE_P_P_H +#define QQUICKANIMATEDIMAGE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickimage_p_p.h" + +#ifndef QT_NO_MOVIE + +QT_BEGIN_NAMESPACE + +class QMovie; +class QNetworkReply; + +class QQuickAnimatedImagePrivate : public QQuickImagePrivate +{ + Q_DECLARE_PUBLIC(QQuickAnimatedImage) + +public: + QQuickAnimatedImagePrivate() + : playing(true), paused(false), preset_currentframe(0), _movie(0), reply(0), redirectCount(0) + { + } + + bool playing; + bool paused; + int preset_currentframe; + QMovie *_movie; + QNetworkReply *reply; + int redirectCount; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_MOVIE + +#endif // QQUICKANIMATEDIMAGE_P_P_H diff --git a/src/declarative/items/qquickanimation.cpp b/src/declarative/items/qquickanimation.cpp new file mode 100644 index 0000000000..55eb4e2e13 --- /dev/null +++ b/src/declarative/items/qquickanimation.cpp @@ -0,0 +1,792 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickanimation_p.h" +#include "qquickanimation_p_p.h" +#include "qquickstateoperations_p.h" + +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickParentAnimation::QQuickParentAnimation(QObject *parent) + : QDeclarativeAnimationGroup(*(new QQuickParentAnimationPrivate), parent) +{ + Q_D(QQuickParentAnimation); + d->topLevelGroup = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->topLevelGroup, this); + + d->startAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->startAction); + + d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->ag); + + d->endAction = new QActionAnimation; + QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->endAction); +} + +QQuickParentAnimation::~QQuickParentAnimation() +{ +} + +QQuickItem *QQuickParentAnimation::target() const +{ + Q_D(const QQuickParentAnimation); + return d->target; +} + +void QQuickParentAnimation::setTarget(QQuickItem *target) +{ + Q_D(QQuickParentAnimation); + if (target == d->target) + return; + + d->target = target; + emit targetChanged(); +} + +QQuickItem *QQuickParentAnimation::newParent() const +{ + Q_D(const QQuickParentAnimation); + return d->newParent; +} + +void QQuickParentAnimation::setNewParent(QQuickItem *newParent) +{ + Q_D(QQuickParentAnimation); + if (newParent == d->newParent) + return; + + d->newParent = newParent; + emit newParentChanged(); +} + +QQuickItem *QQuickParentAnimation::via() const +{ + Q_D(const QQuickParentAnimation); + return d->via; +} + +void QQuickParentAnimation::setVia(QQuickItem *via) +{ + Q_D(QQuickParentAnimation); + if (via == d->via) + return; + + d->via = via; + emit viaChanged(); +} + +//### mirrors same-named function in QQuickItem +QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const +{ + switch (origin) { + default: + case QQuickItem::TopLeft: + return QPointF(0, 0); + case QQuickItem::Top: + return QPointF(width / 2., 0); + case QQuickItem::TopRight: + return QPointF(width, 0); + case QQuickItem::Left: + return QPointF(0, height / 2.); + case QQuickItem::Center: + return QPointF(width / 2., height / 2.); + case QQuickItem::Right: + return QPointF(width, height / 2.); + case QQuickItem::BottomLeft: + return QPointF(0, height); + case QQuickItem::Bottom: + return QPointF(width / 2., height); + case QQuickItem::BottomRight: + return QPointF(width, height); + } +} + +void QQuickParentAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QQuickParentAnimation); + + struct QQuickParentAnimationData : public QAbstractAnimationAction + { + QQuickParentAnimationData() {} + ~QQuickParentAnimationData() { qDeleteAll(pc); } + + QDeclarativeStateActions actions; + //### reverse should probably apply on a per-action basis + bool reverse; + QList pc; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarativeAction &action = actions.at(ii); + if (reverse) + action.event->reverse(); + else + action.event->execute(); + } + } + }; + + QQuickParentAnimationData *data = new QQuickParentAnimationData; + QQuickParentAnimationData *viaData = new QQuickParentAnimationData; + + bool hasExplicit = false; + if (d->target && d->newParent) { + data->reverse = false; + QDeclarativeAction myAction; + QQuickParentChange *pc = new QQuickParentChange; + pc->setObject(d->target); + pc->setParent(d->newParent); + myAction.event = pc; + data->pc << pc; + data->actions << myAction; + hasExplicit = true; + if (d->via) { + viaData->reverse = false; + QDeclarativeAction myVAction; + QQuickParentChange *vpc = new QQuickParentChange; + vpc->setObject(d->target); + vpc->setParent(d->via); + myVAction.event = vpc; + viaData->pc << vpc; + viaData->actions << myVAction; + } + //### once actions have concept of modified, + // loop to match appropriate ParentChanges and mark as modified + } + + if (!hasExplicit) + for (int i = 0; i < actions.size(); ++i) { + QDeclarativeAction &action = actions[i]; + if (action.event && action.event->typeName() == QLatin1String("ParentChange") + && (!d->target || static_cast(action.event)->object() == d->target)) { + + QQuickParentChange *pc = static_cast(action.event); + QDeclarativeAction myAction = action; + data->reverse = action.reverseEvent; + + //### this logic differs from PropertyAnimation + // (probably a result of modified vs. done) + if (d->newParent) { + QQuickParentChange *epc = new QQuickParentChange; + epc->setObject(static_cast(action.event)->object()); + epc->setParent(d->newParent); + myAction.event = epc; + data->pc << epc; + data->actions << myAction; + pc = epc; + } else { + action.actionDone = true; + data->actions << myAction; + } + + if (d->via) { + viaData->reverse = false; + QDeclarativeAction myAction; + QQuickParentChange *vpc = new QQuickParentChange; + vpc->setObject(pc->object()); + vpc->setParent(d->via); + myAction.event = vpc; + viaData->pc << vpc; + viaData->actions << myAction; + QDeclarativeAction dummyAction; + QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QQuickItem *target = pc->object(); + QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent(); + + //### this mirrors the logic in QQuickParentChange. + bool ok; + const QTransform &transform = targetParent->itemTransform(d->via, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform"); + ok = false; + } + + qreal scale = 1; + qreal rotation = 0; + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { + if (transform.m11() == transform.m22()) + scale = transform.m11(); + else { + qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + } else if (ok && isRotate) { + if (transform.m11() == transform.m22()) + scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); + else { + qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + + if (scale != 0) + rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; + else { + qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0"); + ok = false; + } + } + + const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal())); + qreal x = point.x(); + qreal y = point.y(); + if (ok && target->transformOrigin() != QQuickItem::TopLeft) { + qreal w = target->width(); + qreal h = target->height(); + if (pc->widthIsSet() && i < actions.size() - 1) + w = actions[++i].toValue.toReal(); + if (pc->heightIsSet() && i < actions.size() - 1) + h = actions[++i].toValue.toReal(); + const QPointF &transformOrigin + = d->computeTransformOrigin(target->transformOrigin(), w,h); + qreal tempxt = transformOrigin.x(); + qreal tempyt = transformOrigin.y(); + QTransform t; + t.translate(-tempxt, -tempyt); + t.rotate(rotation); + t.scale(scale, scale); + t.translate(tempxt, tempyt); + const QPointF &offset = t.map(QPointF(0,0)); + x += offset.x(); + y += offset.y(); + } + + if (ok) { + //qDebug() << x << y << rotation << scale; + xAction.toValue = x; + yAction.toValue = y; + sAction.toValue = sAction.toValue.toReal() * scale; + rAction.toValue = rAction.toValue.toReal() + rotation; + } + } + } + } + + if (data->actions.count()) { + if (direction == QDeclarativeAbstractAnimation::Forward) { + d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); + d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); + } else { + d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); + d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); + } + } else { + delete data; + delete viaData; + } + + //take care of any child animations + bool valid = d->defaultProperty.isValid(); + for (int ii = 0; ii < d->animations.count(); ++ii) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } + +} + +QAbstractAnimation *QQuickParentAnimation::qtAnimation() +{ + Q_D(QQuickParentAnimation); + return d->topLevelGroup; +} + +QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent) +{ + Q_D(QQuickAnchorAnimation); + d->va = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(d->va, this); +} + +QQuickAnchorAnimation::~QQuickAnchorAnimation() +{ +} + +QAbstractAnimation *QQuickAnchorAnimation::qtAnimation() +{ + Q_D(QQuickAnchorAnimation); + return d->va; +} + +QDeclarativeListProperty QQuickAnchorAnimation::targets() +{ + Q_D(QQuickAnchorAnimation); + return QDeclarativeListProperty(this, d->targets); +} + +int QQuickAnchorAnimation::duration() const +{ + Q_D(const QQuickAnchorAnimation); + return d->va->duration(); +} + +void QQuickAnchorAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QQuickAnchorAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +QEasingCurve QQuickAnchorAnimation::easing() const +{ + Q_D(const QQuickAnchorAnimation); + return d->va->easingCurve(); +} + +void QQuickAnchorAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QQuickAnchorAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(modified); + Q_D(QQuickAnchorAnimation); + QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; + data->interpolatorType = QMetaType::QReal; + data->interpolator = d->interpolator; + + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = false; + + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarativeAction &action = actions[ii]; + if (action.event && action.event->typeName() == QLatin1String("AnchorChanges") + && (d->targets.isEmpty() || d->targets.contains(static_cast(action.event)->object()))) { + data->actions << static_cast(action.event)->additionalActions(); + } + } + + if (data->actions.count()) { + if (!d->rangeIsSet) { + d->va->setStartValue(qreal(0)); + d->va->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + d->va->setFromSourcedValue(&data->fromSourced); + } else { + delete data; + } +} + +QQuickPathAnimation::QQuickPathAnimation(QObject *parent) +: QDeclarativeAbstractAnimation(*(new QQuickPathAnimationPrivate), parent) +{ + Q_D(QQuickPathAnimation); + d->pa = new QDeclarativeBulkValueAnimator; + QDeclarative_setParent_noEvent(d->pa, this); +} + +QQuickPathAnimation::~QQuickPathAnimation() +{ +} + +int QQuickPathAnimation::duration() const +{ + Q_D(const QQuickPathAnimation); + return d->pa->duration(); +} + +void QQuickPathAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QQuickPathAnimation); + if (d->pa->duration() == duration) + return; + d->pa->setDuration(duration); + emit durationChanged(duration); +} + +QEasingCurve QQuickPathAnimation::easing() const +{ + Q_D(const QQuickPathAnimation); + return d->pa->easingCurve(); +} + +void QQuickPathAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QQuickPathAnimation); + if (d->pa->easingCurve() == e) + return; + + d->pa->setEasingCurve(e); + emit easingChanged(e); +} + +QDeclarativePath *QQuickPathAnimation::path() const +{ + Q_D(const QQuickPathAnimation); + return d->path; +} + +void QQuickPathAnimation::setPath(QDeclarativePath *path) +{ + Q_D(QQuickPathAnimation); + if (d->path == path) + return; + + d->path = path; + emit pathChanged(); +} + +QQuickItem *QQuickPathAnimation::target() const +{ + Q_D(const QQuickPathAnimation); + return d->target; +} + +void QQuickPathAnimation::setTarget(QQuickItem *target) +{ + Q_D(QQuickPathAnimation); + if (d->target == target) + return; + + d->target = target; + emit targetChanged(); +} + +QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const +{ + Q_D(const QQuickPathAnimation); + return d->orientation; +} + +void QQuickPathAnimation::setOrientation(Orientation orientation) +{ + Q_D(QQuickPathAnimation); + if (d->orientation == orientation) + return; + + d->orientation = orientation; + emit orientationChanged(d->orientation); +} + +QPointF QQuickPathAnimation::anchorPoint() const +{ + Q_D(const QQuickPathAnimation); + return d->anchorPoint; +} + +void QQuickPathAnimation::setAnchorPoint(const QPointF &point) +{ + Q_D(QQuickPathAnimation); + if (d->anchorPoint == point) + return; + + d->anchorPoint = point; + emit anchorPointChanged(point); +} + +qreal QQuickPathAnimation::orientationEntryInterval() const +{ + Q_D(const QQuickPathAnimation); + return d->entryInterval; +} + +void QQuickPathAnimation::setOrientationEntryInterval(qreal interval) +{ + Q_D(QQuickPathAnimation); + if (d->entryInterval == interval) + return; + d->entryInterval = interval; + emit orientationEntryIntervalChanged(interval); +} + +qreal QQuickPathAnimation::orientationExitInterval() const +{ + Q_D(const QQuickPathAnimation); + return d->exitInterval; +} + +void QQuickPathAnimation::setOrientationExitInterval(qreal interval) +{ + Q_D(QQuickPathAnimation); + if (d->exitInterval == interval) + return; + d->exitInterval = interval; + emit orientationExitIntervalChanged(interval); +} + +qreal QQuickPathAnimation::endRotation() const +{ + Q_D(const QQuickPathAnimation); + return d->endRotation.isNull ? qreal(0) : d->endRotation.value; +} + +void QQuickPathAnimation::setEndRotation(qreal rotation) +{ + Q_D(QQuickPathAnimation); + if (!d->endRotation.isNull && d->endRotation == rotation) + return; + + d->endRotation = rotation; + emit endRotationChanged(d->endRotation); +} + + +QAbstractAnimation *QQuickPathAnimation::qtAnimation() +{ + Q_D(QQuickPathAnimation); + return d->pa; +} + +void QQuickPathAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QQuickPathAnimation); + QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater; + + data->orientation = d->orientation; + data->anchorPoint = d->anchorPoint; + data->entryInterval = d->entryInterval; + data->exitInterval = d->exitInterval; + data->endRotation = d->endRotation; + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false; + data->toDefined = d->path ? d->path->hasEnd() : false; + int origModifiedSize = modified.count(); + + for (int i = 0; i < actions.count(); ++i) { + QDeclarativeAction &action = actions[i]; + if (action.event) + continue; + if (action.specifiedObject == d->target && action.property.name() == QLatin1String("x")) { + data->toX = action.toValue.toReal(); + modified << action.property; + action.fromValue = action.toValue; + } + if (action.specifiedObject == d->target && action.property.name() == QLatin1String("y")) { + data->toY = action.toValue.toReal(); + modified << action.property; + action.fromValue = action.toValue; + } + } + + if (d->target && d->path && + (modified.count() > origModifiedSize || data->toDefined)) { + data->target = d->target; + data->path = d->path; + if (!d->rangeIsSet) { + d->pa->setStartValue(qreal(0)); + d->pa->setEndValue(qreal(1)); + d->rangeIsSet = true; + } + /* + NOTE: The following block relies on the fact that the previous value hasn't + yet been deleted, and has the same target, etc, which may be a bit fragile. + */ + if (d->pa->getAnimValue()) { + QQuickPathAnimationUpdater *prevData = static_cast(d->pa->getAnimValue()); + + // get the original start angle that was used (so we can exactly reverse). + data->startRotation = prevData->startRotation; + + // treat interruptions specially, otherwise we end up with strange paths + if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) { + if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) { + QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV); + if (!prevData->anchorPoint.isNull()) + pathPos -= prevData->anchorPoint; + if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself + data->painterPath = prevData->painterPath; + data->toDefined = data->fromDefined = data->fromSourced = true; + data->prevBez.isValid = false; + data->interruptStart = prevData->currentV; + data->startRotation = prevData->startRotation; + data->pathLength = prevData->pathLength; + data->attributePoints = prevData->attributePoints; + } + } + } + } + d->pa->setFromSourcedValue(&data->fromSourced); + d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); + } else { + d->pa->setFromSourcedValue(0); + d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped); + delete data; + } +} + +void QQuickPathAnimationUpdater::setValue(qreal v) +{ + if (interruptStart.isValid()) { + if (reverse) + v = 1 - v; + qreal end = reverse ? 0.0 : 1.0; + v = interruptStart + v * (end-interruptStart); + } + currentV = v; + bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0)); + if (!fromSourced && (!fromDefined || !toDefined)) { + qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x(); + qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y(); + qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x(); + qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y(); + + prevBez.isValid = false; + painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints); + fromSourced = true; + } + + qreal angle; + bool fixed = orientation == QQuickPathAnimation::Fixed; + QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle); + + //adjust position according to anchor point + if (!anchorPoint.isNull()) { + currentPos -= anchorPoint; + if (atStart) { + if (!anchorPoint.isNull() && !fixed) + target->setTransformOriginPoint(anchorPoint); + } + } + + //### could cache properties rather than reconstructing each time + QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("x")), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("y")), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + + //adjust angle according to orientation + if (!fixed) { + switch (orientation) { + case QQuickPathAnimation::RightFirst: + angle = -angle; + break; + case QQuickPathAnimation::TopFirst: + angle = -angle + 90; + break; + case QQuickPathAnimation::LeftFirst: + angle = -angle + 180; + break; + case QQuickPathAnimation::BottomFirst: + angle = -angle + 270; + break; + default: + angle = 0; + break; + } + + if (atStart && !reverse) { + startRotation = target->rotation(); + + //shortest distance to correct orientation + qreal diff = angle - startRotation; + while (diff > 180.0) { + startRotation.value += 360.0; + diff -= 360.0; + } + while (diff < -180.0) { + startRotation.value -= 360.0; + diff += 360.0; + } + } + + //smoothly transition to the desired orientation + if (startRotation.isValid()) { + if (reverse && v == 0.0) + angle = startRotation; + else if (v < entryInterval) + angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval; + } + if (endRotation.isValid()) { + qreal exitStart = 1 - exitInterval; + if (!reverse && v == 1.0) + angle = endRotation; + else if (v > exitStart) + angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval; + } + QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("rotation")), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + + /* + NOTE: we don't always reset the transform origin, as it can cause a + visual jump if ending on an angle. This means that in some cases + (anchor point and orientation both specified, and ending at an angle) + the transform origin will always be set after running the path animation. + */ + if ((reverse && v == 0.0) || (!reverse && v == 1.0)) { + if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle)) + target->setTransformOriginPoint(QPointF()); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickanimation_p.h b/src/declarative/items/qquickanimation_p.h new file mode 100644 index 0000000000..2ce72249e2 --- /dev/null +++ b/src/declarative/items/qquickanimation_p.h @@ -0,0 +1,209 @@ +// Commit: e39a2e39451bf106a9845f8a60fc571faaa4dde5 +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKANIMATION_H +#define QQUICKANIMATION_H + +#include "qquickitem.h" + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickParentAnimationPrivate; +class QQuickParentAnimation : public QDeclarativeAnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickParentAnimation) + + Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QQuickItem *newParent READ newParent WRITE setNewParent NOTIFY newParentChanged) + Q_PROPERTY(QQuickItem *via READ via WRITE setVia NOTIFY viaChanged) + +public: + QQuickParentAnimation(QObject *parent=0); + virtual ~QQuickParentAnimation(); + + QQuickItem *target() const; + void setTarget(QQuickItem *); + + QQuickItem *newParent() const; + void setNewParent(QQuickItem *); + + QQuickItem *via() const; + void setVia(QQuickItem *); + +Q_SIGNALS: + void targetChanged(); + void newParentChanged(); + void viaChanged(); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QQuickAnchorAnimationPrivate; +class QQuickAnchorAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickAnchorAnimation) + Q_PROPERTY(QDeclarativeListProperty targets READ targets) + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + +public: + QQuickAnchorAnimation(QObject *parent=0); + virtual ~QQuickAnchorAnimation(); + + QDeclarativeListProperty targets(); + + int duration() const; + void setDuration(int); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + +Q_SIGNALS: + void durationChanged(int); + void easingChanged(const QEasingCurve&); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QQuickItem; +class QDeclarativePath; +class QQuickPathAnimationPrivate; +class Q_AUTOTEST_EXPORT QQuickPathAnimation : public QDeclarativeAbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickPathAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) + Q_PROPERTY(QPointF anchorPoint READ anchorPoint WRITE setAnchorPoint NOTIFY anchorPointChanged) + Q_PROPERTY(qreal orientationEntryInterval READ orientationEntryInterval WRITE setOrientationEntryInterval NOTIFY orientationEntryIntervalChanged) + Q_PROPERTY(qreal orientationExitInterval READ orientationExitInterval WRITE setOrientationExitInterval NOTIFY orientationExitIntervalChanged) + Q_PROPERTY(qreal endRotation READ endRotation WRITE setEndRotation NOTIFY endRotationChanged) + +public: + QQuickPathAnimation(QObject *parent=0); + virtual ~QQuickPathAnimation(); + + enum Orientation { + Fixed, + RightFirst, + LeftFirst, + BottomFirst, + TopFirst + }; + Q_ENUMS(Orientation) + + int duration() const; + void setDuration(int); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + + QDeclarativePath *path() const; + void setPath(QDeclarativePath *); + + QQuickItem *target() const; + void setTarget(QQuickItem *); + + Orientation orientation() const; + void setOrientation(Orientation orientation); + + QPointF anchorPoint() const; + void setAnchorPoint(const QPointF &point); + + qreal orientationEntryInterval() const; + void setOrientationEntryInterval(qreal); + + qreal orientationExitInterval() const; + void setOrientationExitInterval(qreal); + + qreal endRotation() const; + void setEndRotation(qreal); + +protected: + virtual void transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void durationChanged(int); + void easingChanged(const QEasingCurve &); + void pathChanged(); + void targetChanged(); + void orientationChanged(Orientation); + void anchorPointChanged(const QPointF &); + void orientationEntryIntervalChanged(qreal); + void orientationExitIntervalChanged(qreal); + void endRotationChanged(qreal); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickParentAnimation) +QML_DECLARE_TYPE(QQuickAnchorAnimation) +QML_DECLARE_TYPE(QQuickPathAnimation) + +QT_END_HEADER + +#endif // QQUICKANIMATION_H diff --git a/src/declarative/items/qquickanimation_p_p.h b/src/declarative/items/qquickanimation_p_p.h new file mode 100644 index 0000000000..de2b6d42f9 --- /dev/null +++ b/src/declarative/items/qquickanimation_p_p.h @@ -0,0 +1,153 @@ +// Commit: 0ade09152067324f74678f2de4d447b6e0280600 +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKANIMATION_P_H +#define QQUICKANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickanimation_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickParentAnimationPrivate : public QDeclarativeAnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QQuickParentAnimation) +public: + QQuickParentAnimationPrivate() + : QDeclarativeAnimationGroupPrivate(), target(0), newParent(0), + via(0), topLevelGroup(0), startAction(0), endAction(0) {} + + QQuickItem *target; + QQuickItem *newParent; + QQuickItem *via; + + QSequentialAnimationGroup *topLevelGroup; + QActionAnimation *startAction; + QActionAnimation *endAction; + + QPointF computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const; +}; + +class QQuickAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QQuickAnchorAnimation) +public: + QQuickAnchorAnimationPrivate() : rangeIsSet(false), va(0), + interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {} + + bool rangeIsSet; + QDeclarativeBulkValueAnimator *va; + QVariantAnimation::Interpolator interpolator; + QList targets; +}; + +class QQuickPathAnimationUpdater : public QDeclarativeBulkValueUpdater +{ +public: + QQuickPathAnimationUpdater() : path(0), target(0), reverse(false), + fromSourced(false), fromDefined(false), toDefined(false), + toX(0), toY(0), currentV(0), orientation(QQuickPathAnimation::Fixed), + entryInterval(0), exitInterval(0) {} + ~QQuickPathAnimationUpdater() {} + + void setValue(qreal v); + + QDeclarativePath *path; + + QPainterPath painterPath; + QDeclarativeCachedBezier prevBez; + qreal pathLength; + QList attributePoints; + + QQuickItem *target; + bool reverse; + bool fromSourced; + bool fromDefined; + bool toDefined; + qreal toX; + qreal toY; + qreal currentV; + QDeclarativeNullableValue interruptStart; + //TODO: bundle below into common struct + QQuickPathAnimation::Orientation orientation; + QPointF anchorPoint; + qreal entryInterval; + qreal exitInterval; + QDeclarativeNullableValue endRotation; + QDeclarativeNullableValue startRotation; +}; + +class QQuickPathAnimationPrivate : public QDeclarativeAbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QQuickPathAnimation) +public: + QQuickPathAnimationPrivate() : path(0), target(0), + rangeIsSet(false), orientation(QQuickPathAnimation::Fixed), entryInterval(0), exitInterval(0), pa(0) {} + + QDeclarativePath *path; + QQuickItem *target; + bool rangeIsSet; + QQuickPathAnimation::Orientation orientation; + QPointF anchorPoint; + qreal entryInterval; + qreal exitInterval; + QDeclarativeNullableValue endRotation; + QDeclarativeBulkValueAnimator *pa; +}; + + +QT_END_NAMESPACE + +#endif // QQUICKANIMATION_P_H diff --git a/src/declarative/items/qquickborderimage.cpp b/src/declarative/items/qquickborderimage.cpp new file mode 100644 index 0000000000..92c1c6d115 --- /dev/null +++ b/src/declarative/items/qquickborderimage.cpp @@ -0,0 +1,601 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickborderimage_p.h" +#include "qquickborderimage_p_p.h" +#include "qquickninepatchnode_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + + +/*! + \qmlclass BorderImage QQuickBorderImage + \inqmlmodule QtQuick 2 + \brief The BorderImage element provides an image that can be used as a border. + \inherits Item + \ingroup qml-basic-visual-elements + + The BorderImage element is used to create borders out of images by scaling or tiling + parts of each image. + + A BorderImage element breaks a source image, specified using the \l url property, + into 9 regions, as shown below: + + \image declarative-scalegrid.png + + When the image is scaled, regions of the source image are scaled or tiled to + create the displayed border image in the following way: + + \list + \i The corners (regions 1, 3, 7, and 9) are not scaled at all. + \i Regions 2 and 8 are scaled according to + \l{BorderImage::horizontalTileMode}{horizontalTileMode}. + \i Regions 4 and 6 are scaled according to + \l{BorderImage::verticalTileMode}{verticalTileMode}. + \i The middle (region 5) is scaled according to both + \l{BorderImage::horizontalTileMode}{horizontalTileMode} and + \l{BorderImage::verticalTileMode}{verticalTileMode}. + \endlist + + The regions of the image are defined using the \l border property group, which + describes the distance from each edge of the source image to use as a border. + + \section1 Example Usage + + The following examples show the effects of the different modes on an image. + Guide lines are overlaid onto the image to show the different regions of the + image as described above. + + \beginfloatleft + \image qml-borderimage-normal-image.png + \endfloat + + An unscaled image is displayed using an Image element. The \l border property is + used to determine the parts of the image that will lie inside the unscaled corner + areas and the parts that will be stretched horizontally and vertically. + + \snippet doc/src/snippets/declarative/borderimage/normal-image.qml normal image + + \clearfloat + \beginfloatleft + \image qml-borderimage-scaled.png + \endfloat + + A BorderImage element is used to display the image, and it is given a size that is + larger than the original image. Since the \l horizontalTileMode property is set to + \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in + regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property + is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image + in regions 4 and 6 are stretched vertically. + + \snippet doc/src/snippets/declarative/borderimage/borderimage-scaled.qml scaled border image + + \clearfloat + \beginfloatleft + \image qml-borderimage-tiled.png + \endfloat + + Again, a large BorderImage element is used to display the image. With the + \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat}, + the parts of image in regions 2 and 8 are tiled so that they fill the space at the + top and bottom of the element. Similarly, the \l verticalTileMode property is set to + \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions + 4 and 6 are tiled so that they fill the space at the left and right of the element. + + \snippet doc/src/snippets/declarative/borderimage/borderimage-tiled.qml tiled border image + + \clearfloat + In some situations, the width of regions 2 and 8 may not be an exact multiple of the width + of the corresponding regions in the source image. Similarly, the height of regions 4 and 6 + may not be an exact multiple of the height of the corresponding regions. It can be useful + to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of + \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these. + + The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage + can be used to simulate a shadow effect on a rectangular item. + + \section1 Quality and Performance + + By default, any scaled regions of the image are rendered without smoothing to improve + rendering speed. Setting the \l smooth property improves rendering quality of scaled + regions, but may slow down rendering. + + The source image may not be loaded instantaneously, depending on its original location. + Loading progress can be monitored with the \l progress property. + + \sa Image, AnimatedImage + */ + +/*! + \qmlproperty bool QtQuick2::BorderImage::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ +QQuickBorderImage::QQuickBorderImage(QQuickItem *parent) +: QQuickImageBase(*(new QQuickBorderImagePrivate), parent) +{ +} + +QQuickBorderImage::~QQuickBorderImage() +{ + Q_D(QQuickBorderImage); + if (d->sciReply) + d->sciReply->deleteLater(); +} + +/*! + \qmlproperty enumeration QtQuick2::BorderImage::status + + This property describes the status of image loading. It can be one of: + + \list + \o BorderImage.Null - no image has been set + \o BorderImage.Ready - the image has been loaded + \o BorderImage.Loading - the image is currently being loaded + \o BorderImage.Error - an error occurred while loading the image + \endlist + + \sa progress +*/ + +/*! + \qmlproperty real QtQuick2::BorderImage::progress + + This property holds the progress of image loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ + +/*! + \qmlproperty bool QtQuick2::BorderImage::smooth + + Set this property if you want the image to be smoothly filtered when scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the image is displayed at its natural size, this property has no visual or + performance effect. + + By default, this property is set to false. + + \note Generally scaling artifacts are only visible if the image is stationary on + the screen. A common pattern when animating an image is to disable smooth + filtering at the beginning of the animation and enable it at the conclusion. +*/ + +/*! + \qmlproperty bool QtQuick2::BorderImage::cache + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool QtQuick2::BorderImage::mirror + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + +/*! + \qmlproperty url QtQuick2::BorderImage::source + + This property holds the URL that refers to the source image. + + BorderImage can handle any image format supported by Qt, loaded from any + URL scheme supported by Qt. + + This property can also be used to refer to .sci files, which are + written in a QML-specific, text-based format that specifies the + borders, the image file and the tile rules for a given border image. + + The following .sci file sets the borders to 10 on each side for the + image \c picture.png: + + \code + border.left: 10 + border.top: 10 + border.bottom: 10 + border.right: 10 + source: "picture.png" + \endcode + + The URL may be absolute, or relative to the URL of the component. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty QSize QtQuick2::BorderImage::sourceSize + + This property holds the actual width and height of the loaded image. + + In BorderImage, this property is read-only. + + \sa Image::sourceSize +*/ +void QQuickBorderImage::setSource(const QUrl &url) +{ + Q_D(QQuickBorderImage); + //equality is fairly expensive, so we bypass for simple, common case + if ((d->url.isEmpty() == url.isEmpty()) && url == d->url) + return; + + if (d->sciReply) { + d->sciReply->deleteLater(); + d->sciReply = 0; + } + + d->url = url; + d->sciurl = QUrl(); + emit sourceChanged(d->url); + + if (isComponentComplete()) + load(); +} + +void QQuickBorderImage::load() +{ + Q_D(QQuickBorderImage); + if (d->progress != 0.0) { + d->progress = 0.0; + emit progressChanged(d->progress); + } + + if (d->url.isEmpty()) { + d->pix.clear(this); + d->status = Null; + setImplicitWidth(0); + setImplicitHeight(0); + emit statusChanged(d->status); + update(); + } else { + d->status = Loading; + if (d->url.path().endsWith(QLatin1String("sci"))) { + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!lf.isEmpty()) { + QFile file(lf); + file.open(QIODevice::ReadOnly); + setGridScaledImage(QQuickGridScaledImage(&file)); + } else { + QNetworkRequest req(d->url); + d->sciReply = qmlEngine(this)->networkAccessManager()->get(req); + FAST_CONNECT(d->sciReply, SIGNAL(finished()), this, SLOT(sciRequestFinished())) + } + } else { + + QDeclarativePixmap::Options options; + if (d->async) + options |= QDeclarativePixmap::Asynchronous; + if (d->cache) + options |= QDeclarativePixmap::Cache; + d->pix.clear(this); + d->pix.load(qmlEngine(this), d->url, options); + + if (d->pix.isLoading()) { + d->pix.connectFinished(this, SLOT(requestFinished())); + d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64))); + } else { + QSize impsize = d->pix.implicitSize(); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); + + if (d->pix.isReady()) { + d->status = Ready; + } else { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } + + d->progress = 1.0; + emit statusChanged(d->status); + emit progressChanged(d->progress); + update(); + } + } + } + + emit statusChanged(d->status); +} + +/*! + \qmlproperty int QtQuick2::BorderImage::border.left + \qmlproperty int QtQuick2::BorderImage::border.right + \qmlproperty int QtQuick2::BorderImage::border.top + \qmlproperty int QtQuick2::BorderImage::border.bottom + + The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections, + as shown below: + + \image declarative-scalegrid.png + + Each border line (left, right, top, and bottom) specifies an offset in pixels + from the respective edge of the source image. By default, each border line has + a value of 0. + + For example, the following definition sets the bottom line 10 pixels up from + the bottom of the image: + + \qml + BorderImage { + border.bottom: 10 + // ... + } + \endqml + + The border lines can also be specified using a + \l {BorderImage::source}{.sci file}. +*/ + +QQuickScaleGrid *QQuickBorderImage::border() +{ + Q_D(QQuickBorderImage); + return d->getScaleGrid(); +} + +/*! + \qmlproperty enumeration QtQuick2::BorderImage::horizontalTileMode + \qmlproperty enumeration QtQuick2::BorderImage::verticalTileMode + + This property describes how to repeat or stretch the middle parts of the border image. + + \list + \o BorderImage.Stretch - Scales the image to fit to the available area. + \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image. + \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped. + \endlist + + The default tile mode for each property is BorderImage.Stretch. +*/ +QQuickBorderImage::TileMode QQuickBorderImage::horizontalTileMode() const +{ + Q_D(const QQuickBorderImage); + return d->horizontalTileMode; +} + +void QQuickBorderImage::setHorizontalTileMode(TileMode t) +{ + Q_D(QQuickBorderImage); + if (t != d->horizontalTileMode) { + d->horizontalTileMode = t; + emit horizontalTileModeChanged(); + update(); + } +} + +QQuickBorderImage::TileMode QQuickBorderImage::verticalTileMode() const +{ + Q_D(const QQuickBorderImage); + return d->verticalTileMode; +} + +void QQuickBorderImage::setVerticalTileMode(TileMode t) +{ + Q_D(QQuickBorderImage); + if (t != d->verticalTileMode) { + d->verticalTileMode = t; + emit verticalTileModeChanged(); + update(); + } +} + +void QQuickBorderImage::setGridScaledImage(const QQuickGridScaledImage& sci) +{ + Q_D(QQuickBorderImage); + if (!sci.isValid()) { + d->status = Error; + emit statusChanged(d->status); + } else { + QQuickScaleGrid *sg = border(); + sg->setTop(sci.gridTop()); + sg->setBottom(sci.gridBottom()); + sg->setLeft(sci.gridLeft()); + sg->setRight(sci.gridRight()); + d->horizontalTileMode = sci.horizontalTileRule(); + d->verticalTileMode = sci.verticalTileRule(); + + d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl())); + + QDeclarativePixmap::Options options; + if (d->async) + options |= QDeclarativePixmap::Asynchronous; + if (d->cache) + options |= QDeclarativePixmap::Cache; + d->pix.clear(this); + d->pix.load(qmlEngine(this), d->sciurl, options); + + if (d->pix.isLoading()) { + static int thisRequestProgress = -1; + static int thisRequestFinished = -1; + if (thisRequestProgress == -1) { + thisRequestProgress = + QQuickBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); + thisRequestFinished = + QQuickBorderImage::staticMetaObject.indexOfSlot("requestFinished()"); + } + + d->pix.connectFinished(this, thisRequestFinished); + d->pix.connectDownloadProgress(this, thisRequestProgress); + + } else { + + QSize impsize = d->pix.implicitSize(); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); + + if (d->pix.isReady()) { + d->status = Ready; + } else { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } + + d->progress = 1.0; + emit statusChanged(d->status); + emit progressChanged(1.0); + update(); + + } + } +} + +void QQuickBorderImage::requestFinished() +{ + Q_D(QQuickBorderImage); + + QSize impsize = d->pix.implicitSize(); + if (d->pix.isError()) { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } else { + d->status = Ready; + } + + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); + + if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height()) + emit sourceSizeChanged(); + + d->progress = 1.0; + emit statusChanged(d->status); + emit progressChanged(1.0); + update(); +} + +#define BORDERIMAGE_MAX_REDIRECT 16 + +void QQuickBorderImage::sciRequestFinished() +{ + Q_D(QQuickBorderImage); + + d->redirectCount++; + if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) { + QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = d->sciReply->url().resolved(redirect.toUrl()); + setSource(url); + return; + } + } + d->redirectCount=0; + + if (d->sciReply->error() != QNetworkReply::NoError) { + d->status = Error; + d->sciReply->deleteLater(); + d->sciReply = 0; + emit statusChanged(d->status); + } else { + QQuickGridScaledImage sci(d->sciReply); + d->sciReply->deleteLater(); + d->sciReply = 0; + setGridScaledImage(sci); + } +} + +void QQuickBorderImage::doUpdate() +{ + update(); +} + +QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + Q_D(QQuickBorderImage); + + QSGTexture *texture = d->pix.texture(d->sceneGraphContext()); + + if (!texture || width() <= 0 || height() <= 0) { + delete oldNode; + return 0; + } + + QQuickNinePatchNode *node = static_cast(oldNode); + + if (!node) { + node = new QQuickNinePatchNode(); + } + + node->setTexture(texture); + + // Don't implicitly create the scalegrid in the rendering thread... + if (d->border) { + const QQuickScaleGrid *border = d->getScaleGrid(); + node->setInnerRect(QRectF(border->left(), + border->top(), + qMax(1, d->pix.width() - border->right() - border->left()), + qMax(1, d->pix.height() - border->bottom() - border->top()))); + } else { + node->setInnerRect(QRectF(0, 0, width(), height())); + } + node->setRect(QRectF(0, 0, width(), height())); + node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); + node->setHorzontalTileMode(d->horizontalTileMode); + node->setVerticalTileMode(d->verticalTileMode); + node->setMirror(d->mirror); + node->update(); + + return node; +} + +void QQuickBorderImage::pixmapChange() +{ + Q_D(QQuickBorderImage); + + d->pixmapChanged = true; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickborderimage_p.h b/src/declarative/items/qquickborderimage_p.h new file mode 100644 index 0000000000..0af6fca55d --- /dev/null +++ b/src/declarative/items/qquickborderimage_p.h @@ -0,0 +1,110 @@ +// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKBORDERIMAGE_P_H +#define QQUICKBORDERIMAGE_P_H + +#include "qquickimagebase_p.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickScaleGrid; +class QQuickGridScaledImage; +class QQuickBorderImagePrivate; +class Q_AUTOTEST_EXPORT QQuickBorderImage : public QQuickImageBase +{ + Q_OBJECT + Q_ENUMS(TileMode) + + Q_PROPERTY(QQuickScaleGrid *border READ border CONSTANT) + Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged) + Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged) + // read-only for BorderImage + Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) + +public: + QQuickBorderImage(QQuickItem *parent=0); + ~QQuickBorderImage(); + + QQuickScaleGrid *border(); + + enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile }; + + TileMode horizontalTileMode() const; + void setHorizontalTileMode(TileMode); + + TileMode verticalTileMode() const; + void setVerticalTileMode(TileMode); + + void setSource(const QUrl &url); + +Q_SIGNALS: + void horizontalTileModeChanged(); + void verticalTileModeChanged(); + void sourceSizeChanged(); + +protected: + virtual void load(); + virtual void pixmapChange(); + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private: + void setGridScaledImage(const QQuickGridScaledImage& sci); + +private Q_SLOTS: + void doUpdate(); + void requestFinished(); + void sciRequestFinished(); + +private: + Q_DISABLE_COPY(QQuickBorderImage) + Q_DECLARE_PRIVATE(QQuickBorderImage) +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QQuickBorderImage) +QT_END_HEADER + +#endif // QQUICKBORDERIMAGE_P_H diff --git a/src/declarative/items/qquickborderimage_p_p.h b/src/declarative/items/qquickborderimage_p_p.h new file mode 100644 index 0000000000..1875dc082a --- /dev/null +++ b/src/declarative/items/qquickborderimage_p_p.h @@ -0,0 +1,103 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKBORDERIMAGE_P_P_H +#define QQUICKBORDERIMAGE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickimagebase_p_p.h" +#include "qquickscalegrid_p_p.h" + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QQuickBorderImagePrivate : public QQuickImageBasePrivate +{ + Q_DECLARE_PUBLIC(QQuickBorderImage) + +public: + QQuickBorderImagePrivate() + : border(0), sciReply(0), + horizontalTileMode(QQuickBorderImage::Stretch), + verticalTileMode(QQuickBorderImage::Stretch), + redirectCount(0), pixmapChanged(false) + { + } + + ~QQuickBorderImagePrivate() + { + } + + + QQuickScaleGrid *getScaleGrid() + { + Q_Q(QQuickBorderImage); + if (!border) { + border = new QQuickScaleGrid(q); + FAST_CONNECT(border, SIGNAL(borderChanged()), q, SLOT(doUpdate())) + } + return border; + } + + QQuickScaleGrid *border; + QUrl sciurl; + QNetworkReply *sciReply; + QQuickBorderImage::TileMode horizontalTileMode; + QQuickBorderImage::TileMode verticalTileMode; + int redirectCount; + + bool pixmapChanged : 1; +}; + +QT_END_NAMESPACE + +#endif // QQUICKBORDERIMAGE_P_P_H diff --git a/src/declarative/items/qquickcanvas.cpp b/src/declarative/items/qquickcanvas.cpp new file mode 100644 index 0000000000..8f87ce7a9c --- /dev/null +++ b/src/declarative/items/qquickcanvas.cpp @@ -0,0 +1,2399 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcanvas.h" +#include "qquickcanvas_p.h" + +#include "qquickitem.h" +#include "qquickitem_p.h" + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +#define QQUICK_CANVAS_TIMING +#ifdef QQUICK_CANVAS_TIMING +static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty(); +static QTime threadTimer; +static int syncTime; +static int renderTime; +static int swapTime; +#endif + +DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP) +DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP) + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +void QQuickCanvasPrivate::updateFocusItemTransform() +{ + Q_Q(QQuickCanvas); + QQuickItem *focus = q->activeFocusItem(); + if (focus && qApp->inputPanel()->inputItem() == focus) + qApp->inputPanel()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToCanvasTransform()); +} + +class QQuickCanvasIncubationController : public QObject, public QDeclarativeIncubationController +{ +public: + QQuickCanvasIncubationController(QQuickCanvasPrivate *canvas) + : m_canvas(canvas), m_eventSent(false) {} + +protected: + virtual bool event(QEvent *e) + { + if (e->type() == QEvent::User) { + Q_ASSERT(m_eventSent); + + bool *amtp = m_canvas->thread->allowMainThreadProcessing(); + while (incubatingObjectCount()) { + if (amtp) + incubateWhile(amtp); + else + incubateFor(5); + QCoreApplication::processEvents(); + } + + m_eventSent = false; + } + return QObject::event(e); + } + + virtual void incubatingObjectCountChanged(int count) + { + if (count && !m_eventSent) { + m_eventSent = true; + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + } + } + +private: + QQuickCanvasPrivate *m_canvas; + bool m_eventSent; +}; + +class QQuickCanvasPlainRenderLoop : public QObject, public QQuickCanvasRenderLoop +{ +public: + QQuickCanvasPlainRenderLoop() + : updatePending(false) + , animationRunning(false) + { + qWarning("QQuickCanvas: using non-threaded render loop. Be very sure to not access scene graph " + "objects outside the QQuickItem::updatePaintNode() call. Failing to do so will cause " + "your code to crash on other platforms!"); + } + + virtual void paint() { + updatePending = false; + if (animationRunning && animationDriver()) + animationDriver()->advance(); + polishItems(); + syncSceneGraph(); + makeCurrent(); + glViewport(0, 0, size.width(), size.height()); + renderSceneGraph(size); + swapBuffers(); + + if (animationRunning) + maybeUpdate(); + } + + virtual QImage grab() { + return qt_gl_read_framebuffer(size, false, false); + } + + virtual void startRendering() { + if (!glContext()) { + createGLContext(); + makeCurrent(); + initializeSceneGraph(); + } else { + makeCurrent(); + } + maybeUpdate(); + } + + virtual void stopRendering() { } + + virtual void maybeUpdate() { + if (!updatePending) { + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + updatePending = true; + } + } + + virtual void animationStarted() { + animationRunning = true; + maybeUpdate(); + } + + virtual void animationStopped() { + animationRunning = false; + } + + virtual bool isRunning() const { return glContext(); } // Event loop is always running... + virtual void resize(const QSize &s) { size = s; } + virtual void setWindowSize(const QSize &s) { size = s; } + + bool event(QEvent *e) { + if (e->type() == QEvent::User) { + paint(); + return true; + } + return QObject::event(e); + } + + QSize size; + + uint updatePending : 1; + uint animationRunning : 1; +}; + + + +/* +Focus behavior +============== + +Prior to being added to a valid canvas items can set and clear focus with no +effect. Only once items are added to a canvas (by way of having a parent set that +already belongs to a canvas) do the focus rules apply. Focus goes back to +having no effect if an item is removed from a canvas. + +When an item is moved into a new focus scope (either being added to a canvas +for the first time, or having its parent changed), if the focus scope already has +a scope focused item that takes precedence over the item being added. Otherwise, +the focus of the added tree is used. In the case of of a tree of items being +added to a canvas for the first time, which may have a conflicted focus state (two +or more items in one scope having focus set), the same rule is applied item by item - +thus the first item that has focus will get it (assuming the scope doesn't already +have a scope focused item), and the other items will have their focus cleared. +*/ + +/* + Threaded Rendering + ================== + + The threaded rendering uses a number of different variables to track potential + states used to handle resizing, initial paint, grabbing and driving animations + while ALWAYS keeping the GL context in the rendering thread and keeping the + overhead of normal one-shot paints and vblank driven animations at a minimum. + + Resize, initial show and grab suffer slightly in this model as they are locked + to the rendering in the rendering thread, but this is a necessary evil for + the system to work. + + Variables that are used: + + Private::animationRunning: This is true while the animations are running, and only + written to inside locks. + + RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the + lock. This variable is an integer to allow for recursive calls to lockInGui() + without using a recursive mutex. See isGuiBlockPending. + + RenderThread::isPaintComplete: This variable is cleared when rendering starts and + set once rendering is complete. It is monitored in the paintEvent(), + resizeEvent() and grab() functions to force them to wait for rendering to + complete. + + RenderThread::isGuiBlockPending: This variable is set in the render thread just + before the sync event is sent to the GUI thread. It is used to avoid deadlocks + in the case where render thread waits while waiting for GUI to pick up the sync + event and GUI thread gets a resizeEvent, the initial paintEvent or a grab. + When this happens, we use the + exhaustSyncEvent() function to do the sync right there and mark the coming + sync event to be discarded. There can only ever be one sync incoming. + + RenderThread::isRenderBlock: This variable is true when animations are not + running and the render thread has gone to sleep, waiting for more to do. + + RenderThread::isExternalUpdatePending: This variable is set to false during + the sync phase in the GUI thread and to true in maybeUpdate(). It is an + indication to the render thread that another render pass needs to take + place, rather than the render thread going to sleep after completing its swap. + + RenderThread::doGrab: This variable is set by the grab() function and + tells the renderer to do a grab after rendering is complete and before + swapping happens. + + RenderThread::shouldExit: This variable is used to determine if the render + thread should do a nother pass. It is typically set as a result of show() + and unset as a result of hide() or during shutdown() + + RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown + after shouldExit has been set to true. + */ + +// #define FOCUS_DEBUG +// #define MOUSE_DEBUG +// #define TOUCH_DEBUG +// #define DIRTY_DEBUG +// #define THREAD_DEBUG + +// #define FRAME_TIMING + +#ifdef FRAME_TIMING +static QTime frameTimer; +int sceneGraphRenderTime; +int readbackTime; +#endif + +QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData() +: transformNode(0) +{ +} + +QQuickRootItem::QQuickRootItem() +{ +} + +void QQuickCanvas::exposeEvent(QExposeEvent *) +{ + Q_D(QQuickCanvas); + d->thread->paint(); +} + +void QQuickCanvas::resizeEvent(QResizeEvent *) +{ + Q_D(QQuickCanvas); + d->thread->resize(size()); +} + +void QQuickCanvas::animationStarted() +{ + d_func()->thread->animationStarted(); +} + +void QQuickCanvas::animationStopped() +{ + d_func()->thread->animationStopped(); +} + +void QQuickCanvas::showEvent(QShowEvent *) +{ + Q_D(QQuickCanvas); + if (d->vsyncAnimations) { + if (!d->animationDriver) { + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection); + connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection); + } + d->animationDriver->install(); + } + + if (!d->thread->isRunning()) { + d->thread->setWindowSize(size()); + d->thread->startRendering(); + } +} + +void QQuickCanvas::hideEvent(QHideEvent *) +{ + Q_D(QQuickCanvas); + d->thread->stopRendering(); +} + + + +/*! + Sets weither this canvas should use vsync driven animations. + + This option can only be set on one single QQuickCanvas, and that it's + vsync signal will then be used to drive all animations in the + process. + + This feature is primarily useful for single QQuickCanvas, QML-only + applications. + + \warning Enabling vsync on multiple QQuickCanvas instances has + undefined behavior. + */ +void QQuickCanvas::setVSyncAnimations(bool enabled) +{ + Q_D(QQuickCanvas); + if (visible()) { + qWarning("QQuickCanvas::setVSyncAnimations: Cannot be changed when widget is shown"); + return; + } + d->vsyncAnimations = enabled; +} + + + +/*! + Returns true if this canvas should use vsync driven animations; + otherwise returns false. + */ +bool QQuickCanvas::vsyncAnimations() const +{ + Q_D(const QQuickCanvas); + return d->vsyncAnimations; +} + +void QQuickCanvasPrivate::initializeSceneGraph() +{ + if (!context) + context = QSGContext::createDefaultContext(); + + if (context->isReady()) + return; + + QOpenGLContext *glctx = const_cast(QOpenGLContext::currentContext()); + context->initialize(glctx); + + Q_Q(QQuickCanvas); + QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()), + Qt::DirectConnection); + + if (!QQuickItemPrivate::get(rootItem)->itemNode()->parent()) { + context->rootNode()->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode()); + } + + emit q_func()->sceneGraphInitialized(); +} + +void QQuickCanvasPrivate::polishItems() +{ + while (!itemsToPolish.isEmpty()) { + QSet::Iterator iter = itemsToPolish.begin(); + QQuickItem *item = *iter; + itemsToPolish.erase(iter); + QQuickItemPrivate::get(item)->polishScheduled = false; + item->updatePolish(); + } + updateFocusItemTransform(); +} + + +void QQuickCanvasPrivate::syncSceneGraph() +{ + updateDirtyNodes(); +} + + +void QQuickCanvasPrivate::renderSceneGraph(const QSize &size) +{ + context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size)); + context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size)); + context->renderer()->setProjectionMatrixToDeviceRect(); + + context->renderNextFrame(renderTarget); + +#ifdef FRAME_TIMING + sceneGraphRenderTime = frameTimer.elapsed(); +#endif + +#ifdef FRAME_TIMING +// int pixel; +// glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); + readbackTime = frameTimer.elapsed(); +#endif +} + + +// ### Do we need this? +void QQuickCanvas::sceneGraphChanged() +{ +// Q_D(QQuickCanvas); +// d->needsRepaint = true; +} + +QQuickCanvasPrivate::QQuickCanvasPrivate() + : rootItem(0) + , activeFocusItem(0) + , mouseGrabberItem(0) + , dirtyItemList(0) + , context(0) + , vsyncAnimations(false) + , thread(0) + , animationDriver(0) + , renderTarget(0) + , incubationController(0) +{ +} + +QQuickCanvasPrivate::~QQuickCanvasPrivate() +{ +} + +void QQuickCanvasPrivate::init(QQuickCanvas *c) +{ + QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep()); + + q_ptr = c; + + Q_Q(QQuickCanvas); + + rootItem = new QQuickRootItem; + QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem); + rootItemPrivate->canvas = q; + rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope; + + // QML always has focus. It is important that this call happens after the rootItem + // has a canvas.. + rootItem->setFocus(true); + + bool threaded = !qmlNoThreadedRenderer(); + + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) { + qWarning("QQuickCanvas: platform does not support threaded rendering!"); + threaded = false; + } + + if (threaded) + thread = new QQuickCanvasRenderThread(); + else + thread = new QQuickCanvasPlainRenderLoop(); + + thread->renderer = q; + thread->d = this; + + context = QSGContext::createDefaultContext(); + thread->moveContextToThread(context); + + q->setSurfaceType(QWindow::OpenGLSurface); + q->setFormat(context->defaultSurfaceFormat()); +} + +void QQuickCanvasPrivate::transformTouchPoints(QList &touchPoints, const QTransform &transform) +{ + for (int i=0; isetWidget(q); // ### refactor... + + QList touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + + touchPoint.setScreenRect(touchPoint.sceneRect()); + touchPoint.setStartScreenPos(touchPoint.startScenePos()); + touchPoint.setLastScreenPos(touchPoint.lastScenePos()); + + touchPoint.setSceneRect(touchPoint.rect()); + touchPoint.setStartScenePos(touchPoint.startPos()); + touchPoint.setLastScenePos(touchPoint.lastPos()); + + if (touchPoint.isPrimary()) + lastMousePosition = touchPoint.pos().toPoint(); + } + touchEvent->setTouchPoints(touchPoints); +} + +void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options) +{ + Q_Q(QQuickCanvas); + + Q_ASSERT(item); + Q_ASSERT(scope || item == rootItem); + +#ifdef FOCUS_DEBUG + qWarning() << "QQuickCanvasPrivate::setFocusInScope():"; + qWarning() << " scope:" << (QObject *)scope; + if (scope) + qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem; + qWarning() << " item:" << (QObject *)item; + qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; +#endif + + QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + + QQuickItem *oldActiveFocusItem = 0; + QQuickItem *newActiveFocusItem = 0; + + QVarLengthArray changed; + + // Does this change the active focus? + if (item == rootItem || scopePrivate->activeFocus) { + oldActiveFocusItem = activeFocusItem; + newActiveFocusItem = item; + while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem()) + newActiveFocusItem = newActiveFocusItem->scopedFocusItem(); + + if (oldActiveFocusItem) { +#ifndef QT_NO_IM + qApp->inputPanel()->commit(); +#endif + + activeFocusItem = 0; + QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); + q->sendEvent(oldActiveFocusItem, &event); + + QQuickItem *afi = oldActiveFocusItem; + while (afi != scope) { + if (QQuickItemPrivate::get(afi)->activeFocus) { + QQuickItemPrivate::get(afi)->activeFocus = false; + changed << afi; + } + afi = afi->parentItem(); + } + } + } + + if (item != rootItem) { + QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; + // Correct focus chain in scope + if (oldSubFocusItem) { + QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi != scope) { + QQuickItemPrivate::get(sfi)->subFocusItem = 0; + sfi = sfi->parentItem(); + } + } + { + scopePrivate->subFocusItem = item; + QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi != scope) { + QQuickItemPrivate::get(sfi)->subFocusItem = item; + sfi = sfi->parentItem(); + } + } + + if (oldSubFocusItem) { + QQuickItemPrivate::get(oldSubFocusItem)->focus = false; + changed << oldSubFocusItem; + } + } + + if (!(options & DontChangeFocusProperty)) { + // if (item != rootItem || q->hasFocus()) { // ### refactor: focus handling... + itemPrivate->focus = true; + changed << item; + // } + } + + if (newActiveFocusItem) { // ### refactor: && q->hasFocus()) { + activeFocusItem = newActiveFocusItem; + + QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true; + changed << newActiveFocusItem; + + QQuickItem *afi = newActiveFocusItem->parentItem(); + while (afi && afi != scope) { + if (afi->isFocusScope()) { + QQuickItemPrivate::get(afi)->activeFocus = true; + changed << afi; + } + afi = afi->parentItem(); + } + + updateInputMethodData(); + + QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); + q->sendEvent(newActiveFocusItem, &event); + } else { + updateInputMethodData(); + } + + if (!changed.isEmpty()) + notifyFocusChangesRecur(changed.data(), changed.count() - 1); +} + +void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options) +{ + Q_Q(QQuickCanvas); + + Q_UNUSED(item); + Q_ASSERT(item); + Q_ASSERT(scope || item == rootItem); + +#ifdef FOCUS_DEBUG + qWarning() << "QQuickCanvasPrivate::clearFocusInScope():"; + qWarning() << " scope:" << (QObject *)scope; + qWarning() << " item:" << (QObject *)item; + qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; +#endif + + QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; + + QQuickItem *oldActiveFocusItem = 0; + QQuickItem *newActiveFocusItem = 0; + + QVarLengthArray changed; + + Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem); + + // Does this change the active focus? + if (item == rootItem || scopePrivate->activeFocus) { + oldActiveFocusItem = activeFocusItem; + newActiveFocusItem = scope; + + Q_ASSERT(oldActiveFocusItem); + +#ifndef QT_NO_IM + qApp->inputPanel()->commit(); +#endif + + activeFocusItem = 0; + QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); + q->sendEvent(oldActiveFocusItem, &event); + + QQuickItem *afi = oldActiveFocusItem; + while (afi != scope) { + if (QQuickItemPrivate::get(afi)->activeFocus) { + QQuickItemPrivate::get(afi)->activeFocus = false; + changed << afi; + } + afi = afi->parentItem(); + } + } + + if (item != rootItem) { + QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; + // Correct focus chain in scope + if (oldSubFocusItem) { + QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi != scope) { + QQuickItemPrivate::get(sfi)->subFocusItem = 0; + sfi = sfi->parentItem(); + } + } + scopePrivate->subFocusItem = 0; + + if (oldSubFocusItem && !(options & DontChangeFocusProperty)) { + QQuickItemPrivate::get(oldSubFocusItem)->focus = false; + changed << oldSubFocusItem; + } + } else if (!(options & DontChangeFocusProperty)) { + QQuickItemPrivate::get(item)->focus = false; + changed << item; + } + + if (newActiveFocusItem) { + Q_ASSERT(newActiveFocusItem == scope); + activeFocusItem = scope; + + updateInputMethodData(); + + QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); + q->sendEvent(newActiveFocusItem, &event); + } else { + updateInputMethodData(); + } + + if (!changed.isEmpty()) + notifyFocusChangesRecur(changed.data(), changed.count() - 1); +} + +void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining) +{ + QDeclarativeGuard item(*items); + + if (remaining) + notifyFocusChangesRecur(items + 1, remaining - 1); + + if (item) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + + if (itemPrivate->notifiedFocus != itemPrivate->focus) { + itemPrivate->notifiedFocus = itemPrivate->focus; + emit item->focusChanged(itemPrivate->focus); + } + + if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) { + itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus; + itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus); + emit item->activeFocusChanged(itemPrivate->activeFocus); + } + } +} + +void QQuickCanvasPrivate::updateInputMethodData() +{ + QQuickItem *inputItem = 0; + if (activeFocusItem && activeFocusItem->flags() & QQuickItem::ItemAcceptsInputMethod) + inputItem = activeFocusItem; + qApp->inputPanel()->setInputItem(inputItem); +} + +QVariant QQuickCanvas::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QQuickCanvas); + if (!d->activeFocusItem || !(QQuickItemPrivate::get(d->activeFocusItem)->flags & QQuickItem::ItemAcceptsInputMethod)) + return QVariant(); + QVariant value = d->activeFocusItem->inputMethodQuery(query); + + //map geometry types + QVariant::Type type = value.type(); + if (type == QVariant::RectF || type == QVariant::Rect) { + const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform(); + value = transform.mapRect(value.toRectF()); + } else if (type == QVariant::PointF || type == QVariant::Point) { + const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform(); + value = transform.map(value.toPointF()); + } + return value; +} + +void QQuickCanvasPrivate::dirtyItem(QQuickItem *) +{ + Q_Q(QQuickCanvas); + q->maybeUpdate(); +} + +void QQuickCanvasPrivate::cleanup(QSGNode *n) +{ + Q_Q(QQuickCanvas); + + Q_ASSERT(!cleanupNodeList.contains(n)); + cleanupNodeList.append(n); + q->maybeUpdate(); +} + + +QQuickCanvas::QQuickCanvas(QWindow *parent) + : QWindow(*(new QQuickCanvasPrivate), parent) +{ + Q_D(QQuickCanvas); + d->init(this); +} + +QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent) + : QWindow(dd, parent) +{ + Q_D(QQuickCanvas); + d->init(this); +} + +QQuickCanvas::~QQuickCanvas() +{ + Q_D(QQuickCanvas); + + if (d->thread->isRunning()) { + d->thread->stopRendering(); + delete d->thread; + d->thread = 0; + } + + // ### should we change ~QQuickItem to handle this better? + // manually cleanup for the root item (item destructor only handles these when an item is parented) + QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem); + rootItemPrivate->removeFromDirtyList(); + + delete d->incubationController; d->incubationController = 0; + + delete d->rootItem; d->rootItem = 0; + d->cleanupNodes(); +} + +QQuickItem *QQuickCanvas::rootItem() const +{ + Q_D(const QQuickCanvas); + + return d->rootItem; +} + +QQuickItem *QQuickCanvas::activeFocusItem() const +{ + Q_D(const QQuickCanvas); + + return d->activeFocusItem; +} + +QQuickItem *QQuickCanvas::mouseGrabberItem() const +{ + Q_D(const QQuickCanvas); + + return d->mouseGrabberItem; +} + + +bool QQuickCanvasPrivate::clearHover() +{ + if (hoverItems.isEmpty()) + return false; + + QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos()); + + bool accepted = false; + foreach (QQuickItem* item, hoverItems) + accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted; + hoverItems.clear(); + return accepted; +} + + +bool QQuickCanvas::event(QEvent *e) +{ + Q_D(QQuickCanvas); + + switch (e->type()) { + + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + QTouchEvent *touch = static_cast(e); + d->translateTouchEvent(touch); + d->deliverTouchEvent(touch); + if (!touch->isAccepted()) + return false; + break; + } + case QEvent::Leave: + d->clearHover(); + d->lastMousePosition = QPoint(); + break; + case QEvent::DragEnter: + case QEvent::DragLeave: + case QEvent::DragMove: + case QEvent::Drop: + d->deliverDragEvent(&d->dragGrabber, e); + break; + case QEvent::WindowDeactivate: + rootItem()->windowDeactivateEvent(); + break; + default: + break; + } + + return QWindow::event(e); +} + +void QQuickCanvas::keyPressEvent(QKeyEvent *e) +{ + Q_D(QQuickCanvas); + + if (d->activeFocusItem) + sendEvent(d->activeFocusItem, e); +} + +void QQuickCanvas::keyReleaseEvent(QKeyEvent *e) +{ + Q_D(QQuickCanvas); + + if (d->activeFocusItem) + sendEvent(d->activeFocusItem, e); +} + +void QQuickCanvas::inputMethodEvent(QInputMethodEvent *e) +{ + Q_D(QQuickCanvas); + + if (d->activeFocusItem) + sendEvent(d->activeFocusItem, e); +} + +bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_Q(QQuickCanvas); + + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(event->windowPos()); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled()) + continue; + if (deliverInitialMousePressEvent(child, event)) + return true; + } + + if (itemPrivate->acceptedMouseButtons & event->button()) { + QPointF p = item->mapFromScene(event->windowPos()); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + me.accept(); + mouseGrabberItem = item; + q->sendEvent(item, &me); + event->setAccepted(me.isAccepted()); + if (me.isAccepted()) + return true; + mouseGrabberItem->ungrabMouse(); + mouseGrabberItem = 0; + } + } + + return false; +} + +bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event) +{ + Q_Q(QQuickCanvas); + + lastMousePosition = event->windowPos(); + + if (!mouseGrabberItem && + event->type() == QEvent::MouseButtonPress && + (event->button() & event->buttons()) == event->buttons()) { + return deliverInitialMousePressEvent(rootItem, event); + } + + if (mouseGrabberItem) { + QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem); + const QTransform &transform = mgPrivate->canvasToItemTransform(); + QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + me.accept(); + q->sendEvent(mouseGrabberItem, &me); + event->setAccepted(me.isAccepted()); + if (me.isAccepted()) + return true; + } + + return false; +} + +void QQuickCanvas::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + d->deliverMouseEvent(event); +} + +void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + if (!d->mouseGrabberItem) { + QWindow::mouseReleaseEvent(event); + return; + } + + d->deliverMouseEvent(event); + d->mouseGrabberItem = 0; +} + +void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) { + if (d->deliverInitialMousePressEvent(d->rootItem, event)) + event->accept(); + else + event->ignore(); + return; + } + + d->deliverMouseEvent(event); +} + +bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, + const QPointF &scenePos, const QPointF &lastScenePos, + Qt::KeyboardModifiers modifiers, bool accepted) +{ + Q_Q(QQuickCanvas); + const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform(); + + //create copy of event + QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers); + hoverEvent.setAccepted(accepted); + + q->sendEvent(item, &hoverEvent); + + return hoverEvent.isAccepted(); +} + +void QQuickCanvas::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + if (!d->mouseGrabberItem) { + if (d->lastMousePosition.isNull()) + d->lastMousePosition = event->windowPos(); + QPointF last = d->lastMousePosition; + d->lastMousePosition = event->windowPos(); + + bool accepted = event->isAccepted(); + bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted); + if (!delivered) { + //take care of any exits + accepted = d->clearHover(); + } + event->setAccepted(accepted); + return; + } + + d->deliverMouseEvent(event); +} + +bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, + Qt::KeyboardModifiers modifiers, bool &accepted) +{ + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(scenePos); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled()) + continue; + if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted)) + return true; + } + + if (itemPrivate->hoverEnabled) { + QPointF p = item->mapFromScene(scenePos); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + if (!hoverItems.isEmpty() && hoverItems[0] == item) { + //move + accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); + } else { + QList itemsToHover; + QQuickItem* parent = item; + itemsToHover << item; + while ((parent = parent->parentItem())) + itemsToHover << parent; + + // Leaving from previous hovered items until we reach the item or one of its ancestors. + while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) { + sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted); + hoverItems.removeFirst(); + } + + if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item + // ### Shouldn't we send moves for the parent items as well? + accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); + } else { + // Enter items that are not entered yet. + int startIdx = -1; + if (!hoverItems.isEmpty()) + startIdx = itemsToHover.indexOf(hoverItems[0]) - 1; + if (startIdx == -1) + startIdx = itemsToHover.count() - 1; + + for (int i = startIdx; i >= 0; i--) { + QQuickItem *itemToHover = itemsToHover[i]; + if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) { + hoverItems.prepend(itemToHover); + sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted); + } + } + } + } + return true; + } + } + + return false; +} + +bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event) +{ + Q_Q(QQuickCanvas); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(event->posF()); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled()) + continue; + if (deliverWheelEvent(child, event)) + return true; + } + + QPointF p = item->mapFromScene(event->posF()); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation()); + wheel.accept(); + q->sendEvent(item, &wheel); + if (wheel.isAccepted()) { + event->accept(); + return true; + } + } + + return false; +} + +#ifndef QT_NO_WHEELEVENT +void QQuickCanvas::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickCanvas); +#ifdef MOUSE_DEBUG + qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation(); +#endif + event->ignore(); + d->deliverWheelEvent(d->rootItem, event); +} +#endif // QT_NO_WHEELEVENT + +bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event) +{ +#ifdef TOUCH_DEBUG + if (event->type() == QEvent::TouchBegin) + qWarning("touchBeginEvent"); + else if (event->type() == QEvent::TouchUpdate) + qWarning("touchUpdateEvent"); + else if (event->type() == QEvent::TouchEnd) + qWarning("touchEndEvent"); +#endif + + QHash > updatedPoints; + + if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points + QSet acceptedNewPoints; + deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints); + if (acceptedNewPoints.count() > 0) + event->accept(); + return event->isAccepted(); + } + + const QList &touchPoints = event->touchPoints(); + QList newPoints; + QQuickItem *item = 0; + for (int i=0; i 0 || updatedPoints.count() > 0) { + QSet acceptedNewPoints; + int prevCount = updatedPoints.count(); + deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints); + if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount) + event->accept(); + } + + if (event->touchPointStates() & Qt::TouchPointReleased) { + for (int i=0; iisAccepted(); +} + +bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList &newPoints, QSet *acceptedNewPoints, QHash > *updatedPoints) +{ + Q_Q(QQuickCanvas); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + QRectF bounds(0, 0, item->width(), item->height()); + for (int i=0; imapFromScene(newPoints[i].scenePos()); + if (!bounds.contains(p)) + return false; + } + } + + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + if (!child->isEnabled()) + continue; + if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints)) + return true; + } + + QList matchingPoints; + if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { + QRectF bounds(0, 0, item->width(), item->height()); + for (int i=0; icontains(newPoints[i].id())) + continue; + QPointF p = item->mapFromScene(newPoints[i].scenePos()); + if (bounds.contains(p)) + matchingPoints << newPoints[i]; + } + } + + if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) { + QList &eventPoints = (*updatedPoints)[item]; + eventPoints.append(matchingPoints); + transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform()); + + Qt::TouchPointStates eventStates; + for (int i=0; ideviceType()); + touchEvent.setModifiers(event->modifiers()); + touchEvent.setTouchPointStates(eventStates); + touchEvent.setTouchPoints(eventPoints); + touchEvent.setTimestamp(event->timestamp()); + + touchEvent.accept(); + q->sendEvent(item, &touchEvent); + + if (touchEvent.isAccepted()) { + for (int i=0; iinsert(matchingPoints[i].id()); + } + } + } + } + + updatedPoints->remove(item); + if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty()) + return true; + + return false; +} + +void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) +{ + Q_Q(QQuickCanvas); + grabber->resetTarget(); + QQuickDragGrabber::iterator grabItem = grabber->begin(); + if (grabItem != grabber->end()) { + Q_ASSERT(event->type() != QEvent::DragEnter); + if (event->type() == QEvent::Drop) { + QDropEvent *e = static_cast(event); + for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { + QPointF p = (**grabItem)->mapFromScene(e->pos()); + QDropEvent translatedEvent( + p.toPoint(), + e->possibleActions(), + e->mimeData(), + e->mouseButtons(), + e->keyboardModifiers()); + QQuickDropEventEx::copyActions(&translatedEvent, *e); + q->sendEvent(**grabItem, &translatedEvent); + e->setAccepted(translatedEvent.isAccepted()); + e->setDropAction(translatedEvent.dropAction()); + grabber->setTarget(**grabItem); + } + } + if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave. + QDragLeaveEvent leaveEvent; + for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) + q->sendEvent(**grabItem, &leaveEvent); + return; + } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { + QDragMoveEvent *moveEvent = static_cast(event); + if (deliverDragEvent(grabber, **grabItem, moveEvent)) { + moveEvent->setAccepted(true); + for (++grabItem; grabItem != grabber->end();) { + QPointF p = (**grabItem)->mapFromScene(moveEvent->pos()); + if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) { + QDragMoveEvent translatedEvent( + p.toPoint(), + moveEvent->possibleActions(), + moveEvent->mimeData(), + moveEvent->mouseButtons(), + moveEvent->keyboardModifiers()); + QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent); + q->sendEvent(**grabItem, &translatedEvent); + ++grabItem; + } else { + QDragLeaveEvent leaveEvent; + q->sendEvent(**grabItem, &leaveEvent); + grabItem = grabber->release(grabItem); + } + } + return; + } else { + QDragLeaveEvent leaveEvent; + q->sendEvent(**grabItem, &leaveEvent); + } + } + } + if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) { + QDragMoveEvent *e = static_cast(event); + QDragEnterEvent enterEvent( + e->pos(), + e->possibleActions(), + e->mimeData(), + e->mouseButtons(), + e->keyboardModifiers()); + QQuickDropEventEx::copyActions(&enterEvent, *e); + event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent)); + } +} + +bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event) +{ + Q_Q(QQuickCanvas); + bool accepted = false; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (itemPrivate->opacity == 0.0 || !item->isVisible() || !item->isEnabled()) + return false; + + QPointF p = item->mapFromScene(event->pos()); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) { + QDragMoveEvent translatedEvent( + p.toPoint(), + event->possibleActions(), + event->mimeData(), + event->mouseButtons(), + event->keyboardModifiers(), + event->type()); + QQuickDropEventEx::copyActions(&translatedEvent, *event); + q->sendEvent(item, &translatedEvent); + if (event->type() == QEvent::DragEnter) { + if (translatedEvent.isAccepted()) { + grabber->grab(item); + accepted = true; + } + } else { + accepted = true; + } + } + } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + return false; + } + + QDragEnterEvent enterEvent( + event->pos(), + event->possibleActions(), + event->mimeData(), + event->mouseButtons(), + event->keyboardModifiers()); + QQuickDropEventEx::copyActions(&enterEvent, *event); + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + if (deliverDragEvent(grabber, children.at(ii), &enterEvent)) + return true; + } + + return accepted; +} + +bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QMouseEvent *event) +{ + if (!target) + return false; + + QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); + if (targetPrivate->filtersChildMouseEvents) + if (target->childMouseEventFilter(item, event)) + return true; + + if (sendFilteredMouseEvent(target->parentItem(), item, event)) + return true; + + return false; +} + +bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e) +{ + Q_D(QQuickCanvas); + + if (!item) { + qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item"); + return false; + } + + Q_ASSERT(e); + + switch (e->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + e->accept(); + QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast(e)); + while (!e->isAccepted() && (item = item->parentItem())) { + e->accept(); + QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast(e)); + } + break; + case QEvent::InputMethod: + e->accept(); + QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast(e)); + while (!e->isAccepted() && (item = item->parentItem())) { + e->accept(); + QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast(e)); + } + break; + case QEvent::FocusIn: + case QEvent::FocusOut: + QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast(e)); + break; + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? + { + QMouseEvent *se = static_cast(e); + if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) { + se->accept(); + QQuickItemPrivate::get(item)->deliverMouseEvent(se); + } + } + break; + case QEvent::Wheel: + QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast(e)); + break; + case QEvent::HoverEnter: + case QEvent::HoverLeave: + case QEvent::HoverMove: + QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast(e)); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast(e)); + break; + case QEvent::DragEnter: + case QEvent::DragMove: + case QEvent::DragLeave: + case QEvent::Drop: + QQuickItemPrivate::get(item)->deliverDragEvent(e); + break; + default: + break; + } + + return false; +} + +void QQuickCanvasPrivate::cleanupNodes() +{ + for (int ii = 0; ii < cleanupNodeList.count(); ++ii) + delete cleanupNodeList.at(ii); + cleanupNodeList.clear(); +} + +void QQuickCanvasPrivate::updateDirtyNodes() +{ +#ifdef DIRTY_DEBUG + qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():"; +#endif + + cleanupNodes(); + + QQuickItem *updateList = dirtyItemList; + dirtyItemList = 0; + if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList; + + while (updateList) { + QQuickItem *item = updateList; + QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); + itemPriv->removeFromDirtyList(); + +#ifdef DIRTY_DEBUG + qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); +#endif + updateDirtyNode(item); + } +} + +void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item) +{ +#ifdef QML_RUNTIME_TESTING + bool didFlash = false; +#endif + + QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); + quint32 dirty = itemPriv->dirtyAttributes; + itemPriv->dirtyAttributes = 0; + + if ((dirty & QQuickItemPrivate::TransformUpdateMask) || + (dirty & QQuickItemPrivate::Size && itemPriv->origin != QQuickItem::TopLeft && + (itemPriv->scale != 1. || itemPriv->rotation != 0.))) { + + QMatrix4x4 matrix; + + if (itemPriv->x != 0. || itemPriv->y != 0.) + matrix.translate(itemPriv->x, itemPriv->y); + + for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii) + itemPriv->transforms.at(ii)->applyTo(&matrix); + + if (itemPriv->scale != 1. || itemPriv->rotation != 0.) { + QPointF origin = item->transformOriginPoint(); + matrix.translate(origin.x(), origin.y()); + if (itemPriv->scale != 1.) + matrix.scale(itemPriv->scale, itemPriv->scale); + if (itemPriv->rotation != 0.) + matrix.rotate(itemPriv->rotation, 0, 0, 1); + matrix.translate(-origin.x(), -origin.y()); + } + + itemPriv->itemNode()->setMatrix(matrix); + } + + bool clipEffectivelyChanged = dirty & QQuickItemPrivate::Clip && + ((item->clip() == false) != (itemPriv->clipNode == 0)); + bool effectRefEffectivelyChanged = dirty & QQuickItemPrivate::EffectReference && + ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0)); + + if (clipEffectivelyChanged) { + QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode(); + QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode; + + if (item->clip()) { + Q_ASSERT(itemPriv->clipNode == 0); + itemPriv->clipNode = new QQuickDefaultClipNode(item->boundingRect()); + itemPriv->clipNode->update(); + + if (child) + parent->removeChildNode(child); + parent->appendChildNode(itemPriv->clipNode); + if (child) + itemPriv->clipNode->appendChildNode(child); + + } else { + Q_ASSERT(itemPriv->clipNode != 0); + parent->removeChildNode(itemPriv->clipNode); + if (child) + itemPriv->clipNode->removeChildNode(child); + delete itemPriv->clipNode; + itemPriv->clipNode = 0; + if (child) + parent->appendChildNode(child); + } + } + + if (dirty & QQuickItemPrivate::ChildrenUpdateMask) + itemPriv->childContainerNode()->removeAllChildNodes(); + + if (effectRefEffectivelyChanged) { + QSGNode *parent = itemPriv->clipNode; + if (!parent) + parent = itemPriv->opacityNode; + if (!parent) + parent = itemPriv->itemNode(); + QSGNode *child = itemPriv->groupNode; + + if (itemPriv->effectRefCount) { + Q_ASSERT(itemPriv->rootNode == 0); + itemPriv->rootNode = new QSGRootNode; + + if (child) + parent->removeChildNode(child); + parent->appendChildNode(itemPriv->rootNode); + if (child) + itemPriv->rootNode->appendChildNode(child); + } else { + Q_ASSERT(itemPriv->rootNode != 0); + parent->removeChildNode(itemPriv->rootNode); + if (child) + itemPriv->rootNode->removeChildNode(child); + delete itemPriv->rootNode; + itemPriv->rootNode = 0; + if (child) + parent->appendChildNode(child); + } + } + + if (dirty & QQuickItemPrivate::ChildrenUpdateMask) { + QSGNode *groupNode = itemPriv->groupNode; + if (groupNode) + groupNode->removeAllChildNodes(); + + QList orderedChildren = itemPriv->paintOrderChildItems(); + int ii = 0; + + for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) { + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii)); + if (!childPrivate->explicitVisible && !childPrivate->effectRefCount) + continue; + if (childPrivate->itemNode()->parent()) + childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode()); + + itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode()); + } + itemPriv->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0; + + if (itemPriv->paintNode) + itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode); + + for (; ii < orderedChildren.count(); ++ii) { + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii)); + if (!childPrivate->explicitVisible && !childPrivate->effectRefCount) + continue; + if (childPrivate->itemNode()->parent()) + childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode()); + + itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode()); + } + } + + if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode) { + itemPriv->clipNode->setRect(item->boundingRect()); + itemPriv->clipNode->update(); + } + + if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible | QQuickItemPrivate::HideReference)) { + qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0 + ? itemPriv->opacity : qreal(0); + + if (opacity != 1 && !itemPriv->opacityNode) { + itemPriv->opacityNode = new QSGOpacityNode; + + QSGNode *parent = itemPriv->itemNode(); + QSGNode *child = itemPriv->clipNode; + if (!child) + child = itemPriv->rootNode; + if (!child) + child = itemPriv->groupNode; + + if (child) + parent->removeChildNode(child); + parent->appendChildNode(itemPriv->opacityNode); + if (child) + itemPriv->opacityNode->appendChildNode(child); + } + if (itemPriv->opacityNode) + itemPriv->opacityNode->setOpacity(opacity); + } + + if (dirty & QQuickItemPrivate::ContentUpdateMask) { + + if (itemPriv->flags & QQuickItem::ItemHasContents) { + updatePaintNodeData.transformNode = itemPriv->itemNode(); + itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData); + + Q_ASSERT(itemPriv->paintNode == 0 || + itemPriv->paintNode->parent() == 0 || + itemPriv->paintNode->parent() == itemPriv->childContainerNode()); + + if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) { + if (itemPriv->beforePaintNode) + itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode); + else + itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode); + } + } else if (itemPriv->paintNode) { + delete itemPriv->paintNode; + itemPriv->paintNode = 0; + } + } + +#ifndef QT_NO_DEBUG + // Check consistency. + const QSGNode *nodeChain[] = { + itemPriv->itemNodeInstance, + itemPriv->opacityNode, + itemPriv->clipNode, + itemPriv->rootNode, + itemPriv->groupNode, + itemPriv->paintNode, + }; + + int ip = 0; + for (;;) { + while (ip < 5 && nodeChain[ip] == 0) + ++ip; + if (ip == 5) + break; + int ic = ip + 1; + while (ic < 5 && nodeChain[ic] == 0) + ++ic; + const QSGNode *parent = nodeChain[ip]; + const QSGNode *child = nodeChain[ic]; + if (child == 0) { + Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0); + } else { + Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1); + Q_ASSERT(child->parent() == parent); + bool containsChild = false; + for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling()) + containsChild |= (n == child); + Q_ASSERT(containsChild); + } + ip = ic; + } +#endif + +#ifdef QML_RUNTIME_TESTING + if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) { + QSGFlashNode *flash = new QSGFlashNode(); + flash->setRect(item->boundingRect()); + itemPriv->childContainerNode()->appendChildNode(flash); + didFlash = true; + } + Q_Q(QQuickCanvas); + if (didFlash) { + q->maybeUpdate(); + } +#endif + +} + +void QQuickCanvas::maybeUpdate() +{ + Q_D(QQuickCanvas); + + if (d->thread && d->thread->isRunning()) + d->thread->maybeUpdate(); +} + +/*! + \fn void QSGEngine::sceneGraphInitialized(); + + This signal is emitted when the scene graph has been initialized. + + This signal will be emitted from the scene graph rendering thread. + */ + +/*! + Returns the QSGEngine used for this scene. + + The engine will only be available once the scene graph has been + initialized. Register for the sceneGraphEngine() signal to get + notification about this. + */ + +QSGEngine *QQuickCanvas::sceneGraphEngine() const +{ + Q_D(const QQuickCanvas); + if (d->context && d->context->isReady()) + return d->context->engine(); + return 0; +} + + + +/*! + Sets the render target for this canvas to be \a fbo. + + The specified fbo must be created in the context of the canvas + or one that shares with it. + + \warning + This function can only be called from the thread doing + the rendering. + */ + +void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo) +{ + Q_D(QQuickCanvas); + if (d->context && d->context && QThread::currentThread() != d->context->thread()) { + qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread"); + return; + } + + d->renderTarget = fbo; +} + + + +/*! + Returns the render target for this canvas. + + The default is to render to the surface of the canvas, in which + case the render target is 0. + */ +QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const +{ + Q_D(const QQuickCanvas); + return d->renderTarget; +} + + +/*! + Grabs the contents of the framebuffer and returns it as an image. + + This function might not work if the view is not visible. + + \warning Calling this function will cause performance problems. + + \warning This function can only be called from the GUI thread. + */ +QImage QQuickCanvas::grabFrameBuffer() +{ + Q_D(QQuickCanvas); + return d->thread ? d->thread->grab() : QImage(); +} + +/*! + Returns an incubation controller that splices incubation between frames + for this canvas. QQuickView automatically installs this controller for you. + + The controller is owned by the canvas and will be destroyed when the canvas + is deleted. +*/ +QDeclarativeIncubationController *QQuickCanvas::incubationController() const +{ + Q_D(const QQuickCanvas); + + if (!d->incubationController) + d->incubationController = new QQuickCanvasIncubationController(const_cast(d)); + return d->incubationController; +} + + +void QQuickCanvasRenderLoop::createGLContext() +{ + gl = new QOpenGLContext(); + gl->setFormat(renderer->requestedFormat()); + gl->create(); +} + +void QQuickCanvasRenderThread::run() +{ +#ifdef THREAD_DEBUG + qDebug("QML Rendering Thread Started"); +#endif + + if (!glContext()) { + createGLContext(); + makeCurrent(); + initializeSceneGraph(); + } else { + makeCurrent(); + } + + while (!shouldExit) { + lock(); + + bool sizeChanged = false; + isExternalUpdatePending = false; + + if (renderedSize != windowSize) { +#ifdef THREAD_DEBUG + printf(" RenderThread: window has changed size...\n"); +#endif + glViewport(0, 0, windowSize.width(), windowSize.height()); + sizeChanged = true; + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: preparing to sync...\n"); +#endif + + if (!isGuiBlocked) { + isGuiBlockPending = true; + +#ifdef THREAD_DEBUG + printf(" RenderThread: aquired sync lock...\n"); +#endif + allowMainThreadProcessingFlag = false; + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +#ifdef THREAD_DEBUG + printf(" RenderThread: going to sleep...\n"); +#endif + wait(); + + isGuiBlockPending = false; + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: Doing locked sync\n"); +#endif +#ifdef QQUICK_CANVAS_TIMING + if (qquick_canvas_timing) + threadTimer.start(); +#endif + inSync = true; + syncSceneGraph(); + inSync = false; + + // Wake GUI after sync to let it continue animating and event processing. + allowMainThreadProcessingFlag = true; + wake(); + unlock(); +#ifdef THREAD_DEBUG + printf(" RenderThread: sync done\n"); +#endif +#ifdef QQUICK_CANVAS_TIMING + if (qquick_canvas_timing) + syncTime = threadTimer.elapsed(); +#endif + +#ifdef THREAD_DEBUG + printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height()); +#endif + + renderSceneGraph(windowSize); +#ifdef QQUICK_CANVAS_TIMING + if (qquick_canvas_timing) + renderTime = threadTimer.elapsed() - syncTime; +#endif + + // The content of the target buffer is undefined after swap() so grab needs + // to happen before swap(); + if (doGrab) { +#ifdef THREAD_DEBUG + printf(" RenderThread: doing a grab...\n"); +#endif + grabContent = qt_gl_read_framebuffer(windowSize, false, false); + doGrab = false; + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: wait for swap...\n"); +#endif + + swapBuffers(); +#ifdef THREAD_DEBUG + printf(" RenderThread: swap complete...\n"); +#endif +#ifdef QQUICK_CANVAS_TIMING + if (qquick_canvas_timing) { + swapTime = threadTimer.elapsed() - renderTime; + qDebug() << "- Breakdown of frame time: sync:" << syncTime + << "ms render:" << renderTime << "ms swap:" << swapTime + << "ms total:" << swapTime + renderTime << "ms"; + } +#endif + + lock(); + isPaintCompleted = true; + if (sizeChanged) + renderedSize = windowSize; + + // Wake the GUI thread now that rendering is complete, to signal that painting + // is done, resizing is done or grabbing is completed. For grabbing, we're + // signalling this much later than needed (we could have done it before swap) + // but we don't want to lock an extra time. + wake(); + + if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) { +#ifdef THREAD_DEBUG + printf(" RenderThread: nothing to do, going to sleep...\n"); +#endif + isRenderBlocked = true; + wait(); + isRenderBlocked = false; + } + + unlock(); + + // Process any "deleteLater" objects... + QCoreApplication::processEvents(); + } + +#ifdef THREAD_DEBUG + printf(" RenderThread: render loop exited... Good Night!\n"); +#endif + + doneCurrent(); + + lock(); + hasExited = true; +#ifdef THREAD_DEBUG + printf(" RenderThread: waking GUI for final sleep..\n"); +#endif + wake(); + unlock(); + +#ifdef THREAD_DEBUG + printf(" RenderThread: All done...\n"); +#endif +} + + + +bool QQuickCanvasRenderThread::event(QEvent *e) +{ + Q_ASSERT(QThread::currentThread() == qApp->thread()); + + if (e->type() == QEvent::User) { + if (!syncAlreadyHappened) + sync(false); + + syncAlreadyHappened = false; + + if (animationRunning && animationDriver()) { +#ifdef THREAD_DEBUG + qDebug("GUI: Advancing animations...\n"); +#endif + + animationDriver()->advance(); + +#ifdef THREAD_DEBUG + qDebug("GUI: Animations advanced...\n"); +#endif + } + + return true; + } + + return QThread::event(e); +} + + + +void QQuickCanvasRenderThread::exhaustSyncEvent() +{ + if (isGuiBlockPending) { + sync(true); + syncAlreadyHappened = true; + } +} + + + +void QQuickCanvasRenderThread::sync(bool guiAlreadyLocked) +{ +#ifdef THREAD_DEBUG + printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event"); +#endif + if (!guiAlreadyLocked) + lockInGui(); + + renderThreadAwakened = false; + + polishItems(); + + wake(); + wait(); + + if (!guiAlreadyLocked) + unlockInGui(); +} + + + + +/*! + Acquires the mutex for the GUI thread. The function uses the isGuiBlocked + variable to keep track of how many recursion levels the gui is locket with. + We only actually acquire the mutex for the first level to avoid deadlocking + ourselves. + */ + +void QQuickCanvasRenderThread::lockInGui() +{ + // We must avoid recursive locking in the GUI thread, hence we + // only lock when we are the first one to try to block. + if (!isGuiBlocked) + lock(); + + isGuiBlocked++; + +#ifdef THREAD_DEBUG + printf("GUI: aquired lock... %d\n", isGuiBlocked); +#endif +} + + + +void QQuickCanvasRenderThread::unlockInGui() +{ +#ifdef THREAD_DEBUG + printf("GUI: releasing lock... %d\n", isGuiBlocked); +#endif + --isGuiBlocked; + if (!isGuiBlocked) + unlock(); +} + + + + +void QQuickCanvasRenderThread::animationStarted() +{ +#ifdef THREAD_DEBUG + printf("GUI: animationStarted()\n"); +#endif + + lockInGui(); + + animationRunning = true; + + if (isRenderBlocked) + wake(); + + unlockInGui(); +} + + + +void QQuickCanvasRenderThread::animationStopped() +{ +#ifdef THREAD_DEBUG + printf("GUI: animationStopped()...\n"); +#endif + + lockInGui(); + animationRunning = false; + unlockInGui(); +} + + +void QQuickCanvasRenderThread::paint() +{ +#ifdef THREAD_DEBUG + printf("GUI: paint called..\n"); +#endif + + lockInGui(); + exhaustSyncEvent(); + + isPaintCompleted = false; + while (isRunning() && !isPaintCompleted) { + if (isRenderBlocked) + wake(); + wait(); + } + unlockInGui(); +} + + + +void QQuickCanvasRenderThread::resize(const QSize &size) +{ +#ifdef THREAD_DEBUG + printf("GUI: Resize Event: %dx%d\n", size.width(), size.height()); +#endif + + if (!isRunning()) { + windowSize = size; + return; + } + + lockInGui(); + exhaustSyncEvent(); + + windowSize = size; + + while (isRunning() && renderedSize != windowSize) { + if (isRenderBlocked) + wake(); + wait(); + } + unlockInGui(); +} + + + +void QQuickCanvasRenderThread::startRendering() +{ +#ifdef THREAD_DEBUG + printf("GUI: Starting Render Thread\n"); +#endif + hasExited = false; + shouldExit = false; + isGuiBlocked = 0; + isGuiBlockPending = false; + start(); +} + + + +void QQuickCanvasRenderThread::stopRendering() +{ +#ifdef THREAD_DEBUG + printf("GUI: stopping render thread\n"); +#endif + + lockInGui(); + exhaustSyncEvent(); + shouldExit = true; + + if (isRenderBlocked) { +#ifdef THREAD_DEBUG + printf("GUI: waking up render thread\n"); +#endif + wake(); + } + + while (!hasExited) { +#ifdef THREAD_DEBUG + printf("GUI: waiting for render thread to have exited..\n"); +#endif + wait(); + } + + unlockInGui(); + +#ifdef THREAD_DEBUG + printf("GUI: waiting for render thread to terminate..\n"); +#endif + // Actually wait for the thread to terminate. Otherwise we can delete it + // too early and crash. + QThread::wait(); + +#ifdef THREAD_DEBUG + printf("GUI: thread has terminated and we're all good..\n"); +#endif + +} + + + +QImage QQuickCanvasRenderThread::grab() +{ + if (!isRunning()) + return QImage(); + + if (QThread::currentThread() != qApp->thread()) { + qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread"); + return QImage(); + } + +#ifdef THREAD_DEBUG + printf("GUI: doing a pixelwise grab..\n"); +#endif + + lockInGui(); + exhaustSyncEvent(); + + doGrab = true; + isPaintCompleted = false; + while (isRunning() && !isPaintCompleted) { + if (isRenderBlocked) + wake(); + wait(); + } + + QImage grabbed = grabContent; + grabContent = QImage(); + + unlockInGui(); + + return grabbed; +} + + + +void QQuickCanvasRenderThread::maybeUpdate() +{ + Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync, + "QQuickCanvas::update", + "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()"); + + if (inSync) { + isExternalUpdatePending = true; + + } else if (!renderThreadAwakened) { +#ifdef THREAD_DEBUG + printf("GUI: doing update...\n"); +#endif + renderThreadAwakened = true; + lockInGui(); + isExternalUpdatePending = true; + if (isRenderBlocked) + wake(); + unlockInGui(); + } +} + + +#include "moc_qquickcanvas.cpp" + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickcanvas.h b/src/declarative/items/qquickcanvas.h new file mode 100644 index 0000000000..8b69744161 --- /dev/null +++ b/src/declarative/items/qquickcanvas.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCANVAS_H +#define QQUICKCANVAS_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickItem; +class QSGEngine; +class QQuickCanvasPrivate; +class QOpenGLFramebufferObject; +class QDeclarativeIncubationController; + +class Q_DECLARATIVE_EXPORT QQuickCanvas : public QWindow +{ +Q_OBJECT +Q_DECLARE_PRIVATE(QQuickCanvas) +public: + QQuickCanvas(QWindow *parent = 0); + + virtual ~QQuickCanvas(); + + QQuickItem *rootItem() const; + QQuickItem *activeFocusItem() const; + + QQuickItem *mouseGrabberItem() const; + + bool sendEvent(QQuickItem *, QEvent *); + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + QSGEngine *sceneGraphEngine() const; + + void setVSyncAnimations(bool enabled); + bool vsyncAnimations() const; + + QImage grabFrameBuffer(); + + void setRenderTarget(QOpenGLFramebufferObject *fbo); + QOpenGLFramebufferObject *renderTarget() const; + + QDeclarativeIncubationController *incubationController() const; + +Q_SIGNALS: + void frameSwapped(); + void sceneGraphInitialized(); + +protected: + QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent = 0); + + virtual void exposeEvent(QExposeEvent *); + virtual void resizeEvent(QResizeEvent *); + + virtual void showEvent(QShowEvent *); + virtual void hideEvent(QHideEvent *); + + virtual bool event(QEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + virtual void inputMethodEvent(QInputMethodEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseDoubleClickEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); +#ifndef QT_NO_WHEELEVENT + virtual void wheelEvent(QWheelEvent *); +#endif + +private Q_SLOTS: + void sceneGraphChanged(); + void maybeUpdate(); + void animationStarted(); + void animationStopped(); + +private: + friend class QQuickItem; + friend class QQuickCanvasRenderLoop; + Q_DISABLE_COPY(QQuickCanvas) +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQuickCanvas *) + +QT_END_HEADER + +#endif // QQUICKCANVAS_H + diff --git a/src/declarative/items/qquickcanvas_p.h b/src/declarative/items/qquickcanvas_p.h new file mode 100644 index 0000000000..5c68442f29 --- /dev/null +++ b/src/declarative/items/qquickcanvas_p.h @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCANVAS_P_H +#define QQUICKCANVAS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" +#include "qquickcanvas.h" +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +//Make it easy to identify and customize the root item if needed +class QQuickRootItem : public QQuickItem +{ + Q_OBJECT +public: + QQuickRootItem(); +}; + +class QQuickCanvasPrivate; + +class QTouchEvent; +class QQuickCanvasRenderLoop; +class QQuickCanvasIncubationController; + +class QQuickCanvasPrivate : public QWindowPrivate +{ +public: + Q_DECLARE_PUBLIC(QQuickCanvas) + + static inline QQuickCanvasPrivate *get(QQuickCanvas *c) { return c->d_func(); } + + QQuickCanvasPrivate(); + virtual ~QQuickCanvasPrivate(); + + void init(QQuickCanvas *); + + QQuickRootItem *rootItem; + + QQuickItem *activeFocusItem; + QQuickItem *mouseGrabberItem; + QQuickDragGrabber dragGrabber; + + // Mouse positions are saved in widget coordinates + QPointF lastMousePosition; + void translateTouchEvent(QTouchEvent *touchEvent); + static void transformTouchPoints(QList &touchPoints, const QTransform &transform); + bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); + bool deliverMouseEvent(QMouseEvent *); + bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QMouseEvent *); + bool deliverWheelEvent(QQuickItem *, QWheelEvent *); + bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList &, QSet *, + QHash > *); + bool deliverTouchEvent(QTouchEvent *); + bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); + bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, + Qt::KeyboardModifiers modifiers, bool accepted); + bool clearHover(); + void deliverDragEvent(QQuickDragGrabber *, QEvent *); + bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *); + + QList hoverItems; + enum FocusOption { + DontChangeFocusProperty = 0x01, + }; + Q_DECLARE_FLAGS(FocusOptions, FocusOption) + + void setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); + void clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); + void notifyFocusChangesRecur(QQuickItem **item, int remaining); + + void updateInputMethodData(); + void updateFocusItemTransform(); + + void dirtyItem(QQuickItem *); + void cleanup(QSGNode *); + + void initializeSceneGraph(); + void polishItems(); + void syncSceneGraph(); + void renderSceneGraph(const QSize &size); + + QQuickItem::UpdatePaintNodeData updatePaintNodeData; + + QQuickItem *dirtyItemList; + QList cleanupNodeList; + + QSet itemsToPolish; + + void updateDirtyNodes(); + void cleanupNodes(); + bool updateEffectiveOpacity(QQuickItem *); + void updateEffectiveOpacityRoot(QQuickItem *, qreal); + void updateDirtyNode(QQuickItem *); + + QSGContext *context; + + uint vsyncAnimations : 1; + + QQuickCanvasRenderLoop *thread; + QSize widgetSize; + QSize viewportSize; + + QAnimationDriver *animationDriver; + + QOpenGLFramebufferObject *renderTarget; + + QHash itemForTouchPointId; + + mutable QQuickCanvasIncubationController *incubationController; +}; + +class QQuickCanvasRenderLoop +{ +public: + QQuickCanvasRenderLoop() + : d(0) + , renderer(0) + , gl(0) + { + } + virtual ~QQuickCanvasRenderLoop() + { + delete gl; + } + + friend class QQuickCanvasPrivate; + + virtual void paint() = 0; + virtual void resize(const QSize &size) = 0; + virtual void startRendering() = 0; + virtual void stopRendering() = 0; + virtual QImage grab() = 0; + virtual void setWindowSize(const QSize &size) = 0; + virtual void maybeUpdate() = 0; + virtual bool isRunning() const = 0; + virtual void animationStarted() = 0; + virtual void animationStopped() = 0; + virtual void moveContextToThread(QSGContext *) { } + virtual bool *allowMainThreadProcessing() { return 0; } + +protected: + void initializeSceneGraph() { d->initializeSceneGraph(); } + void syncSceneGraph() { d->syncSceneGraph(); } + void renderSceneGraph(const QSize &size) { d->renderSceneGraph(size); } + void polishItems() { d->polishItems(); } + QAnimationDriver *animationDriver() const { return d->animationDriver; } + + inline QOpenGLContext *glContext() const { return gl; } + void createGLContext(); + void makeCurrent() { gl->makeCurrent(renderer); } + void doneCurrent() { gl->doneCurrent(); } + void swapBuffers() { + gl->swapBuffers(renderer); + emit renderer->frameSwapped(); + } + +private: + QQuickCanvasPrivate *d; + QQuickCanvas *renderer; + + QOpenGLContext *gl; +}; + +class QQuickCanvasRenderThread : public QThread, public QQuickCanvasRenderLoop +{ + Q_OBJECT +public: + QQuickCanvasRenderThread() + : mutex(QMutex::NonRecursive) + , allowMainThreadProcessingFlag(true) + , animationRunning(false) + , isGuiBlocked(0) + , isPaintCompleted(false) + , isGuiBlockPending(false) + , isRenderBlocked(false) + , isExternalUpdatePending(false) + , syncAlreadyHappened(false) + , inSync(false) + , doGrab(false) + , shouldExit(false) + , hasExited(false) + , renderThreadAwakened(false) + {} + + inline void lock() { mutex.lock(); } + inline void unlock() { mutex.unlock(); } + inline void wait() { condition.wait(&mutex); } + inline void wake() { condition.wakeOne(); } + + void lockInGui(); + void unlockInGui(); + + void paint(); + void resize(const QSize &size); + void startRendering(); + void stopRendering(); + void exhaustSyncEvent(); + void sync(bool guiAlreadyLocked); + bool isRunning() const { return QThread::isRunning(); } + void setWindowSize(const QSize &size) { windowSize = size; } + void maybeUpdate(); + void moveContextToThread(QSGContext *c) { c->moveToThread(this); } + bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; } + + bool event(QEvent *); + + QImage grab(); + +public slots: + void animationStarted(); + void animationStopped(); + +public: + QMutex mutex; + QWaitCondition condition; + + bool allowMainThreadProcessingFlag; + + QSize windowSize; + QSize renderedSize; + + uint animationRunning: 1; + int isGuiBlocked; + uint isPaintCompleted : 1; + uint isGuiBlockPending : 1; + uint isRenderBlocked : 1; + uint isExternalUpdatePending : 1; + uint syncAlreadyHappened : 1; + uint inSync : 1; + uint doGrab : 1; + uint shouldExit : 1; + uint hasExited : 1; + uint renderThreadAwakened : 1; + + QImage grabContent; + + void run(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickCanvasPrivate::FocusOptions) + +QT_END_NAMESPACE + +#endif // QQUICKCANVAS_P_H diff --git a/src/declarative/items/qquickclipnode.cpp b/src/declarative/items/qquickclipnode.cpp new file mode 100644 index 0000000000..4aeb2dcf69 --- /dev/null +++ b/src/declarative/items/qquickclipnode.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qquickclipnode_p.h" + +#include +#include + +QQuickDefaultClipNode::QQuickDefaultClipNode(const QRectF &rect) + : m_rect(rect) + , m_radius(0) + , m_dirty_geometry(true) + , m_geometry(QSGGeometry::defaultAttributes_Point2D(), 0) +{ + setGeometry(&m_geometry); + setIsRectangular(true); +} + +void QQuickDefaultClipNode::setRect(const QRectF &rect) +{ + m_rect = rect; + m_dirty_geometry = true; +} + +void QQuickDefaultClipNode::setRadius(qreal radius) +{ + m_radius = radius; + m_dirty_geometry = true; + setIsRectangular(radius == 0); +} + +void QQuickDefaultClipNode::update() +{ + if (m_dirty_geometry) { + updateGeometry(); + m_dirty_geometry = false; + } +} + +void QQuickDefaultClipNode::updateGeometry() +{ + QSGGeometry *g = geometry(); + + if (qFuzzyIsNull(m_radius)) { + g->allocate(4); + QSGGeometry::updateRectGeometry(g, m_rect); + + } else { + int vertexCount = 0; + + // Radius should never exceeds half of the width or half of the height + qreal radius = qMin(qMin(m_rect.width() / 2, m_rect.height() / 2), m_radius); + QRectF rect = m_rect; + rect.adjust(radius, radius, -radius, -radius); + + int segments = qMin(30, qCeil(radius)); // Number of segments per corner. + + g->allocate((segments + 1) * 2); + + QVector2D *vertices = (QVector2D *)g->vertexData(); + + for (int part = 0; part < 2; ++part) { + for (int i = 0; i <= segments; ++i) { + //### Should change to calculate sin/cos only once. + qreal angle = qreal(0.5 * M_PI) * (part + i / qreal(segments)); + qreal s = qFastSin(angle); + qreal c = qFastCos(angle); + qreal y = (part ? rect.bottom() : rect.top()) - radius * c; // current inner y-coordinate. + qreal lx = rect.left() - radius * s; // current inner left x-coordinate. + qreal rx = rect.right() + radius * s; // current inner right x-coordinate. + + vertices[vertexCount++] = QVector2D(rx, y); + vertices[vertexCount++] = QVector2D(lx, y); + } + } + + markDirty(DirtyGeometry); + } + setClipRect(m_rect); +} + diff --git a/src/declarative/items/qquickclipnode_p.h b/src/declarative/items/qquickclipnode_p.h new file mode 100644 index 0000000000..0323c9a24a --- /dev/null +++ b/src/declarative/items/qquickclipnode_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCLIPNODE_P_H +#define QQUICKCLIPNODE_P_H + +#include + +class QQuickDefaultClipNode : public QSGClipNode +{ +public: + QQuickDefaultClipNode(const QRectF &); + + void setRect(const QRectF &); + QRectF rect() const { return m_rect; } + + void setRadius(qreal radius); + qreal radius() const { return m_radius; } + + virtual void update(); + +private: + void updateGeometry(); + QRectF m_rect; + qreal m_radius; + + uint m_dirty_geometry : 1; + uint m_reserved : 31; + + QSGGeometry m_geometry; +}; + +#endif // QQUICKCLIPNODE_P_H diff --git a/src/declarative/items/qquickdrag.cpp b/src/declarative/items/qquickdrag.cpp new file mode 100644 index 0000000000..d3439b08bf --- /dev/null +++ b/src/declarative/items/qquickdrag.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdrag_p.h" + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQuickDragAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickDragAttached) +public: + static QQuickDragAttachedPrivate *get(QQuickDragAttached *attached) { + return static_cast(QObjectPrivate::get(attached)); } + + QQuickDragAttachedPrivate() + : attachedItem(0) + , mimeData(0) + , proposedAction(Qt::MoveAction) + , supportedActions(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction) + , active(false) + , listening(false) + { + } + + void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &); + void start() { start(supportedActions); } + void start(Qt::DropActions supportedActions); + void setTarget(QQuickItem *item); + + QQuickDragGrabber dragGrabber; + + QDeclarativeGuard source; + QDeclarativeGuard target; + QQuickItem *attachedItem; + QQuickDragMimeData *mimeData; + Qt::DropAction proposedAction; + Qt::DropActions supportedActions; + bool active : 1; + bool listening : 1; + QPointF hotSpot; + QStringList keys; +}; + +/*! + \qmlclass Drag QQuickDrag + \inqmlmodule QtQuick 2 + \brief The Drag attached property provides drag and drop events for moved Items. + + Using the Drag attached property any Item can made a source of drag and drop + events within a scene. + + When a drag is \l active on an item any change in that items position will + generate a drag events that will be sent to any DropArea that intersects + the with new position of the item. Other items which implement drag and + drop event handlers can also receive these events. + + The following snippet shows how an item can be dragged with a MouseArea. + However, dragging is not limited to mouse drags, anything that can move an item + can generate drag events, this can include touch events, animations and bindings. + + \snippet doc/src/snippets/declarative/drag.qml 0 + + A drag can be terminated either by cancelling it with Drag.cancel() or setting + Drag.active to false, or it can be terminated with a drop event by calling + Drag.drop(). If the drop event is accepted Drag.drop() will return the + \l {supportedActions}{drop action} chosen by the recipient of the event, + otherwise it will return Qt.IgnoreAction. + +*/ + +void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QQuickDragAttached); + if (newGeometry.topLeft() == oldGeometry.topLeft() || !active) + return; + + if (QQuickCanvas *canvas = attachedItem->canvas()) { + QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint(); + QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier); + QQuickDropEventEx::setProposedAction(&event, proposedAction); + QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, &event); + if (target != dragGrabber.target()) { + target = dragGrabber.target(); + emit q->targetChanged(); + } + } +} + +QQuickDragAttached::QQuickDragAttached(QObject *parent) + : QObject(*new QQuickDragAttachedPrivate, parent) +{ + Q_D(QQuickDragAttached); + d->attachedItem = qobject_cast(parent); + d->source = d->attachedItem; +} + +QQuickDragAttached::~QQuickDragAttached() +{ + Q_D(QQuickDragAttached); + delete d->mimeData; +} + +/*! + \qmlattachedproperty bool QtQuick2::Drag::active + + This property holds whether a drag event sequence is currently active. + + Setting this property to true will send a QDragEnter event to the scene + with the item's current position. Setting it to false will send a + QDragLeave event. + + While a drag is active any change in an item's position will send a QDragMove + event with item's new position to the scene. +*/ + +bool QQuickDragAttached::isActive() const +{ + Q_D(const QQuickDragAttached); + return d->active; +} + +void QQuickDragAttached::setActive(bool active) +{ + Q_D(QQuickDragAttached); + if (d->active != active) { + if (active) + d->start(d->supportedActions); + else + cancel(); + } +} + +/*! + \qmlattachedproperty Object QtQuick2::Drag::source + + This property holds an object that is identified to recipients of drag events as + the source of the events. By default this is the item Drag property is attached to. + + Changes to source while a Drag is active don't take effect until a new drag is started. +*/ + +QObject *QQuickDragAttached::source() const +{ + Q_D(const QQuickDragAttached); + return d->source; +} + +void QQuickDragAttached::setSource(QObject *item) +{ + Q_D(QQuickDragAttached); + if (d->source != item) { + d->source = item; + emit sourceChanged(); + } +} + +void QQuickDragAttached::resetSource() +{ + Q_D(QQuickDragAttached); + if (d->source != d->attachedItem) { + d->source = d->attachedItem; + emit sourceChanged(); + } +} + +/*! + \qmlattachedproperty Object QtQuick2::Drag::target + + While a drag is active this property holds the last object to accept an + enter event from the dragged item, if the current drag position doesn't + intersect any accepting targets it is null. + + When a drag is not active this property holds the object that accepted + the drop event that ended the drag, if no object accepted the drop or + the drag was cancelled the target will then be null. +*/ + +QObject *QQuickDragAttached::target() const +{ + Q_D(const QQuickDragAttached); + return d->target; +} + +/*! + \qmlattachedproperty QPointF QtQuick2::Drag::hotSpot + + This property holds the drag position relative to the top left of the item. + + By default this is (0, 0). + + Changes to hotSpot will take effect when the next event is sent. +*/ + +QPointF QQuickDragAttached::hotSpot() const +{ + Q_D(const QQuickDragAttached); + return d->hotSpot; +} + +void QQuickDragAttached::setHotSpot(const QPointF &hotSpot) +{ + Q_D(QQuickDragAttached); + if (d->hotSpot != hotSpot) { + d->hotSpot = hotSpot; + emit hotSpotChanged(); + // Send a move event if active? + } +} + +/*! + \qmlattachedproperty stringlist QtQuick2::Drag::keys + + This property holds a list of keys that can be used by a DropArea to filter drag events. + + Changes to keys while a Drag is active don't take effect until a new drag is started. +*/ + +QStringList QQuickDragAttached::keys() const +{ + Q_D(const QQuickDragAttached); + return d->keys; +} + +void QQuickDragAttached::setKeys(const QStringList &keys) +{ + Q_D(QQuickDragAttached); + if (d->keys != keys) { + d->keys = keys; + emit keysChanged(); + } +} + +/*! + \qmlattachedproperty flags QtQuick2::Drag::supportedActions + + This property holds return values of Drag.drop() supported by the drag source. + + Changes to supportedActions while a Drag is active don't take effect + until a new drag is started. +*/ + +Qt::DropActions QQuickDragAttached::supportedActions() const +{ + Q_D(const QQuickDragAttached); + return d->supportedActions; +} + +void QQuickDragAttached::setSupportedActions(Qt::DropActions actions) +{ + Q_D(QQuickDragAttached); + if (d->supportedActions != actions) { + d->supportedActions = actions; + emit supportedActionsChanged(); + } +} + +/*! + \qmlattachedproperty enumeration QtQuick2::Drag::proposedAction + + This property holds an action that is recommended by the drag source as a + return value from Drag.drop(). + + Changes to proposedAction will take effect when the next event is sent. +*/ + +Qt::DropAction QQuickDragAttached::proposedAction() const +{ + Q_D(const QQuickDragAttached); + return d->proposedAction; +} + +void QQuickDragAttached::setProposedAction(Qt::DropAction action) +{ + Q_D(QQuickDragAttached); + if (d->proposedAction != action) { + d->proposedAction = action; + emit proposedActionChanged(); + // send a move event with the new default action if active? + } +} + +void QQuickDragAttachedPrivate::start(Qt::DropActions supportedActions) +{ + Q_Q(QQuickDragAttached); + Q_ASSERT(!active); + + if (QQuickCanvas *canvas = attachedItem ? attachedItem->canvas() : 0) { + if (!mimeData) + mimeData = new QQuickDragMimeData; + if (!listening) { + QQuickItemPrivate::get(attachedItem)->addItemChangeListener(this, QQuickItemPrivate::Geometry); + listening = true; + } + + mimeData->m_source = source; + mimeData->m_supportedActions = supportedActions; + mimeData->m_keys = keys; + active = true; + + QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint(); + QDragEnterEvent event(scenePos, supportedActions, mimeData, Qt::NoButton, Qt::NoModifier); + QQuickDropEventEx::setProposedAction(&event, proposedAction); + QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, &event); + + emit q->activeChanged(); + if (target != dragGrabber.target()) { + target = dragGrabber.target(); + emit q->targetChanged(); + } + } +} + +/*! + \qmlattachedmethod void QtQuick2::Drag::start(flags supportedActions) + + Starts sending drag events. + + The optional \a supportedActions argument can be used to override the \l supportedActions + property for the started sequence. +*/ + +void QQuickDragAttached::start(QDeclarativeV8Function *args) +{ + Q_D(QQuickDragAttached); + if (d->active) + cancel(); + + Qt::DropActions supportedActions = d->supportedActions; + // check arguments for supportedActions, maybe data? + if (args->Length() >= 1) { + v8::Local v = (*args)[0]; + if (v->IsInt32()) + supportedActions = Qt::DropActions(v->Int32Value()); + } + + d->start(supportedActions); +} + +/*! + \qmlattachedmethod enum QtQuick2::Drag::drop() + + Ends a drag sequence by sending a drop event to the target item. + + Returns the action accepted by the target item. If the target item or a parent doesn't accept + the drop event then Qt.IgnoreAction will be returned. + + The returned drop action may be one of: + + \list + \o Qt.CopyAction Copy the data to the target + \o Qt.MoveAction Move the data from the source to the target + \o Qt.LinkAction Create a link from the source to the target. + \o Qt.IgnoreAction Ignore the action (do nothing with the data). + \endlist + +*/ + +int QQuickDragAttached::drop() +{ + Q_D(QQuickDragAttached); + Qt::DropAction acceptedAction = Qt::IgnoreAction; + + if (!d->active) + return acceptedAction; + + QObject *target = 0; + + if (QQuickCanvas *canvas = d->attachedItem->canvas()) { + QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint(); + + QDropEvent event( + scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier); + QQuickDropEventEx::setProposedAction(&event, d->proposedAction); + QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&d->dragGrabber, &event); + + if (event.isAccepted()) { + acceptedAction = event.dropAction(); + target = d->dragGrabber.target(); + } + } + + d->active = false; + if (d->target != target) { + d->target = target; + emit targetChanged(); + } + + emit activeChanged(); + return acceptedAction; +} + +/*! + \qmlattachedmethod void QtQuick2::Drag::cancel() + + Ends a drag sequence. +*/ + +void QQuickDragAttached::cancel() +{ + Q_D(QQuickDragAttached); + if (!d->active) + return; + + if (QQuickCanvas *canvas = d->attachedItem->canvas()) { + QDragLeaveEvent event; + QQuickCanvasPrivate::get(canvas)->deliverDragEvent(&d->dragGrabber, &event); + } + + d->active = false; + if (d->target) { + d->target = 0; + emit targetChanged(); + } + emit activeChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickdrag_p.h b/src/declarative/items/qquickdrag_p.h new file mode 100644 index 0000000000..1d950299d7 --- /dev/null +++ b/src/declarative/items/qquickdrag_p.h @@ -0,0 +1,208 @@ +// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKDRAG_P_H +#define QQUICKDRAG_P_H + +#include + +#include + +#include +#include + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickItem; +class QQuickDrag; +class QQuickDragPrivate; + +class QQuickDragGrabber +{ + class Item : public QDeclarativeGuard + { + public: + Item(QQuickItem *item) : QDeclarativeGuard(item) {} + + QIntrusiveListNode node; + protected: + void objectDestroyed(QQuickItem *) { delete this; } + }; + + typedef QIntrusiveList ItemList; + +public: + QQuickDragGrabber() : m_target(0) {} + ~QQuickDragGrabber() { while (!m_items.isEmpty()) delete m_items.first(); } + + + QObject *target() const + { + if (m_target) + return m_target; + else if (!m_items.isEmpty()) + return *m_items.first(); + else + return 0; + } + void setTarget(QObject *target) { m_target = target; } + void resetTarget() { m_target = 0; } + + typedef ItemList::iterator iterator; + iterator begin() { return m_items.begin(); } + iterator end() { return m_items.end(); } + + void grab(QQuickItem *item) { m_items.insert(new Item(item)); } + iterator release(iterator at) { Item *item = *at; at = at.erase(); delete item; return at; } + +private: + + ItemList m_items; + QObject *m_target; +}; + +class QQuickDropEventEx : public QDropEvent +{ +public: + void setProposedAction(Qt::DropAction action) { default_action = action; drop_action = action; } + + static void setProposedAction(QDropEvent *event, Qt::DropAction action) { + static_cast(event)->setProposedAction(action); + } + + void copyActions(const QDropEvent &from) { + default_action = from.proposedAction(); drop_action = from.dropAction(); } + + static void copyActions(QDropEvent *to, const QDropEvent &from) { + static_cast(to)->copyActions(from); + } +}; + +class QQuickDragMimeData : public QMimeData +{ + Q_OBJECT +public: + QQuickDragMimeData() + : m_source(0) + { + } + + QStringList keys() const { return m_keys; } + QObject *source() const { return m_source; } + +private: + QObject *m_source; + Qt::DropActions m_supportedActions; + QStringList m_keys; + + friend class QQuickDragAttached; + friend class QQuickDragAttachedPrivate; +}; + +class QDeclarativeV8Function; + +class QQuickDragAttachedPrivate; +class QQuickDragAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource) + Q_PROPERTY(QObject *target READ target NOTIFY targetChanged) + Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged) + Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged) + Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged) + Q_PROPERTY(Qt::DropAction proposedAction READ proposedAction WRITE setProposedAction NOTIFY proposedActionChanged) +public: + QQuickDragAttached(QObject *parent); + ~QQuickDragAttached(); + + bool isActive() const; + void setActive(bool active); + + QObject *source() const; + void setSource(QObject *item); + void resetSource(); + + QObject *target() const; + + QPointF hotSpot() const; + void setHotSpot(const QPointF &hotSpot); + + QStringList keys() const; + void setKeys(const QStringList &keys); + + Qt::DropActions supportedActions() const; + void setSupportedActions(Qt::DropActions actions); + + Qt::DropAction proposedAction() const; + void setProposedAction(Qt::DropAction action); + + Q_INVOKABLE int drop(); + +public Q_SLOTS: + void start(QDeclarativeV8Function *); + void cancel(); + +Q_SIGNALS: + void activeChanged(); + void sourceChanged(); + void targetChanged(); + void hotSpotChanged(); + void keysChanged(); + void supportedActionsChanged(); + void proposedActionChanged(); + +private: + Q_DECLARE_PRIVATE(QQuickDragAttached) +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/items/qquickdroparea.cpp b/src/declarative/items/qquickdroparea.cpp new file mode 100644 index 0000000000..b9b93085e6 --- /dev/null +++ b/src/declarative/items/qquickdroparea.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdroparea_p.h" +#include "qquickdrag_p.h" +#include "qquickitem_p.h" +#include "qquickcanvas.h" + +#include + +QQuickDropAreaDrag::QQuickDropAreaDrag(QQuickDropAreaPrivate *d, QObject *parent) + : QObject(parent) + , d(d) +{ +} + +QQuickDropAreaDrag::~QQuickDropAreaDrag() +{ +} + +class QQuickDropAreaPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickDropArea) + +public: + QQuickDropAreaPrivate(); + ~QQuickDropAreaPrivate(); + + bool hasMatchingKey(const QStringList &keys) const; + + QStringList getKeys(const QMimeData *mimeData) const; + + QStringList keys; + QRegExp keyRegExp; + QPointF dragPosition; + QQuickDropAreaDrag *drag; + QDeclarativeGuard source; + QDeclarativeGuard mimeData; +}; + +QQuickDropAreaPrivate::QQuickDropAreaPrivate() + : drag(0) +{ +} + +QQuickDropAreaPrivate::~QQuickDropAreaPrivate() +{ + delete drag; +} + +/*! + \qmlclass DropArea QQuickDropArea + \inqmlmodule QtQuick 2 + \brief The DropArea item provides drag and drop handling. + + A DropArea is an invisible item which receives events when other items are + dragged over it. + + The Drag attached property can be used to notify the DropArea when an Item is + dragged over it. + + The \l keys property can be used to filter drag events which don't include + a matching key. + + The \l dropItem property is communicated to the source of a drag event as + the recipient of a drop on the drag target. + + The \l delegate property provides a means to specify a component to be + instantiated for each active drag over a drag target. +*/ + +QQuickDropArea::QQuickDropArea(QQuickItem *parent) + : QQuickItem(*new QQuickDropAreaPrivate, parent) +{ + setFlags(ItemAcceptsDrops); +} + +QQuickDropArea::~QQuickDropArea() +{ +} + +/*! + \qmlproperty bool QtQuick2::DropArea::containsDrag + + This property identifies whether the DropArea currently contains any + dragged items. +*/ + +bool QQuickDropArea::containsDrag() const +{ + Q_D(const QQuickDropArea); + return d->mimeData; +} + +/*! + \qmlproperty stringlist QtQuick2::DropArea::keys + + This property holds a list of drag keys a DropArea will accept. + + If no keys are listed the DropArea will accept events from any drag source, + otherwise the drag source must have at least one compatible key. + + \sa QtQuick2::Drag::keys +*/ + +QStringList QQuickDropArea::keys() const +{ + Q_D(const QQuickDropArea); + return d->keys; +} + +void QQuickDropArea::setKeys(const QStringList &keys) +{ + Q_D(QQuickDropArea); + if (d->keys != keys) { + d->keys = keys; + + if (keys.isEmpty()) { + d->keyRegExp = QRegExp(); + } else { + QString pattern = QLatin1Char('(') + QRegExp::escape(keys.first()); + for (int i = 1; i < keys.count(); ++i) + pattern += QLatin1Char('|') + QRegExp::escape(keys.at(i)); + pattern += QLatin1Char(')'); + d->keyRegExp = QRegExp(pattern.replace(QLatin1String("\\*"), QLatin1String(".+"))); + } + emit keysChanged(); + } +} + +QQuickDropAreaDrag *QQuickDropArea::drag() +{ + Q_D(QQuickDropArea); + if (!d->drag) + d->drag = new QQuickDropAreaDrag(d); + return d->drag; +} + +/*! + \qmlproperty Object QtQuick2::DropArea::drag.source + + This property holds the source of a drag. +*/ + +QObject *QQuickDropAreaDrag::source() const +{ + return d->source; +} + +/*! + \qmlproperty qreal QtQuick2::DropArea::drag.x + \qmlproperty qreal QtQuick2::DropArea::drag.y + + These properties hold the coordinates of the last drag event. +*/ + +qreal QQuickDropAreaDrag::x() const +{ + return d->dragPosition.x(); +} + +qreal QQuickDropAreaDrag::y() const +{ + return d->dragPosition.y(); +} + +/*! + \qmlsignal QtQuick2::DropArea::onPositionChanged(DragEvent drag) + + This handler is called when the position of a drag has changed. +*/ + +void QQuickDropArea::dragMoveEvent(QDragMoveEvent *event) +{ + Q_D(QQuickDropArea); + if (!d->mimeData) + return; + + d->dragPosition = event->pos(); + if (d->drag) + emit d->drag->positionChanged(); + + event->accept(); + QQuickDropEvent dragTargetEvent(d, event); + emit positionChanged(&dragTargetEvent); +} + +bool QQuickDropAreaPrivate::hasMatchingKey(const QStringList &keys) const +{ + if (keyRegExp.isEmpty()) + return true; + + foreach (const QString &key, keys) { + if (keyRegExp.exactMatch(key)) + return true; + } + return false; +} + +QStringList QQuickDropAreaPrivate::getKeys(const QMimeData *mimeData) const +{ + if (const QQuickDragMimeData *dragMime = qobject_cast(mimeData)) + return dragMime->keys(); + return mimeData->formats(); +} + +/*! + \qmlsignal QtQuick2::DropArea::onEntered(DragEvent drag) + + This handler is called when a \a drag enters the bounds of a DropArea. +*/ + +void QQuickDropArea::dragEnterEvent(QDragEnterEvent *event) +{ + Q_D(QQuickDropArea); + const QMimeData *mimeData = event->mimeData(); + if (!d->effectiveEnable || d->mimeData || !mimeData || !d->hasMatchingKey(d->getKeys(mimeData))) + return; + + d->dragPosition = event->pos(); + + event->accept(); + QQuickDropEvent dragTargetEvent(d, event); + emit entered(&dragTargetEvent); + + if (event->isAccepted()) { + d->mimeData = const_cast(mimeData); + if (QQuickDragMimeData *dragMime = qobject_cast(d->mimeData)) + d->source = dragMime->source(); + else + d->source = event->source(); + d->dragPosition = event->pos(); + if (d->drag) { + emit d->drag->positionChanged(); + emit d->drag->sourceChanged(); + } + emit containsDragChanged(); + } +} + +/*! + \qmlsignal QtQuick2::DropArea::onExited() + + This handler is called when a drag exits the bounds of a DropArea. +*/ + +void QQuickDropArea::dragLeaveEvent(QDragLeaveEvent *) +{ + Q_D(QQuickDropArea); + if (!d->mimeData) + return; + + emit exited(); + + d->mimeData = 0; + d->source = 0; + emit containsDragChanged(); + if (d->drag) + emit d->drag->sourceChanged(); +} + +/*! + \qmlsignal QtQuick2::DropArea::onDropped(DragEvent drop) + + This handler is called when a drop event occurs within the bounds of a + a DropArea. +*/ + +void QQuickDropArea::dropEvent(QDropEvent *event) +{ + Q_D(QQuickDropArea); + if (!d->mimeData) + return; + + QQuickDropEvent dragTargetEvent(d, event); + emit dropped(&dragTargetEvent); + + d->mimeData = 0; + d->source = 0; + emit containsDragChanged(); + if (d->drag) + emit d->drag->sourceChanged(); +} + +/*! + \qmlclass DragEvent QQuickDragEvent + \inqmlmodule QtQuick 2 + \brief The DragEvent object provides information about a drag event. + + The position of the drag event can be obtained from the \l x and \l y + properties, and the \l keys property identifies the drag keys of the event + \l source. +*/ + +/*! + \qmlproperty real QtQuick2::DragEvent::x + + This property holds the x coordinate of a drag event. +*/ + +/*! + \qmlproperty real QtQuick2::DragEvent::y + + This property holds the y coordinate of a drag event. +*/ + +/*! + \qmlproperty Object QtQuick2::DragEvent::drag.source + + This property holds the source of a drag event. +*/ + +QObject *QQuickDropEvent::source() +{ + if (const QQuickDragMimeData *dragMime = qobject_cast(event->mimeData())) + return dragMime->source(); + else + return event->source(); +} + +/*! + \qmlproperty stringlist QtQuick2::DragEvent::keys + + This property holds a list of keys identifying the data type or source of a + drag event. +*/ + +QStringList QQuickDropEvent::keys() const +{ + return d->getKeys(event->mimeData()); +} + +/*! + \qmlproperty enum QtQuick2::DragEvent::action + + This property holds the action that the \l source is to perform on an accepted drop. + + The drop action may be one of: + + \list + \o Qt.CopyAction Copy the data to the target + \o Qt.MoveAction Move the data from the source to the target + \o Qt.LinkAction Create a link from the source to the target. + \o Qt.IgnoreAction Ignore the action (do nothing with the data). + \endlist +*/ + +/*! + \qmlproperty flags QtQuick2::DragEvent::supportedActions + + This property holds the set of \l {action}{actions} supported by the + drag source. +*/ + +/*! + \qmlproperty real QtQuick2::DragEvent::accepted + + This property holds whether the drag event was accepted by a handler. + + The default value is true. +*/ + +/*! + \qmlmethod void QtQuick2::DragEvent::accept() + \qmlmethod void QtQuick2::DragEvent::accept(enum action) + + Accepts the drag event. + + If an \a action is specified it will overwrite the value of the \l action property. +*/ + +void QQuickDropEvent::accept(QDeclarativeV8Function *args) +{ + Qt::DropAction action = event->dropAction(); + + if (args->Length() >= 1) { + v8::Local v = (*args)[0]; + if (v->IsInt32()) + action = Qt::DropAction(v->Int32Value()); + } + // get action from arguments. + event->setDropAction(action); + event->accept(); +} + + +QT_END_NAMESPACE + diff --git a/src/declarative/items/qquickdroparea_p.h b/src/declarative/items/qquickdroparea_p.h new file mode 100644 index 0000000000..31e2bd7813 --- /dev/null +++ b/src/declarative/items/qquickdroparea_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKDROPAREA_P_H +#define QQUICKDROPAREA_P_H + +#include "qquickitem.h" + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickDropAreaPrivate; +class QQuickDropEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x) + Q_PROPERTY(qreal y READ y) + Q_PROPERTY(QObject *source READ source) + Q_PROPERTY(QStringList keys READ keys) + Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions) + Q_PROPERTY(Qt::DropAction action READ action WRITE setAction RESET resetAction) + Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) +public: + QQuickDropEvent(QQuickDropAreaPrivate *d, QDropEvent *event) : d(d), event(event) {} + + qreal x() const { return event->pos().x(); } + qreal y() const { return event->pos().y(); } + + QObject *source(); + + Qt::DropActions supportedActions() const { return event->possibleActions(); } + Qt::DropAction action() const { return event->dropAction(); } + void setAction(Qt::DropAction action) { event->setDropAction(action); } + void resetAction() { event->setDropAction(event->proposedAction()); } + + QStringList keys() const; + + bool accepted() const { return event->isAccepted(); } + void setAccepted(bool accepted) { event->setAccepted(accepted); } + + Q_INVOKABLE void accept(QDeclarativeV8Function *); + +private: + QQuickDropAreaPrivate *d; + QDropEvent *event; +}; + +class QQuickDropAreaDrag : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x NOTIFY positionChanged) + Q_PROPERTY(qreal y READ y NOTIFY positionChanged) + Q_PROPERTY(QObject *source READ source NOTIFY sourceChanged) +public: + QQuickDropAreaDrag(QQuickDropAreaPrivate *d, QObject *parent = 0); + ~QQuickDropAreaDrag(); + + qreal x() const; + qreal y() const; + QObject *source() const; + +Q_SIGNALS: + void positionChanged(); + void sourceChanged(); + +private: + QQuickDropAreaPrivate *d; + + friend class QQuickDropArea; + friend class QQuickDropAreaPrivate; +}; + +class QQuickDropAreaPrivate; +class Q_AUTOTEST_EXPORT QQuickDropArea : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged) + Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged) + Q_PROPERTY(QQuickDropAreaDrag *drag READ drag CONSTANT) + +public: + QQuickDropArea(QQuickItem *parent=0); + ~QQuickDropArea(); + + bool containsDrag() const; + void setContainsDrag(bool drag); + + QStringList keys() const; + void setKeys(const QStringList &keys); + + QQuickDropAreaDrag *drag(); + +Q_SIGNALS: + void containsDragChanged(); + void keysChanged(); + void sourceChanged(); + + void entered(QQuickDropEvent *drag); + void exited(); + void positionChanged(QQuickDropEvent *drag); + void dropped(QQuickDropEvent *drop); + +protected: + void dragMoveEvent(QDragMoveEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dropEvent(QDropEvent *event); + +private: + Q_DISABLE_COPY(QQuickDropArea) + Q_DECLARE_PRIVATE(QQuickDropArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickDropEvent) +QML_DECLARE_TYPE(QQuickDropArea) + +QT_END_HEADER + +#endif // QQUICKDROPAREA_P_H diff --git a/src/declarative/items/qquickevents.cpp b/src/declarative/items/qquickevents.cpp new file mode 100644 index 0000000000..c546dd305a --- /dev/null +++ b/src/declarative/items/qquickevents.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickevents_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass KeyEvent QQuickKeyEvent + \inqmlmodule QtQuick 2 + \ingroup qml-event-elements + + \brief The KeyEvent object provides information about a key event. + + For example, the following changes the Item's state property when the Enter + key is pressed: + \qml +Item { + focus: true + Keys.onPressed: { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; } +} + \endqml +*/ + +/*! + \qmlproperty int QtQuick2::KeyEvent::key + + This property holds the code of the key that was pressed or released. + + See \l {Qt::Key}{Qt.Key} for the list of keyboard codes. These codes are + independent of the underlying window system. Note that this + function does not distinguish between capital and non-capital + letters, use the text() function (returning the Unicode text the + key generated) for this purpose. + + A value of either 0 or \l {Qt::Key_unknown}{Qt.Key_Unknown} means that the event is not + the result of a known key; for example, it may be the result of + a compose sequence, a keyboard macro, or due to key event + compression. +*/ + +/*! + \qmlproperty string QtQuick2::KeyEvent::text + + This property holds the Unicode text that the key generated. + The text returned can be an empty string in cases where modifier keys, + such as Shift, Control, Alt, and Meta, are being pressed or released. + In such cases \c key will contain a valid value +*/ + +/*! + \qmlproperty bool QtQuick2::KeyEvent::isAutoRepeat + + This property holds whether this event comes from an auto-repeating key. +*/ + +/*! + \qmlproperty int QtQuick2::KeyEvent::count + + This property holds the number of keys involved in this event. If \l KeyEvent::text + is not empty, this is simply the length of the string. +*/ + +/*! + \qmlproperty bool QtQuick2::KeyEvent::accepted + + Setting \a accepted to true prevents the key event from being + propagated to the item's parent. + + Generally, if the item acts on the key event then it should be accepted + so that ancestor items do not also respond to the same event. +*/ + +/*! + \qmlproperty int QtQuick2::KeyEvent::modifiers + + This property holds the keyboard modifier flags that existed immediately + before the event occurred. + + It contains a bitwise combination of: + \list + \o Qt.NoModifier - No modifier key is pressed. + \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. + \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. + \o Qt.AltModifier - An Alt key on the keyboard is pressed. + \o Qt.MetaModifier - A Meta key on the keyboard is pressed. + \o Qt.KeypadModifier - A keypad button is pressed. + \endlist + + For example, to react to a Shift key + Enter key combination: + \qml + Item { + focus: true + Keys.onPressed: { + if ((event.key == Qt.Key_Enter) && (event.modifiers & Qt.ShiftModifier)) + doSomething(); + } + } + \endqml +*/ + + +/*! + \qmlclass MouseEvent QQuickMouseEvent + \inqmlmodule QtQuick 2 + \ingroup qml-event-elements + + \brief The MouseEvent object provides information about a mouse event. + + The position of the mouse can be found via the \l x and \l y properties. + The button that caused the event is available via the \l button property. + + \sa MouseArea +*/ + +/*! + \internal + \class QQuickMouseEvent +*/ + +/*! + \qmlproperty int QtQuick2::MouseEvent::x + \qmlproperty int QtQuick2::MouseEvent::y + + These properties hold the coordinates of the position supplied by the mouse event. +*/ + + +/*! + \qmlproperty bool QtQuick2::MouseEvent::accepted + + Setting \a accepted to true prevents the mouse event from being + propagated to items below this item. + + Generally, if the item acts on the mouse event then it should be accepted + so that items lower in the stacking order do not also respond to the same event. +*/ + +/*! + \qmlproperty enumeration QtQuick2::MouseEvent::button + + This property holds the button that caused the event. It can be one of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist +*/ + +/*! + \qmlproperty bool QtQuick2::MouseEvent::wasHeld + + This property is true if the mouse button has been held pressed longer the + threshold (800ms). +*/ + +/*! + \qmlproperty int QtQuick2::MouseEvent::buttons + + This property holds the mouse buttons pressed when the event was generated. + For mouse move events, this is all buttons that are pressed down. For mouse + press and double click events this includes the button that caused the event. + For mouse release events this excludes the button that caused the event. + + It contains a bitwise combination of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist +*/ + +/*! + \qmlproperty int QtQuick2::MouseEvent::modifiers + + This property holds the keyboard modifier flags that existed immediately + before the event occurred. + + It contains a bitwise combination of: + \list + \o Qt.NoModifier - No modifier key is pressed. + \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. + \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. + \o Qt.AltModifier - An Alt key on the keyboard is pressed. + \o Qt.MetaModifier - A Meta key on the keyboard is pressed. + \o Qt.KeypadModifier - A keypad button is pressed. + \endlist + + For example, to react to a Shift key + Left mouse button click: + \qml + MouseArea { + onClicked: { + if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ShiftModifier)) + doSomething(); + } + } + \endqml +*/ + + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickevents_p_p.h b/src/declarative/items/qquickevents_p_p.h new file mode 100644 index 0000000000..cbe75ab9bf --- /dev/null +++ b/src/declarative/items/qquickevents_p_p.h @@ -0,0 +1,144 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKEVENTS_P_P_H +#define QQUICKEVENTS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickKeyEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int key READ key) + Q_PROPERTY(QString text READ text) + Q_PROPERTY(int modifiers READ modifiers) + Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat) + Q_PROPERTY(int count READ count) + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + +public: + QQuickKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text=QString(), bool autorep=false, ushort count=1) + : event(type, key, modifiers, text, autorep, count) { event.setAccepted(false); } + QQuickKeyEvent(const QKeyEvent &ke) + : event(ke) { event.setAccepted(false); } + + int key() const { return event.key(); } + QString text() const { return event.text(); } + int modifiers() const { return event.modifiers(); } + bool isAutoRepeat() const { return event.isAutoRepeat(); } + int count() const { return event.count(); } + + bool isAccepted() { return event.isAccepted(); } + void setAccepted(bool accepted) { event.setAccepted(accepted); } + +private: + QKeyEvent event; +}; + +// used in QtLocation +class Q_DECLARATIVE_EXPORT QQuickMouseEvent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int x READ x) + Q_PROPERTY(int y READ y) + Q_PROPERTY(int button READ button) + Q_PROPERTY(int buttons READ buttons) + Q_PROPERTY(int modifiers READ modifiers) + Q_PROPERTY(bool wasHeld READ wasHeld) + Q_PROPERTY(bool isClick READ isClick) + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + +public: + QQuickMouseEvent(int x, int y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers + , bool isClick=false, bool wasHeld=false) + : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers) + , _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {} + + int x() const { return _x; } + int y() const { return _y; } + int button() const { return _button; } + int buttons() const { return _buttons; } + int modifiers() const { return _modifiers; } + bool wasHeld() const { return _wasHeld; } + bool isClick() const { return _isClick; } + + // only for internal usage + void setX(int x) { _x = x; } + void setY(int y) { _y = y; } + void setPosition(const QPointF &point) { _x = point.x(); _y = point.y(); } + + bool isAccepted() { return _accepted; } + void setAccepted(bool accepted) { _accepted = accepted; } + +private: + int _x; + int _y; + Qt::MouseButton _button; + Qt::MouseButtons _buttons; + Qt::KeyboardModifiers _modifiers; + bool _wasHeld; + bool _isClick; + bool _accepted; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickKeyEvent) +QML_DECLARE_TYPE(QQuickMouseEvent) + +#endif // QQUICKEVENTS_P_P_H diff --git a/src/declarative/items/qquickflickable.cpp b/src/declarative/items/qquickflickable.cpp new file mode 100644 index 0000000000..655472004d --- /dev/null +++ b/src/declarative/items/qquickflickable.cpp @@ -0,0 +1,1997 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickflickable_p.h" +#include "qquickflickable_p_p.h" +#include "qquickcanvas.h" +#include "qquickcanvas_p.h" + +#include +#include +#include +#include +#include "qplatformdefs.h" + +QT_BEGIN_NAMESPACE + +// The maximum number of pixels a flick can overshoot +#ifndef QML_FLICK_OVERSHOOT +#define QML_FLICK_OVERSHOOT 200 +#endif + +// The number of samples to use in calculating the velocity of a flick +#ifndef QML_FLICK_SAMPLEBUFFER +#define QML_FLICK_SAMPLEBUFFER 3 +#endif + +// The number of samples to discard when calculating the flick velocity. +// Touch panels often produce inaccurate results as the finger is lifted. +#ifndef QML_FLICK_DISCARDSAMPLES +#define QML_FLICK_DISCARDSAMPLES 1 +#endif + +// The default maximum velocity of a flick. +#ifndef QML_FLICK_DEFAULTMAXVELOCITY +#define QML_FLICK_DEFAULTMAXVELOCITY 2500 +#endif + +// The default deceleration of a flick. +#ifndef QML_FLICK_DEFAULTDECELERATION +#define QML_FLICK_DEFAULTDECELERATION 1500 +#endif + +// How much faster to decelerate when overshooting +#ifndef QML_FLICK_OVERSHOOTFRICTION +#define QML_FLICK_OVERSHOOTFRICTION 8 +#endif + +// FlickThreshold determines how far the "mouse" must have moved +// before we perform a flick. +static const int FlickThreshold = 20; + +// RetainGrabVelocity is the maxmimum instantaneous velocity that +// will ensure the Flickable retains the grab on consecutive flicks. +static const int RetainGrabVelocity = 15; + +QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent) + : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.) + , m_yPosition(0.), m_heightRatio(0.) +{ +} + +qreal QQuickFlickableVisibleArea::widthRatio() const +{ + return m_widthRatio; +} + +qreal QQuickFlickableVisibleArea::xPosition() const +{ + return m_xPosition; +} + +qreal QQuickFlickableVisibleArea::heightRatio() const +{ + return m_heightRatio; +} + +qreal QQuickFlickableVisibleArea::yPosition() const +{ + return m_yPosition; +} + +void QQuickFlickableVisibleArea::updateVisible() +{ + QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable); + + bool changeX = false; + bool changeY = false; + bool changeWidth = false; + bool changeHeight = false; + + // Vertical + const qreal viewheight = flickable->height(); + const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent(); + qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight); + qreal pageSize = viewheight / (maxyextent + viewheight); + + if (pageSize != m_heightRatio) { + m_heightRatio = pageSize; + changeHeight = true; + } + if (pagePos != m_yPosition) { + m_yPosition = pagePos; + changeY = true; + } + + // Horizontal + const qreal viewwidth = flickable->width(); + const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent(); + pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth); + pageSize = viewwidth / (maxxextent + viewwidth); + + if (pageSize != m_widthRatio) { + m_widthRatio = pageSize; + changeWidth = true; + } + if (pagePos != m_xPosition) { + m_xPosition = pagePos; + changeX = true; + } + + if (changeX) + emit xPositionChanged(m_xPosition); + if (changeY) + emit yPositionChanged(m_yPosition); + if (changeWidth) + emit widthRatioChanged(m_widthRatio); + if (changeHeight) + emit heightRatioChanged(m_heightRatio); +} + + +QQuickFlickablePrivate::QQuickFlickablePrivate() + : contentItem(new QQuickItem) + , hData(this, &QQuickFlickablePrivate::setViewportX) + , vData(this, &QQuickFlickablePrivate::setViewportY) + , hMoved(false), vMoved(false) + , stealMouse(false), pressed(false), interactive(true), calcVelocity(false) + , pixelAligned(false) + , deceleration(QML_FLICK_DEFAULTDECELERATION) + , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100) + , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400) + , fixupMode(Normal), vTime(0), visibleArea(0) + , flickableDirection(QQuickFlickable::AutoFlickDirection) + , boundsBehavior(QQuickFlickable::DragAndOvershootBounds) +{ +} + +void QQuickFlickablePrivate::init() +{ + Q_Q(QQuickFlickable); + QDeclarative_setParent_noEvent(contentItem, q); + contentItem->setParentItem(q); + FAST_CONNECT(&timeline, SIGNAL(updated()), q, SLOT(ticked())) + FAST_CONNECT(&timeline, SIGNAL(completed()), q, SLOT(movementEnding())) + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildMouseEvents(true); + QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem); + viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); + lastPosTime.invalidate(); +} + +/* + Returns the amount to overshoot by given a velocity. + Will be roughly in range 0 - size/4 +*/ +qreal QQuickFlickablePrivate::overShootDistance(qreal size) +{ + if (maxVelocity <= 0) + return 0.0; + + return qMin(qreal(QML_FLICK_OVERSHOOT), size/3); +} + +void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity) +{ + if (v > maxVelocity) + v = maxVelocity; + else if (v < -maxVelocity) + v = -maxVelocity; + velocityBuffer.append(v); + if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER) + velocityBuffer.remove(0); +} + +void QQuickFlickablePrivate::AxisData::updateVelocity() +{ + velocity = 0; + if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) { + int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES; + for (int i = 0; i < count; ++i) { + qreal v = velocityBuffer.at(i); + velocity += v; + } + velocity /= count; + } +} + +void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom) +{ + Q_Q(QQuickFlickable); + if (item == contentItem) { + if (newGeom.x() != oldGeom.x()) + emit q->contentXChanged(); + if (newGeom.y() != oldGeom.y()) + emit q->contentYChanged(); + } +} + +void QQuickFlickablePrivate::flickX(qreal velocity) +{ + Q_Q(QQuickFlickable); + flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); +} + +void QQuickFlickablePrivate::flickY(qreal velocity) +{ + Q_Q(QQuickFlickable); + flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); +} + +void QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QQuickFlickable); + qreal maxDistance = -1; + data.fixingUp = false; + // -ve velocity means list is moving up + if (velocity > 0) { + maxDistance = qAbs(minExtent - data.move.value()); + data.flickTarget = minExtent; + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + data.flickTarget = maxExtent; + } + if (maxDistance > 0) { + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + timeline.reset(data.move); + if (boundsBehavior == QQuickFlickable::DragAndOvershootBounds) + timeline.accel(data.move, v, deceleration); + else + timeline.accel(data.move, v, deceleration, maxDistance); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!hData.flicking && q->xflick()) { + hData.flicking = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + if (!vData.flicking) + emit q->flickStarted(); + } + if (!vData.flicking && q->yflick()) { + vData.flicking = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + if (!hData.flicking) + emit q->flickStarted(); + } + } else { + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + +void QQuickFlickablePrivate::fixupY_callback(void *data) +{ + ((QQuickFlickablePrivate *)data)->fixupY(); +} + +void QQuickFlickablePrivate::fixupX_callback(void *data) +{ + ((QQuickFlickablePrivate *)data)->fixupX(); +} + +void QQuickFlickablePrivate::fixupX() +{ + Q_Q(QQuickFlickable); + fixup(hData, q->minXExtent(), q->maxXExtent()); +} + +void QQuickFlickablePrivate::fixupY() +{ + Q_Q(QQuickFlickable); + fixup(vData, q->minYExtent(), q->maxYExtent()); +} + +void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if (data.move.value() > minExtent || maxExtent > minExtent) { + timeline.reset(data.move); + if (data.move.value() != minExtent) { + switch (fixupMode) { + case Immediate: + timeline.set(data.move, minExtent); + break; + case ExtentChanged: + // The target has changed. Don't start from the beginning; just complete the + // second half of the animation using the new extent. + timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + break; + default: { + qreal dist = minExtent - data.move; + timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); + timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + } + } + } + } else if (data.move.value() < maxExtent) { + timeline.reset(data.move); + switch (fixupMode) { + case Immediate: + timeline.set(data.move, maxExtent); + break; + case ExtentChanged: + // The target has changed. Don't start from the beginning; just complete the + // second half of the animation using the new extent. + timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + break; + default: { + qreal dist = maxExtent - data.move; + timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); + timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); + data.fixingUp = true; + } + } + } + data.inOvershoot = false; + fixupMode = Normal; + vTime = timeline.time(); +} + +void QQuickFlickablePrivate::updateBeginningEnd() +{ + Q_Q(QQuickFlickable); + bool atBoundaryChange = false; + + // Vertical + const int maxyextent = int(-q->maxYExtent()); + const qreal ypos = -vData.move.value(); + bool atBeginning = (ypos <= -q->minYExtent()); + bool atEnd = (maxyextent <= ypos); + + if (atBeginning != vData.atBeginning) { + vData.atBeginning = atBeginning; + atBoundaryChange = true; + } + if (atEnd != vData.atEnd) { + vData.atEnd = atEnd; + atBoundaryChange = true; + } + + // Horizontal + const int maxxextent = int(-q->maxXExtent()); + const qreal xpos = -hData.move.value(); + atBeginning = (xpos <= -q->minXExtent()); + atEnd = (maxxextent <= xpos); + + if (atBeginning != hData.atBeginning) { + hData.atBeginning = atBeginning; + atBoundaryChange = true; + } + if (atEnd != hData.atEnd) { + hData.atEnd = atEnd; + atBoundaryChange = true; + } + + if (vData.extentsChanged) { + vData.extentsChanged = false; + emit q->yOriginChanged(); + } + + if (hData.extentsChanged) { + hData.extentsChanged = false; + emit q->xOriginChanged(); + } + + if (atBoundaryChange) + emit q->isAtBoundaryChanged(); + + if (visibleArea) + visibleArea->updateVisible(); +} + +/* +XXXTODO add docs describing moving, dragging, flicking properties, e.g. + +When the user starts dragging the Flickable, the dragging and moving properties +will be true. + +If the velocity is sufficient when the drag is ended, flicking may begin. + +The moving properties will remain true until all dragging and flicking +is finished. +*/ + +/*! + \qmlsignal QtQuick2::Flickable::onDragStarted() + + This handler is called when the view starts to be dragged due to user + interaction. +*/ + +/*! + \qmlsignal QtQuick2::Flickable::onDragEnded() + + This handler is called when the user stops dragging the view. + + If the velocity of the drag is suffient at the time the + touch/mouse button is released then a flick will start. +*/ + +/*! + \qmlclass Flickable QQuickFlickable + \inqmlmodule QtQuick 2 + \ingroup qml-basic-interaction-elements + + \brief The Flickable item provides a surface that can be "flicked". + \inherits Item + + The Flickable item places its children on a surface that can be dragged + and flicked, causing the view onto the child items to scroll. This + behavior forms the basis of Items that are designed to show large numbers + of child items, such as \l ListView and \l GridView. + + In traditional user interfaces, views can be scrolled using standard + controls, such as scroll bars and arrow buttons. In some situations, it + is also possible to drag the view directly by pressing and holding a + mouse button while moving the cursor. In touch-based user interfaces, + this dragging action is often complemented with a flicking action, where + scrolling continues after the user has stopped touching the view. + + Flickable does not automatically clip its contents. If it is not used as + a full-screen item, you should consider setting the \l{Item::}{clip} property + to true. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage flickable.gif + \enddiv + + The following example shows a small view onto a large image in which the + user can drag or flick the image in order to view different parts of it. + + \snippet doc/src/snippets/declarative/flickable.qml document + + \clearfloat + + Items declared as children of a Flickable are automatically parented to the + Flickable's \l contentItem. This should be taken into account when + operating on the children of the Flickable; it is usually the children of + \c contentItem that are relevant. For example, the bound of Items added + to the Flickable will be available by \c contentItem.childrenRect + + \section1 Limitations + + \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by + \c id. Use \c parent instead. +*/ + +/*! + \qmlsignal QtQuick2::Flickable::onMovementStarted() + + This handler is called when the view begins moving due to user + interaction. +*/ + +/*! + \qmlsignal QtQuick2::Flickable::onMovementEnded() + + This handler is called when the view stops moving due to user + interaction. If a flick was generated, this handler will + be triggered once the flick stops. If a flick was not + generated, the handler will be triggered when the + user stops dragging - i.e. a mouse or touch release. +*/ + +/*! + \qmlsignal QtQuick2::Flickable::onFlickStarted() + + This handler is called when the view is flicked. A flick + starts from the point that the mouse or touch is released, + while still in motion. +*/ + +/*! + \qmlsignal QtQuick2::Flickable::onFlickEnded() + + This handler is called when the view stops moving due to a flick. +*/ + +/*! + \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition + \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio + \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition + \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio + + These properties describe the position and size of the currently viewed area. + The size is defined as the percentage of the full view currently visible, + scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to + 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio. + However, it is possible for the contents to be dragged outside of the normal + range, resulting in the page positions also being outside the normal range. + + These properties are typically used to draw a scrollbar. For example: + + \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0 + \dots 8 + \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1 + + \sa {declarative/ui-components/scrollbar}{scrollbar example} +*/ +QQuickFlickable::QQuickFlickable(QQuickItem *parent) + : QQuickItem(*(new QQuickFlickablePrivate), parent) +{ + Q_D(QQuickFlickable); + d->init(); +} + +QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent) + : QQuickItem(dd, parent) +{ + Q_D(QQuickFlickable); + d->init(); +} + +QQuickFlickable::~QQuickFlickable() +{ +} + +/*! + \qmlproperty real QtQuick2::Flickable::contentX + \qmlproperty real QtQuick2::Flickable::contentY + + These properties hold the surface coordinate currently at the top-left + corner of the Flickable. For example, if you flick an image up 100 pixels, + \c contentY will be 100. +*/ +qreal QQuickFlickable::contentX() const +{ + Q_D(const QQuickFlickable); + return -d->contentItem->x(); +} + +void QQuickFlickable::setContentX(qreal pos) +{ + Q_D(QQuickFlickable); + d->hData.explicitValue = true; + d->timeline.reset(d->hData.move); + d->vTime = d->timeline.time(); + movementXEnding(); + if (-pos != d->hData.move.value()) { + d->hData.move.setValue(-pos); + viewportMoved(); + } +} + +qreal QQuickFlickable::contentY() const +{ + Q_D(const QQuickFlickable); + return -d->contentItem->y(); +} + +void QQuickFlickable::setContentY(qreal pos) +{ + Q_D(QQuickFlickable); + d->vData.explicitValue = true; + d->timeline.reset(d->vData.move); + d->vTime = d->timeline.time(); + movementYEnding(); + if (-pos != d->vData.move.value()) { + d->vData.move.setValue(-pos); + viewportMoved(); + } +} + +/*! + \qmlproperty bool QtQuick2::Flickable::interactive + + This property describes whether the user can interact with the Flickable. + A user cannot drag or flick a Flickable that is not interactive. + + By default, this property is true. + + This property is useful for temporarily disabling flicking. This allows + special interaction with Flickable's children; for example, you might want + to freeze a flickable map while scrolling through a pop-up dialog that + is a child of the Flickable. +*/ +bool QQuickFlickable::isInteractive() const +{ + Q_D(const QQuickFlickable); + return d->interactive; +} + +void QQuickFlickable::setInteractive(bool interactive) +{ + Q_D(QQuickFlickable); + if (interactive != d->interactive) { + d->interactive = interactive; + if (!interactive && (d->hData.flicking || d->vData.flicking)) { + d->timeline.clear(); + d->vTime = d->timeline.time(); + d->hData.flicking = false; + d->vData.flicking = false; + emit flickingChanged(); + emit flickingHorizontallyChanged(); + emit flickingVerticallyChanged(); + emit flickEnded(); + } + emit interactiveChanged(); + } +} + +/*! + \qmlproperty real QtQuick2::Flickable::horizontalVelocity + \qmlproperty real QtQuick2::Flickable::verticalVelocity + + The instantaneous velocity of movement along the x and y axes, in pixels/sec. + + The reported velocity is smoothed to avoid erratic output. +*/ +qreal QQuickFlickable::horizontalVelocity() const +{ + Q_D(const QQuickFlickable); + return d->hData.smoothVelocity.value(); +} + +qreal QQuickFlickable::verticalVelocity() const +{ + Q_D(const QQuickFlickable); + return d->vData.smoothVelocity.value(); +} + +/*! + \qmlproperty bool QtQuick2::Flickable::atXBeginning + \qmlproperty bool QtQuick2::Flickable::atXEnd + \qmlproperty bool QtQuick2::Flickable::atYBeginning + \qmlproperty bool QtQuick2::Flickable::atYEnd + + These properties are true if the flickable view is positioned at the beginning, + or end respecively. +*/ +bool QQuickFlickable::isAtXEnd() const +{ + Q_D(const QQuickFlickable); + return d->hData.atEnd; +} + +bool QQuickFlickable::isAtXBeginning() const +{ + Q_D(const QQuickFlickable); + return d->hData.atBeginning; +} + +bool QQuickFlickable::isAtYEnd() const +{ + Q_D(const QQuickFlickable); + return d->vData.atEnd; +} + +bool QQuickFlickable::isAtYBeginning() const +{ + Q_D(const QQuickFlickable); + return d->vData.atBeginning; +} + +void QQuickFlickable::ticked() +{ + viewportMoved(); +} + +/*! + \qmlproperty Item QtQuick2::Flickable::contentItem + + The internal item that contains the Items to be moved in the Flickable. + + Items declared as children of a Flickable are automatically parented to the Flickable's contentItem. + + Items created dynamically need to be explicitly parented to the \e contentItem: + \code + Flickable { + id: myFlickable + function addItem(file) { + var component = Qt.createComponent(file) + component.createObject(myFlickable.contentItem); + } + } + \endcode +*/ +QQuickItem *QQuickFlickable::contentItem() +{ + Q_D(QQuickFlickable); + return d->contentItem; +} + +QQuickFlickableVisibleArea *QQuickFlickable::visibleArea() +{ + Q_D(QQuickFlickable); + if (!d->visibleArea) + d->visibleArea = new QQuickFlickableVisibleArea(this); + return d->visibleArea; +} + +/*! + \qmlproperty enumeration QtQuick2::Flickable::flickableDirection + + This property determines which directions the view can be flicked. + + \list + \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the + \e contentHeight is not equal to the \e height of the Flickable. + Allows flicking horizontally if the \e contentWidth is not equal + to the \e width of the Flickable. + \o Flickable.HorizontalFlick - allows flicking horizontally. + \o Flickable.VerticalFlick - allows flicking vertically. + \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions. + \endlist +*/ +QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const +{ + Q_D(const QQuickFlickable); + return d->flickableDirection; +} + +void QQuickFlickable::setFlickableDirection(FlickableDirection direction) +{ + Q_D(QQuickFlickable); + if (direction != d->flickableDirection) { + d->flickableDirection = direction; + emit flickableDirectionChanged(); + } +} + +bool QQuickFlickable::pixelAligned() const +{ + Q_D(const QQuickFlickable); + return d->pixelAligned; +} + +void QQuickFlickable::setPixelAligned(bool align) +{ + Q_D(QQuickFlickable); + if (align != d->pixelAligned) { + d->pixelAligned = align; + emit pixelAlignedChanged(); + } +} + +void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event) +{ + Q_Q(QQuickFlickable); + if (interactive && timeline.isActive() + && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity + || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) { + stealMouse = true; // If we've been flicked then steal the click. + } else { + stealMouse = false; + } + q->setKeepMouseGrab(stealMouse); + pressed = true; + timeline.clear(); + hData.reset(); + vData.reset(); + hData.dragMinBound = q->minXExtent(); + vData.dragMinBound = q->minYExtent(); + hData.dragMaxBound = q->maxXExtent(); + vData.dragMaxBound = q->maxYExtent(); + fixupMode = Normal; + lastPos = QPoint(); + QQuickItemPrivate::start(lastPosTime); + pressPos = event->localPos(); + hData.pressPos = hData.move.value(); + vData.pressPos = vData.move.value(); + hData.flicking = false; + vData.flicking = false; + QQuickItemPrivate::start(pressTime); + QQuickItemPrivate::start(velocityTime); +} + +void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) +{ + Q_Q(QQuickFlickable); + if (!interactive || !lastPosTime.isValid()) + return; + bool rejectY = false; + bool rejectX = false; + + bool stealY = stealMouse; + bool stealX = stealMouse; + + if (q->yflick()) { + int dy = int(event->localPos().y() - pressPos.y()); + if (qAbs(dy) > qApp->styleHints()->startDragDistance() || QQuickItemPrivate::elapsed(pressTime) > 200) { + if (!vMoved) + vData.dragStartOffset = dy; + qreal newY = dy + vData.pressPos - vData.dragStartOffset; + const qreal minY = vData.dragMinBound; + const qreal maxY = vData.dragMaxBound; + if (newY > minY) + newY = minY + (newY - minY) / 2; + if (newY < maxY && maxY - minY <= 0) + newY = maxY + (newY - maxY) / 2; + if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) { + rejectY = true; + if (newY < maxY) { + newY = maxY; + rejectY = false; + } + if (newY > minY) { + newY = minY; + rejectY = false; + } + } + if (!rejectY && stealMouse) { + vData.move.setValue(qRound(newY)); + vMoved = true; + } + if (qAbs(dy) > qApp->styleHints()->startDragDistance()) + stealY = true; + } + } + + if (q->xflick()) { + int dx = int(event->localPos().x() - pressPos.x()); + if (qAbs(dx) > qApp->styleHints()->startDragDistance() || QQuickItemPrivate::elapsed(pressTime) > 200) { + if (!hMoved) + hData.dragStartOffset = dx; + qreal newX = dx + hData.pressPos - hData.dragStartOffset; + const qreal minX = hData.dragMinBound; + const qreal maxX = hData.dragMaxBound; + if (newX > minX) + newX = minX + (newX - minX) / 2; + if (newX < maxX && maxX - minX <= 0) + newX = maxX + (newX - maxX) / 2; + if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) { + rejectX = true; + if (newX < maxX) { + newX = maxX; + rejectX = false; + } + if (newX > minX) { + newX = minX; + rejectX = false; + } + } + if (!rejectX && stealMouse) { + hData.move.setValue(qRound(newX)); + hMoved = true; + } + + if (qAbs(dx) > qApp->styleHints()->startDragDistance()) + stealX = true; + } + } + + stealMouse = stealX || stealY; + if (stealMouse) + q->setKeepMouseGrab(true); + + if (rejectY) { + vData.velocityBuffer.clear(); + vData.velocity = 0; + } + if (rejectX) { + hData.velocityBuffer.clear(); + hData.velocity = 0; + } + + if (hMoved || vMoved) { + draggingStarting(); + q->movementStarting(); + q->viewportMoved(); + } + + if (!lastPos.isNull()) { + qreal elapsed = qreal(QQuickItemPrivate::elapsed(lastPosTime)) / 1000.; + if (elapsed <= 0) + return; + QQuickItemPrivate::restart(lastPosTime); + qreal dy = event->localPos().y()-lastPos.y(); + if (q->yflick() && !rejectY) + vData.addVelocitySample(dy/elapsed, maxVelocity); + qreal dx = event->localPos().x()-lastPos.x(); + if (q->xflick() && !rejectX) + hData.addVelocitySample(dx/elapsed, maxVelocity); + } + + lastPos = event->localPos(); +} + +void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event) +{ + Q_Q(QQuickFlickable); + stealMouse = false; + q->setKeepMouseGrab(false); + pressed = false; + + // if we drag then pause before release we should not cause a flick. + qint64 elapsed = QQuickItemPrivate::elapsed(lastPosTime); + + vData.updateVelocity(); + hData.updateVelocity(); + + draggingEnding(); + + if (!lastPosTime.isValid()) + return; + + vTime = timeline.time(); + + qreal velocity = elapsed < 100 ? vData.velocity : 0; + if (vData.atBeginning || vData.atEnd) + velocity /= 2; + if (q->yflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) { + velocityTimeline.reset(vData.smoothVelocity); + vData.smoothVelocity.setValue(-velocity); + flickY(velocity); + } else { + fixupY(); + } + + velocity = elapsed < 100 ? hData.velocity : 0; + if (hData.atBeginning || hData.atEnd) + velocity /= 2; + if (q->xflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) { + velocityTimeline.reset(hData.smoothVelocity); + hData.smoothVelocity.setValue(-velocity); + flickX(velocity); + } else { + fixupX(); + } + + if (!timeline.isActive()) + q->movementEnding(); +} + +void QQuickFlickable::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickFlickable); + if (d->interactive) { + if (!d->pressed) + d->handleMousePressEvent(event); + event->accept(); + } else { + QQuickItem::mousePressEvent(event); + } +} + +void QQuickFlickable::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickFlickable); + if (d->interactive) { + d->handleMouseMoveEvent(event); + event->accept(); + } else { + QQuickItem::mouseMoveEvent(event); + } +} + +void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickFlickable); + if (d->interactive) { + d->clearDelayedPress(); + d->handleMouseReleaseEvent(event); + event->accept(); + ungrabMouse(); + } else { + QQuickItem::mouseReleaseEvent(event); + } +} + +void QQuickFlickable::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickFlickable); + if (!d->interactive) { + QQuickItem::wheelEvent(event); + } else if (yflick() && event->orientation() == Qt::Vertical) { + bool valid = false; + if (event->delta() > 0 && contentY() > -minYExtent()) { + d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4)); + valid = true; + } else if (event->delta() < 0 && contentY() < -maxYExtent()) { + d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4)); + valid = true; + } + if (valid) { + d->vData.flicking = false; + d->flickY(d->vData.velocity); + if (d->vData.flicking) { + d->vMoved = true; + movementStarting(); + } + event->accept(); + } + } else if (xflick() && event->orientation() == Qt::Horizontal) { + bool valid = false; + if (event->delta() > 0 && contentX() > -minXExtent()) { + d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4)); + valid = true; + } else if (event->delta() < 0 && contentX() < -maxXExtent()) { + d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4)); + valid = true; + } + if (valid) { + d->hData.flicking = false; + d->flickX(d->hData.velocity); + if (d->hData.flicking) { + d->hMoved = true; + movementStarting(); + } + event->accept(); + } + } else { + QQuickItem::wheelEvent(event); + } +} + +bool QQuickFlickablePrivate::isOutermostPressDelay() const +{ + Q_Q(const QQuickFlickable); + QQuickItem *item = q->parentItem(); + while (item) { + QQuickFlickable *flick = qobject_cast(item); + if (flick && flick->pressDelay() > 0 && flick->isInteractive()) + return false; + item = item->parentItem(); + } + + return true; +} + +void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event) +{ + Q_Q(QQuickFlickable); + if (!q->canvas() || pressDelay <= 0) + return; + if (!isOutermostPressDelay()) + return; + delayedPressTarget = q->canvas()->mouseGrabberItem(); + delayedPressEvent = new QMouseEvent(*event); + delayedPressEvent->setAccepted(false); + delayedPressTimer.start(pressDelay, q); +} + +void QQuickFlickablePrivate::clearDelayedPress() +{ + if (delayedPressEvent) { + delayedPressTimer.stop(); + delete delayedPressEvent; + delayedPressEvent = 0; + } +} + +//XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned. +void QQuickFlickablePrivate::setViewportX(qreal x) +{ + contentItem->setX(pixelAligned ? qRound(x) : x); +} + +void QQuickFlickablePrivate::setViewportY(qreal y) +{ + contentItem->setY(pixelAligned ? qRound(y) : y); +} + +void QQuickFlickable::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickFlickable); + if (event->timerId() == d->delayedPressTimer.timerId()) { + d->delayedPressTimer.stop(); + if (d->delayedPressEvent) { + QQuickItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0; + if (!grabber || grabber != this) { + // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) + // so we reset the grabber + if (canvas()->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + // Use the event handler that will take care of finding the proper item to propagate the event + QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent); + } + delete d->delayedPressEvent; + d->delayedPressEvent = 0; + } + } +} + +qreal QQuickFlickable::minYExtent() const +{ + Q_D(const QQuickFlickable); + return d->vData.startMargin; +} + +qreal QQuickFlickable::minXExtent() const +{ + Q_D(const QQuickFlickable); + return d->hData.startMargin; +} + +/* returns -ve */ +qreal QQuickFlickable::maxXExtent() const +{ + Q_D(const QQuickFlickable); + return width() - vWidth() - d->hData.endMargin; +} +/* returns -ve */ +qreal QQuickFlickable::maxYExtent() const +{ + Q_D(const QQuickFlickable); + return height() - vHeight() - d->vData.endMargin; +} + +void QQuickFlickable::componentComplete() +{ + Q_D(QQuickFlickable); + QQuickItem::componentComplete(); + if (!d->hData.explicitValue && d->hData.startMargin != 0.) + setContentX(-minXExtent()); + if (!d->vData.explicitValue && d->vData.startMargin != 0.) + setContentY(-minYExtent()); +} + +void QQuickFlickable::viewportMoved() +{ + Q_D(QQuickFlickable); + + qreal prevX = d->lastFlickablePosition.x(); + qreal prevY = d->lastFlickablePosition.y(); + if (d->pressed || d->calcVelocity) { + int elapsed = QQuickItemPrivate::restart(d->velocityTime); + if (elapsed > 0) { + qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed; + if (qAbs(horizontalVelocity) > 0) { + d->velocityTimeline.reset(d->hData.smoothVelocity); + d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing); + } + qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed; + if (qAbs(verticalVelocity) > 0) { + d->velocityTimeline.reset(d->vData.smoothVelocity); + d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing); + d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing); + } + } + } else { + if (d->timeline.time() > d->vTime) { + d->velocityTimeline.clear(); + qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime); + qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime); + d->hData.smoothVelocity.setValue(horizontalVelocity); + d->vData.smoothVelocity.setValue(verticalVelocity); + } + } + + if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking + && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent()) + && qAbs(d->vData.smoothVelocity.value()) > 100) { + // Increase deceleration if we've passed a bound + d->vData.inOvershoot = true; + qreal maxDistance = d->overShootDistance(height()); + d->timeline.reset(d->vData.move); + d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance); + d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d)); + } + if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking + && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent()) + && qAbs(d->hData.smoothVelocity.value()) > 100) { + // Increase deceleration if we've passed a bound + d->hData.inOvershoot = true; + qreal maxDistance = d->overShootDistance(width()); + d->timeline.reset(d->hData.move); + d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance); + d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d)); + } + + d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value()); + + d->vTime = d->timeline.time(); + d->updateBeginningEnd(); +} + +void QQuickFlickable::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QQuickFlickable); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + bool changed = false; + if (newGeometry.width() != oldGeometry.width()) { + if (xflick()) + changed = true; + if (d->hData.viewSize < 0) { + d->contentItem->setWidth(width()); + emit contentWidthChanged(); + } + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupX(); + } + } + if (newGeometry.height() != oldGeometry.height()) { + if (yflick()) + changed = true; + if (d->vData.viewSize < 0) { + d->contentItem->setHeight(height()); + emit contentHeightChanged(); + } + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupY(); + } + } + + if (changed) + d->updateBeginningEnd(); +} + +void QQuickFlickable::cancelFlick() +{ + Q_D(QQuickFlickable); + d->timeline.reset(d->hData.move); + d->timeline.reset(d->vData.move); + movementEnding(); +} + +void QQuickFlickablePrivate::data_append(QDeclarativeListProperty *prop, QObject *o) +{ + QQuickItem *i = qobject_cast(o); + if (i) { + i->setParentItem(static_cast(prop->data)->contentItem); + } else { + o->setParent(prop->object); // XXX todo - do we want this? + } +} + +int QQuickFlickablePrivate::data_count(QDeclarativeListProperty *) +{ + // XXX todo + return 0; +} + +QObject *QQuickFlickablePrivate::data_at(QDeclarativeListProperty *, int) +{ + // XXX todo + return 0; +} + +void QQuickFlickablePrivate::data_clear(QDeclarativeListProperty *) +{ + // XXX todo +} + +QDeclarativeListProperty QQuickFlickable::flickableData() +{ + Q_D(QQuickFlickable); + return QDeclarativeListProperty(this, (void *)d, QQuickFlickablePrivate::data_append, + QQuickFlickablePrivate::data_count, + QQuickFlickablePrivate::data_at, + QQuickFlickablePrivate::data_clear); +} + +QDeclarativeListProperty QQuickFlickable::flickableChildren() +{ + Q_D(QQuickFlickable); + return QQuickItemPrivate::get(d->contentItem)->children(); +} + +/*! + \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior + This property holds whether the surface may be dragged + beyond the Fickable's boundaries, or overshoot the + Flickable's boundaries when flicked. + + This enables the feeling that the edges of the view are soft, + rather than a hard physical boundary. + + The \c boundsBehavior can be one of: + + \list + \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary + of the flickable, and flicks will not overshoot. + \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary + of the Flickable, but flicks will not overshoot. + \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged + beyond the boundary of the Flickable, and can overshoot the + boundary when flicked. + \endlist +*/ +QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const +{ + Q_D(const QQuickFlickable); + return d->boundsBehavior; +} + +void QQuickFlickable::setBoundsBehavior(BoundsBehavior b) +{ + Q_D(QQuickFlickable); + if (b == d->boundsBehavior) + return; + d->boundsBehavior = b; + emit boundsBehaviorChanged(); +} + +/*! + \qmlproperty real QtQuick2::Flickable::contentWidth + \qmlproperty real QtQuick2::Flickable::contentHeight + + The dimensions of the content (the surface controlled by Flickable). + This should typically be set to the combined size of the items placed in the + Flickable. + + The following snippet shows how these properties are used to display + an image that is larger than the Flickable item itself: + + \snippet doc/src/snippets/declarative/flickable.qml document + + In some cases, the the content dimensions can be automatically set + using the \l {Item::childrenRect.width}{childrenRect.width} + and \l {Item::childrenRect.height}{childrenRect.height} properties. +*/ +qreal QQuickFlickable::contentWidth() const +{ + Q_D(const QQuickFlickable); + return d->hData.viewSize; +} + +void QQuickFlickable::setContentWidth(qreal w) +{ + Q_D(QQuickFlickable); + if (d->hData.viewSize == w) + return; + d->hData.viewSize = w; + if (w < 0) + d->contentItem->setWidth(width()); + else + d->contentItem->setWidth(w); + d->hData.markExtentsDirty(); + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupX(); + } else if (!d->pressed && d->hData.fixingUp) { + d->fixupMode = QQuickFlickablePrivate::ExtentChanged; + d->fixupX(); + } + emit contentWidthChanged(); + d->updateBeginningEnd(); +} + +qreal QQuickFlickable::contentHeight() const +{ + Q_D(const QQuickFlickable); + return d->vData.viewSize; +} + +void QQuickFlickable::setContentHeight(qreal h) +{ + Q_D(QQuickFlickable); + if (d->vData.viewSize == h) + return; + d->vData.viewSize = h; + if (h < 0) + d->contentItem->setHeight(height()); + else + d->contentItem->setHeight(h); + d->vData.markExtentsDirty(); + // Make sure that we're entirely in view. + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupY(); + } else if (!d->pressed && d->vData.fixingUp) { + d->fixupMode = QQuickFlickablePrivate::ExtentChanged; + d->fixupY(); + } + emit contentHeightChanged(); + d->updateBeginningEnd(); +} + +/*! + \qmlproperty real QtQuick2::Flickable::topMargin + \qmlproperty real QtQuick2::Flickable::leftMargin + \qmlproperty real QtQuick2::Flickable::bottomMargin + \qmlproperty real QtQuick2::Flickable::rightMargin + + These properties hold the margins around the content. This space is reserved + in addition to the contentWidth and contentHeight. +*/ + + +qreal QQuickFlickable::topMargin() const +{ + Q_D(const QQuickFlickable); + return d->vData.startMargin; +} + +void QQuickFlickable::setTopMargin(qreal m) +{ + Q_D(QQuickFlickable); + if (d->vData.startMargin == m) + return; + d->vData.startMargin = m; + d->vData.markExtentsDirty(); + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupY(); + } + emit topMarginChanged(); + d->updateBeginningEnd(); +} + +qreal QQuickFlickable::bottomMargin() const +{ + Q_D(const QQuickFlickable); + return d->vData.endMargin; +} + +void QQuickFlickable::setBottomMargin(qreal m) +{ + Q_D(QQuickFlickable); + if (d->vData.endMargin == m) + return; + d->vData.endMargin = m; + d->vData.markExtentsDirty(); + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupY(); + } + emit bottomMarginChanged(); + d->updateBeginningEnd(); +} + +qreal QQuickFlickable::leftMargin() const +{ + Q_D(const QQuickFlickable); + return d->hData.startMargin; +} + +void QQuickFlickable::setLeftMargin(qreal m) +{ + Q_D(QQuickFlickable); + if (d->hData.startMargin == m) + return; + d->hData.startMargin = m; + d->hData.markExtentsDirty(); + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupX(); + } + emit leftMarginChanged(); + d->updateBeginningEnd(); +} + +qreal QQuickFlickable::rightMargin() const +{ + Q_D(const QQuickFlickable); + return d->hData.endMargin; +} + +void QQuickFlickable::setRightMargin(qreal m) +{ + Q_D(QQuickFlickable); + if (d->hData.endMargin == m) + return; + d->hData.endMargin = m; + d->hData.markExtentsDirty(); + if (!d->pressed && !d->hData.moving && !d->vData.moving) { + d->fixupMode = QQuickFlickablePrivate::Immediate; + d->fixupX(); + } + emit rightMarginChanged(); + d->updateBeginningEnd(); +} + +/*! + \qmlproperty real QtQuick2::Flickable::xOrigin + \qmlproperty real QtQuick2::Flickable::yOrigin + + These properties hold the origin of the content. This is usually (0,0), however + ListView and GridView may have an arbitrary origin due to delegate size variation, + or item insertion/removal outside the visible region. +*/ + +qreal QQuickFlickable::yOrigin() const +{ + Q_D(const QQuickFlickable); + return -minYExtent() + d->vData.startMargin; +} + +qreal QQuickFlickable::xOrigin() const +{ + Q_D(const QQuickFlickable); + return -minXExtent() + d->hData.startMargin; +} + + +/*! + \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center) + + Resizes the content to \a width x \a height about \a center. + + This does not scale the contents of the Flickable - it only resizes the \l contentWidth + and \l contentHeight. + + Resizing the content may result in the content being positioned outside + the bounds of the Flickable. Calling \l returnToBounds() will + move the content back within legal bounds. +*/ +void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center) +{ + Q_D(QQuickFlickable); + if (w != d->hData.viewSize) { + qreal oldSize = d->hData.viewSize; + d->hData.viewSize = w; + d->contentItem->setWidth(w); + emit contentWidthChanged(); + if (center.x() != 0) { + qreal pos = center.x() * w / oldSize; + setContentX(contentX() + pos - center.x()); + } + } + if (h != d->vData.viewSize) { + qreal oldSize = d->vData.viewSize; + d->vData.viewSize = h; + d->contentItem->setHeight(h); + emit contentHeightChanged(); + if (center.y() != 0) { + qreal pos = center.y() * h / oldSize; + setContentY(contentY() + pos - center.y()); + } + } + d->updateBeginningEnd(); +} + +/*! + \qmlmethod QtQuick2::Flickable::returnToBounds() + + Ensures the content is within legal bounds. + + This may be called to ensure that the content is within legal bounds + after manually positioning the content. +*/ +void QQuickFlickable::returnToBounds() +{ + Q_D(QQuickFlickable); + d->fixupX(); + d->fixupY(); +} + +qreal QQuickFlickable::vWidth() const +{ + Q_D(const QQuickFlickable); + if (d->hData.viewSize < 0) + return width(); + else + return d->hData.viewSize; +} + +qreal QQuickFlickable::vHeight() const +{ + Q_D(const QQuickFlickable); + if (d->vData.viewSize < 0) + return height(); + else + return d->vData.viewSize; +} + +bool QQuickFlickable::xflick() const +{ + Q_D(const QQuickFlickable); + if (d->flickableDirection == QQuickFlickable::AutoFlickDirection) + return vWidth() != width(); + return d->flickableDirection & QQuickFlickable::HorizontalFlick; +} + +bool QQuickFlickable::yflick() const +{ + Q_D(const QQuickFlickable); + if (d->flickableDirection == QQuickFlickable::AutoFlickDirection) + return vHeight() != height(); + return d->flickableDirection & QQuickFlickable::VerticalFlick; +} + +void QQuickFlickable::mouseUngrabEvent() +{ + Q_D(QQuickFlickable); + if (d->pressed) { + // if our mouse grab has been removed (probably by another Flickable), + // fix our state + d->pressed = false; + d->draggingEnding(); + d->stealMouse = false; + setKeepMouseGrab(false); + } +} + +bool QQuickFlickable::sendMouseEvent(QMouseEvent *event) +{ + Q_D(QQuickFlickable); + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + bool disabledItem = grabber && !grabber->isEnabled(); + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) { + QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + + mouseEvent.setAccepted(false); + + switch (mouseEvent.type()) { + case QEvent::MouseMove: + d->handleMouseMoveEvent(&mouseEvent); + break; + case QEvent::MouseButtonPress: + if (d->pressed) // we are already pressed - this is a delayed replay + return false; + + d->handleMousePressEvent(&mouseEvent); + d->captureDelayedPress(event); + stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above + break; + case QEvent::MouseButtonRelease: + if (d->delayedPressEvent) { + // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) + // so we reset the grabber + if (c->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + //Use the event handler that will take care of finding the proper item to propagate the event + QQuickCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent); + d->clearDelayedPress(); + // We send the release + canvas()->sendEvent(c->mouseGrabberItem(), event); + // And the event has been consumed + d->stealMouse = false; + d->pressed = false; + return true; + } + d->handleMouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = qobject_cast(c->mouseGrabberItem()); + if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) { + d->clearDelayedPress(); + grabMouse(); + } + + return stealThisEvent || d->delayedPressEvent || disabledItem; + } else if (d->lastPosTime.isValid()) { + d->lastPosTime.invalidate(); + returnToBounds(); + } + if (event->type() == QEvent::MouseButtonRelease) { + d->lastPosTime.invalidate(); + d->clearDelayedPress(); + d->stealMouse = false; + d->pressed = false; + } + return false; +} + + +bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e) +{ + Q_D(QQuickFlickable); + if (!isVisible() || !d->interactive || !isEnabled()) + return QQuickItem::childMouseEventFilter(i, e); + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast(e)); + default: + break; + } + + return QQuickItem::childMouseEventFilter(i, e); +} + +/*! + \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity + This property holds the maximum velocity that the user can flick the view in pixels/second. + + The default value is platform dependent. +*/ +qreal QQuickFlickable::maximumFlickVelocity() const +{ + Q_D(const QQuickFlickable); + return d->maxVelocity; +} + +void QQuickFlickable::setMaximumFlickVelocity(qreal v) +{ + Q_D(QQuickFlickable); + if (v == d->maxVelocity) + return; + d->maxVelocity = v; + emit maximumFlickVelocityChanged(); +} + +/*! + \qmlproperty real QtQuick2::Flickable::flickDeceleration + This property holds the rate at which a flick will decelerate. + + The default value is platform dependent. +*/ +qreal QQuickFlickable::flickDeceleration() const +{ + Q_D(const QQuickFlickable); + return d->deceleration; +} + +void QQuickFlickable::setFlickDeceleration(qreal deceleration) +{ + Q_D(QQuickFlickable); + if (deceleration == d->deceleration) + return; + d->deceleration = deceleration; + emit flickDecelerationChanged(); +} + +bool QQuickFlickable::isFlicking() const +{ + Q_D(const QQuickFlickable); + return d->hData.flicking || d->vData.flicking; +} + +/*! + \qmlproperty bool QtQuick2::Flickable::flicking + \qmlproperty bool QtQuick2::Flickable::flickingHorizontally + \qmlproperty bool QtQuick2::Flickable::flickingVertically + + These properties describe whether the view is currently moving horizontally, + vertically or in either direction, due to the user flicking the view. +*/ +bool QQuickFlickable::isFlickingHorizontally() const +{ + Q_D(const QQuickFlickable); + return d->hData.flicking; +} + +bool QQuickFlickable::isFlickingVertically() const +{ + Q_D(const QQuickFlickable); + return d->vData.flicking; +} + +/*! + \qmlproperty bool QtQuick2::Flickable::dragging + \qmlproperty bool QtQuick2::Flickable::draggingHorizontally + \qmlproperty bool QtQuick2::Flickable::draggingVertically + + These properties describe whether the view is currently moving horizontally, + vertically or in either direction, due to the user dragging the view. +*/ +bool QQuickFlickable::isDragging() const +{ + Q_D(const QQuickFlickable); + return d->hData.dragging || d->vData.dragging; +} + +bool QQuickFlickable::isDraggingHorizontally() const +{ + Q_D(const QQuickFlickable); + return d->hData.dragging; +} + +bool QQuickFlickable::isDraggingVertically() const +{ + Q_D(const QQuickFlickable); + return d->vData.dragging; +} + +void QQuickFlickablePrivate::draggingStarting() +{ + Q_Q(QQuickFlickable); + bool wasDragging = hData.dragging || vData.dragging; + if (hMoved && !hData.dragging) { + hData.dragging = true; + emit q->draggingHorizontallyChanged(); + } + if (vMoved && !vData.dragging) { + vData.dragging = true; + emit q->draggingVerticallyChanged(); + } + if (!wasDragging && (hData.dragging || vData.dragging)) { + emit q->draggingChanged(); + emit q->dragStarted(); + } +} + +void QQuickFlickablePrivate::draggingEnding() +{ + Q_Q(QQuickFlickable); + bool wasDragging = hData.dragging || vData.dragging; + if (hData.dragging) { + hData.dragging = false; + emit q->draggingHorizontallyChanged(); + } + if (vData.dragging) { + vData.dragging = false; + emit q->draggingVerticallyChanged(); + } + if (wasDragging && !hData.dragging && !vData.dragging) { + emit q->draggingChanged(); + emit q->dragEnded(); + } +} + +/*! + \qmlproperty int QtQuick2::Flickable::pressDelay + + This property holds the time to delay (ms) delivering a press to + children of the Flickable. This can be useful where reacting + to a press before a flicking action has undesirable effects. + + If the flickable is dragged/flicked before the delay times out + the press event will not be delivered. If the button is released + within the timeout, both the press and release will be delivered. + + Note that for nested Flickables with pressDelay set, the pressDelay of + inner Flickables is overridden by the outermost Flickable. +*/ +int QQuickFlickable::pressDelay() const +{ + Q_D(const QQuickFlickable); + return d->pressDelay; +} + +void QQuickFlickable::setPressDelay(int delay) +{ + Q_D(QQuickFlickable); + if (d->pressDelay == delay) + return; + d->pressDelay = delay; + emit pressDelayChanged(); +} + +/*! + \qmlproperty bool QtQuick2::Flickable::moving + \qmlproperty bool QtQuick2::Flickable::movingHorizontally + \qmlproperty bool QtQuick2::Flickable::movingVertically + + These properties describe whether the view is currently moving horizontally, + vertically or in either direction, due to the user either dragging or + flicking the view. +*/ + +bool QQuickFlickable::isMoving() const +{ + Q_D(const QQuickFlickable); + return d->hData.moving || d->vData.moving; +} + +bool QQuickFlickable::isMovingHorizontally() const +{ + Q_D(const QQuickFlickable); + return d->hData.moving; +} + +bool QQuickFlickable::isMovingVertically() const +{ + Q_D(const QQuickFlickable); + return d->vData.moving; +} + +void QQuickFlickable::movementStarting() +{ + Q_D(QQuickFlickable); + if (d->hMoved && !d->hData.moving) { + d->hData.moving = true; + emit movingChanged(); + emit movingHorizontallyChanged(); + if (!d->vData.moving) + emit movementStarted(); + } + else if (d->vMoved && !d->vData.moving) { + d->vData.moving = true; + emit movingChanged(); + emit movingVerticallyChanged(); + if (!d->hData.moving) + emit movementStarted(); + } +} + +void QQuickFlickable::movementEnding() +{ + Q_D(QQuickFlickable); + movementXEnding(); + movementYEnding(); + d->hData.smoothVelocity.setValue(0); + d->vData.smoothVelocity.setValue(0); +} + +void QQuickFlickable::movementXEnding() +{ + Q_D(QQuickFlickable); + if (d->hData.flicking) { + d->hData.flicking = false; + emit flickingChanged(); + emit flickingHorizontallyChanged(); + if (!d->vData.flicking) + emit flickEnded(); + } + if (!d->pressed && !d->stealMouse) { + if (d->hData.moving) { + d->hData.moving = false; + d->hMoved = false; + emit movingChanged(); + emit movingHorizontallyChanged(); + if (!d->vData.moving) + emit movementEnded(); + } + } + d->hData.fixingUp = false; +} + +void QQuickFlickable::movementYEnding() +{ + Q_D(QQuickFlickable); + if (d->vData.flicking) { + d->vData.flicking = false; + emit flickingChanged(); + emit flickingVerticallyChanged(); + if (!d->hData.flicking) + emit flickEnded(); + } + if (!d->pressed && !d->stealMouse) { + if (d->vData.moving) { + d->vData.moving = false; + d->vMoved = false; + emit movingChanged(); + emit movingVerticallyChanged(); + if (!d->hData.moving) + emit movementEnded(); + } + } + d->vData.fixingUp = false; +} + +void QQuickFlickablePrivate::updateVelocity() +{ + Q_Q(QQuickFlickable); + emit q->horizontalVelocityChanged(); + emit q->verticalVelocityChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickflickable_p.h b/src/declarative/items/qquickflickable_p.h new file mode 100644 index 0000000000..fe4ac40efb --- /dev/null +++ b/src/declarative/items/qquickflickable_p.h @@ -0,0 +1,277 @@ +// Commit: 1bcddaaf318fc37c71c5191913f3487c49444ec6 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFLICKABLE_P_H +#define QQUICKFLICKABLE_P_H + +#include "qquickitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickFlickablePrivate; +class QQuickFlickableVisibleArea; +class Q_AUTOTEST_EXPORT QQuickFlickable : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged) + Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged) + Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged) + Q_PROPERTY(QQuickItem *contentItem READ contentItem CONSTANT) + + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) + Q_PROPERTY(qreal yOrigin READ yOrigin NOTIFY yOriginChanged) + + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal xOrigin READ xOrigin NOTIFY xOriginChanged) + + Q_PROPERTY(qreal horizontalVelocity READ horizontalVelocity NOTIFY horizontalVelocityChanged) + Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged) + + Q_PROPERTY(BoundsBehavior boundsBehavior READ boundsBehavior WRITE setBoundsBehavior NOTIFY boundsBehaviorChanged) + Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) + Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) + Q_PROPERTY(bool movingHorizontally READ isMovingHorizontally NOTIFY movingHorizontallyChanged) + Q_PROPERTY(bool movingVertically READ isMovingVertically NOTIFY movingVerticallyChanged) + Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) + Q_PROPERTY(bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged) + Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged) + Q_PROPERTY(bool dragging READ isDragging NOTIFY draggingChanged) + Q_PROPERTY(bool draggingHorizontally READ isDraggingHorizontally NOTIFY draggingHorizontallyChanged) + Q_PROPERTY(bool draggingVertically READ isDraggingVertically NOTIFY draggingVerticallyChanged) + Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE setFlickableDirection NOTIFY flickableDirectionChanged) + + Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) + Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay NOTIFY pressDelayChanged) + + Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY isAtBoundaryChanged) + Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY isAtBoundaryChanged) + Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY isAtBoundaryChanged) + Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY isAtBoundaryChanged) + + Q_PROPERTY(QQuickFlickableVisibleArea *visibleArea READ visibleArea CONSTANT) + + Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged) + + Q_PROPERTY(QDeclarativeListProperty flickableData READ flickableData) + Q_PROPERTY(QDeclarativeListProperty flickableChildren READ flickableChildren) + Q_CLASSINFO("DefaultProperty", "flickableData") + + Q_ENUMS(FlickableDirection) + Q_ENUMS(BoundsBehavior) + +public: + QQuickFlickable(QQuickItem *parent=0); + ~QQuickFlickable(); + + QDeclarativeListProperty flickableData(); + QDeclarativeListProperty flickableChildren(); + + enum BoundsBehavior { StopAtBounds, DragOverBounds, DragAndOvershootBounds }; + BoundsBehavior boundsBehavior() const; + void setBoundsBehavior(BoundsBehavior); + + qreal contentWidth() const; + void setContentWidth(qreal); + + qreal contentHeight() const; + void setContentHeight(qreal); + + qreal contentX() const; + virtual void setContentX(qreal pos); + + qreal contentY() const; + virtual void setContentY(qreal pos); + + qreal topMargin() const; + void setTopMargin(qreal m); + + qreal bottomMargin() const; + void setBottomMargin(qreal m); + + qreal leftMargin() const; + void setLeftMargin(qreal m); + + qreal rightMargin() const; + void setRightMargin(qreal m); + + virtual qreal yOrigin() const; + virtual qreal xOrigin() const; + + bool isMoving() const; + bool isMovingHorizontally() const; + bool isMovingVertically() const; + bool isFlicking() const; + bool isFlickingHorizontally() const; + bool isFlickingVertically() const; + bool isDragging() const; + bool isDraggingHorizontally() const; + bool isDraggingVertically() const; + + int pressDelay() const; + void setPressDelay(int delay); + + qreal maximumFlickVelocity() const; + void setMaximumFlickVelocity(qreal); + + qreal flickDeceleration() const; + void setFlickDeceleration(qreal); + + bool isInteractive() const; + void setInteractive(bool); + + qreal horizontalVelocity() const; + qreal verticalVelocity() const; + + bool isAtXEnd() const; + bool isAtXBeginning() const; + bool isAtYEnd() const; + bool isAtYBeginning() const; + + QQuickItem *contentItem(); + + enum FlickableDirection { AutoFlickDirection=0x00, HorizontalFlick=0x01, VerticalFlick=0x02, HorizontalAndVerticalFlick=0x03 }; + FlickableDirection flickableDirection() const; + void setFlickableDirection(FlickableDirection); + + bool pixelAligned() const; + void setPixelAligned(bool align); + + Q_INVOKABLE void resizeContent(qreal w, qreal h, QPointF center); + Q_INVOKABLE void returnToBounds(); + +Q_SIGNALS: + void contentWidthChanged(); + void contentHeightChanged(); + void contentXChanged(); + void contentYChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void leftMarginChanged(); + void rightMarginChanged(); + void yOriginChanged(); + void xOriginChanged(); + void movingChanged(); + void movingHorizontallyChanged(); + void movingVerticallyChanged(); + void flickingChanged(); + void flickingHorizontallyChanged(); + void flickingVerticallyChanged(); + void draggingChanged(); + void draggingHorizontallyChanged(); + void draggingVerticallyChanged(); + void horizontalVelocityChanged(); + void verticalVelocityChanged(); + void isAtBoundaryChanged(); + void flickableDirectionChanged(); + void interactiveChanged(); + void boundsBehaviorChanged(); + void maximumFlickVelocityChanged(); + void flickDecelerationChanged(); + void pressDelayChanged(); + void movementStarted(); + void movementEnded(); + void flickStarted(); + void flickEnded(); + void dragStarted(); + void dragEnded(); + void pixelAlignedChanged(); + +protected: + virtual bool childMouseEventFilter(QQuickItem *, QEvent *); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void wheelEvent(QWheelEvent *event); + virtual void timerEvent(QTimerEvent *event); + + QQuickFlickableVisibleArea *visibleArea(); + +protected Q_SLOTS: + virtual void ticked(); + void movementStarting(); + void movementEnding(); + +protected: + void movementXEnding(); + void movementYEnding(); + virtual qreal minXExtent() const; + virtual qreal minYExtent() const; + virtual qreal maxXExtent() const; + virtual qreal maxYExtent() const; + qreal vWidth() const; + qreal vHeight() const; + virtual void componentComplete(); + virtual void viewportMoved(); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + void mouseUngrabEvent(); + bool sendMouseEvent(QMouseEvent *event); + + bool xflick() const; + bool yflick() const; + void cancelFlick(); + +protected: + QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent); + +private: + Q_DISABLE_COPY(QQuickFlickable) + Q_DECLARE_PRIVATE(QQuickFlickable) + friend class QQuickFlickableVisibleArea; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFlickable) + +QT_END_HEADER + +#endif // QQUICKFLICKABLE_P_H diff --git a/src/declarative/items/qquickflickable_p_p.h b/src/declarative/items/qquickflickable_p_p.h new file mode 100644 index 0000000000..8ceea6162f --- /dev/null +++ b/src/declarative/items/qquickflickable_p_p.h @@ -0,0 +1,262 @@ +// Commit: 160f1867868cdea916923652b00484ed11f90aaa +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFLICKABLE_P_P_H +#define QQUICKFLICKABLE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickflickable_p.h" +#include "qquickitem_p.h" +#include "qquickitemchangelistener_p.h" + +#include +#include +#include "qplatformdefs.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +// Really slow flicks can be annoying. +const qreal MinimumFlickVelocity = 75.0; + +class QQuickFlickableVisibleArea; +class QQuickFlickablePrivate : public QQuickItemPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickFlickable) + +public: + static inline QQuickFlickablePrivate *get(QQuickFlickable *o) { return o->d_func(); } + + QQuickFlickablePrivate(); + void init(); + + struct Velocity : public QDeclarativeTimeLineValue + { + Velocity(QQuickFlickablePrivate *p) + : parent(p) {} + virtual void setValue(qreal v) { + if (v != value()) { + QDeclarativeTimeLineValue::setValue(v); + parent->updateVelocity(); + } + } + QQuickFlickablePrivate *parent; + }; + + struct AxisData { + AxisData(QQuickFlickablePrivate *fp, void (QQuickFlickablePrivate::*func)(qreal)) + : move(fp, func), viewSize(-1), startMargin(0), endMargin(0) + , smoothVelocity(fp), atEnd(false), atBeginning(true) + , fixingUp(false), inOvershoot(false), moving(false), flicking(false) + , dragging(false), extentsChanged(false) + , explicitValue(false), minExtentDirty(true), maxExtentDirty(true) + {} + + void reset() { + velocityBuffer.clear(); + dragStartOffset = 0; + fixingUp = false; + inOvershoot = false; + } + + void markExtentsDirty() { + minExtentDirty = true; + maxExtentDirty = true; + extentsChanged = true; + } + + void addVelocitySample(qreal v, qreal maxVelocity); + void updateVelocity(); + + QDeclarativeTimeLineValueProxy move; + qreal viewSize; + qreal pressPos; + qreal dragStartOffset; + qreal dragMinBound; + qreal dragMaxBound; + qreal velocity; + qreal flickTarget; + qreal startMargin; + qreal endMargin; + QQuickFlickablePrivate::Velocity smoothVelocity; + QPODVector velocityBuffer; + bool atEnd : 1; + bool atBeginning : 1; + bool fixingUp : 1; + bool inOvershoot : 1; + bool moving : 1; + bool flicking : 1; + bool dragging : 1; + bool extentsChanged : 1; + bool explicitValue : 1; + mutable bool minExtentDirty : 1; + mutable bool maxExtentDirty : 1; + }; + + void flickX(qreal velocity); + void flickY(qreal velocity); + virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + + void fixupX(); + void fixupY(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + + void updateBeginningEnd(); + + bool isOutermostPressDelay() const; + void captureDelayedPress(QMouseEvent *event); + void clearDelayedPress(); + + void setViewportX(qreal x); + void setViewportY(qreal y); + + qreal overShootDistance(qreal size); + + void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &); + + void draggingStarting(); + void draggingEnding(); + +public: + QQuickItem *contentItem; + + AxisData hData; + AxisData vData; + + QDeclarativeTimeLine timeline; + bool hMoved : 1; + bool vMoved : 1; + bool stealMouse : 1; + bool pressed : 1; + bool interactive : 1; + bool calcVelocity : 1; + bool pixelAligned : 1; + QElapsedTimer lastPosTime; + QPointF lastPos; + QPointF pressPos; + QElapsedTimer pressTime; + qreal deceleration; + qreal maxVelocity; + QElapsedTimer velocityTime; + QPointF lastFlickablePosition; + qreal reportedVelocitySmoothing; + QMouseEvent *delayedPressEvent; + QQuickItem *delayedPressTarget; + QBasicTimer delayedPressTimer; + int pressDelay; + int fixupDuration; + + enum FixupMode { Normal, Immediate, ExtentChanged }; + FixupMode fixupMode; + + static void fixupY_callback(void *); + static void fixupX_callback(void *); + + void updateVelocity(); + int vTime; + QDeclarativeTimeLine velocityTimeline; + QQuickFlickableVisibleArea *visibleArea; + QQuickFlickable::FlickableDirection flickableDirection; + QQuickFlickable::BoundsBehavior boundsBehavior; + + void handleMousePressEvent(QMouseEvent *); + void handleMouseMoveEvent(QMouseEvent *); + void handleMouseReleaseEvent(QMouseEvent *); + + // flickableData property + static void data_append(QDeclarativeListProperty *, QObject *); + static int data_count(QDeclarativeListProperty *); + static QObject *data_at(QDeclarativeListProperty *, int); + static void data_clear(QDeclarativeListProperty *); +}; + +class QQuickFlickableVisibleArea : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged) + Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged) + Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged) + Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged) + +public: + QQuickFlickableVisibleArea(QQuickFlickable *parent=0); + + qreal xPosition() const; + qreal widthRatio() const; + qreal yPosition() const; + qreal heightRatio() const; + + void updateVisible(); + +signals: + void xPositionChanged(qreal xPosition); + void yPositionChanged(qreal yPosition); + void widthRatioChanged(qreal widthRatio); + void heightRatioChanged(qreal heightRatio); + +private: + QQuickFlickable *flickable; + qreal m_xPosition; + qreal m_widthRatio; + qreal m_yPosition; + qreal m_heightRatio; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFlickableVisibleArea) + +#endif // QQUICKFLICKABLE_P_P_H diff --git a/src/declarative/items/qquickflipable.cpp b/src/declarative/items/qquickflipable.cpp new file mode 100644 index 0000000000..ed89f2aaf5 --- /dev/null +++ b/src/declarative/items/qquickflipable.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickflipable_p.h" +#include "qquickitem_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +// XXX todo - i think this needs work and a bit of a re-think + +class QQuickLocalTransform : public QQuickTransform +{ + Q_OBJECT +public: + QQuickLocalTransform(QObject *parent) : QQuickTransform(parent) {} + + void setTransform(const QTransform &t) { + transform = t; + update(); + } + virtual void applyTo(QMatrix4x4 *matrix) const { + *matrix *= transform; + } +private: + QTransform transform; +}; + +class QQuickFlipablePrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickFlipable) +public: + QQuickFlipablePrivate() : current(QQuickFlipable::Front), front(0), back(0), sideDirty(false) {} + + virtual void transformChanged(); + void updateSide(); + void setBackTransform(); + + QQuickFlipable::Side current; + QDeclarativeGuard backTransform; + QDeclarativeGuard front; + QDeclarativeGuard back; + + bool sideDirty; + bool wantBackXFlipped; + bool wantBackYFlipped; +}; + +/*! + \qmlclass Flipable QQuickFlipable + \inqmlmodule QtQuick 2 + \ingroup qml-basic-interaction-elements + \brief The Flipable item provides a surface that can be flipped. + \inherits Item + + Flipable is an item that can be visibly "flipped" between its front and + back sides, like a card. It is used together with \l Rotation, \l State + and \l Transition elements to produce a flipping effect. + + The \l front and \l back properties are used to hold the items that are + shown respectively on the front and back sides of the flipable item. + + \section1 Example Usage + + The following example shows a Flipable item that flips whenever it is + clicked, rotating about the y-axis. + + This flipable item has a \c flipped boolean property that is toggled + whenever the MouseArea within the flipable is clicked. When + \c flipped is true, the item changes to the "back" state; in this + state, the \c angle of the \l Rotation item is changed to 180 + degrees to produce the flipping effect. When \c flipped is false, the + item reverts to the default state, in which the \c angle value is 0. + + \snippet doc/src/snippets/declarative/flipable/flipable.qml 0 + + \image flipable.gif + + The \l Transition creates the animation that changes the angle over + four seconds. When the item changes between its "back" and + default states, the NumberAnimation animates the angle between + its old and new values. + + See \l {QML States} for details on state changes and the default + state, and \l {QML Animation and Transitions} for more information on how + animations work within transitions. + + \sa {declarative/ui-components/flipable}{Flipable example} +*/ +QQuickFlipable::QQuickFlipable(QQuickItem *parent) +: QQuickItem(*(new QQuickFlipablePrivate), parent) +{ +} + +QQuickFlipable::~QQuickFlipable() +{ +} + +/*! + \qmlproperty Item QtQuick2::Flipable::front + \qmlproperty Item QtQuick2::Flipable::back + + The front and back sides of the flipable. +*/ + +QQuickItem *QQuickFlipable::front() +{ + Q_D(const QQuickFlipable); + return d->front; +} + +void QQuickFlipable::setFront(QQuickItem *front) +{ + Q_D(QQuickFlipable); + if (d->front) { + qmlInfo(this) << tr("front is a write-once property"); + return; + } + d->front = front; + d->front->setParentItem(this); + if (Back == d->current) + d->front->setOpacity(0.); + emit frontChanged(); +} + +QQuickItem *QQuickFlipable::back() +{ + Q_D(const QQuickFlipable); + return d->back; +} + +void QQuickFlipable::setBack(QQuickItem *back) +{ + Q_D(QQuickFlipable); + if (d->back) { + qmlInfo(this) << tr("back is a write-once property"); + return; + } + if (back == 0) + return; + d->back = back; + d->back->setParentItem(this); + + d->backTransform = new QQuickLocalTransform(d->back); + d->backTransform->prependToItem(d->back); + + if (Front == d->current) + d->back->setOpacity(0.); + connect(back, SIGNAL(widthChanged()), + this, SLOT(retransformBack())); + connect(back, SIGNAL(heightChanged()), + this, SLOT(retransformBack())); + emit backChanged(); +} + +void QQuickFlipable::retransformBack() +{ + Q_D(QQuickFlipable); + if (d->current == QQuickFlipable::Back && d->back) + d->setBackTransform(); +} + +/*! + \qmlproperty enumeration QtQuick2::Flipable::side + + The side of the Flipable currently visible. Possible values are \c + Flipable.Front and \c Flipable.Back. +*/ +QQuickFlipable::Side QQuickFlipable::side() const +{ + Q_D(const QQuickFlipable); + + const_cast(d)->updateSide(); + return d->current; +} + +void QQuickFlipablePrivate::transformChanged() +{ + Q_Q(QQuickFlipable); + + if (!sideDirty) { + sideDirty = true; + q->polish(); + } + + QQuickItemPrivate::transformChanged(); +} + +void QQuickFlipable::updatePolish() +{ + Q_D(QQuickFlipable); + d->updateSide(); +} + +// determination on the currently visible side of the flipable +// has to be done on the complete scene transform to give +// correct results. +void QQuickFlipablePrivate::updateSide() +{ + Q_Q(QQuickFlipable); + + if (!sideDirty) + return; + + sideDirty = false; + + QTransform sceneTransform; + itemToParentTransform(sceneTransform); + + QPointF p1(0, 0); + QPointF p2(1, 0); + QPointF p3(1, 1); + + QPointF scenep1 = sceneTransform.map(p1); + QPointF scenep2 = sceneTransform.map(p2); + QPointF scenep3 = sceneTransform.map(p3); +#if 0 + p1 = q->mapToParent(p1); + p2 = q->mapToParent(p2); + p3 = q->mapToParent(p3); +#endif + + qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) - + (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x()); + + wantBackYFlipped = scenep1.x() >= scenep2.x(); + wantBackXFlipped = scenep2.y() >= scenep3.y(); + + QQuickFlipable::Side newSide; + if (cross > 0) { + newSide = QQuickFlipable::Back; + } else { + newSide = QQuickFlipable::Front; + } + + if (newSide != current) { + current = newSide; + if (current == QQuickFlipable::Back && back) + setBackTransform(); + if (front) + front->setOpacity((current==QQuickFlipable::Front)?1.:0.); + if (back) + back->setOpacity((current==QQuickFlipable::Back)?1.:0.); + emit q->sideChanged(); + } +} + +/* Depends on the width/height of the back item, and so needs reevaulating + if those change. +*/ +void QQuickFlipablePrivate::setBackTransform() +{ + QTransform mat; + mat.translate(back->width()/2,back->height()/2); + if (back->width() && wantBackYFlipped) + mat.rotate(180, Qt::YAxis); + if (back->height() && wantBackXFlipped) + mat.rotate(180, Qt::XAxis); + mat.translate(-back->width()/2,-back->height()/2); + + if (backTransform) + backTransform->setTransform(mat); +} + +QT_END_NAMESPACE + +#include "qquickflipable.moc" diff --git a/src/declarative/items/qquickflipable_p.h b/src/declarative/items/qquickflipable_p.h new file mode 100644 index 0000000000..698ec0fcb2 --- /dev/null +++ b/src/declarative/items/qquickflipable_p.h @@ -0,0 +1,104 @@ +// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFLIPABLE_P_H +#define QQUICKFLIPABLE_P_H + +#include "qquickitem.h" + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickFlipablePrivate; +class Q_AUTOTEST_EXPORT QQuickFlipable : public QQuickItem +{ + Q_OBJECT + + Q_ENUMS(Side) + Q_PROPERTY(QQuickItem *front READ front WRITE setFront NOTIFY frontChanged) + Q_PROPERTY(QQuickItem *back READ back WRITE setBack NOTIFY backChanged) + Q_PROPERTY(Side side READ side NOTIFY sideChanged) + //### flipAxis + //### flipRotation +public: + QQuickFlipable(QQuickItem *parent=0); + ~QQuickFlipable(); + + QQuickItem *front(); + void setFront(QQuickItem *); + + QQuickItem *back(); + void setBack(QQuickItem *); + + enum Side { Front, Back }; + Side side() const; + +Q_SIGNALS: + void frontChanged(); + void backChanged(); + void sideChanged(); + +protected: + virtual void updatePolish(); + +private Q_SLOTS: + void retransformBack(); + +private: + Q_DISABLE_COPY(QQuickFlipable) + Q_DECLARE_PRIVATE(QQuickFlipable) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFlipable) + +QT_END_HEADER + +#endif // QQUICKFLIPABLE_P_H diff --git a/src/declarative/items/qquickfocusscope.cpp b/src/declarative/items/qquickfocusscope.cpp new file mode 100644 index 0000000000..b807fe58f6 --- /dev/null +++ b/src/declarative/items/qquickfocusscope.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickfocusscope_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass FocusScope QQuickFocusScope + \inqmlmodule QtQuick 2 + \ingroup qml-basic-interaction-elements + + \brief The FocusScope object explicitly creates a focus scope. + \inherits Item + + Focus scopes assist in keyboard focus handling when building reusable QML + components. All the details are covered in the + \l {qmlfocus}{keyboard focus documentation}. + + \sa {declarative/keyinteraction/focus}{Keyboard focus example} +*/ +QQuickFocusScope::QQuickFocusScope(QQuickItem *parent) +: QQuickItem(parent) +{ + setFlag(ItemIsFocusScope); +} + +QQuickFocusScope::~QQuickFocusScope() +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickfocusscope_p.h b/src/declarative/items/qquickfocusscope_p.h new file mode 100644 index 0000000000..ff1a389f62 --- /dev/null +++ b/src/declarative/items/qquickfocusscope_p.h @@ -0,0 +1,68 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFOCUSSCOPE_P_H +#define QQUICKFOCUSSCOPE_P_H + +#include "qquickitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QQuickFocusScope : public QQuickItem +{ + Q_OBJECT +public: + QQuickFocusScope(QQuickItem *parent=0); + virtual ~QQuickFocusScope(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFocusScope) + +QT_END_HEADER + +#endif // QQUICKFOCUSSCOPE_P_H diff --git a/src/declarative/items/qquickgridview.cpp b/src/declarative/items/qquickgridview.cpp new file mode 100644 index 0000000000..a194772a1e --- /dev/null +++ b/src/declarative/items/qquickgridview.cpp @@ -0,0 +1,1930 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickgridview_p.h" +#include "qquickvisualitemmodel_p.h" +#include "qquickflickable_p_p.h" +#include "qquickitemview_p_p.h" + +#include +#include + +#include +#include +#include +#include +#include "qplatformdefs.h" + +QT_BEGIN_NAMESPACE + +#ifndef QML_FLICK_SNAPONETHRESHOLD +#define QML_FLICK_SNAPONETHRESHOLD 30 +#endif + +//---------------------------------------------------------------------------- + +class FxGridItemSG : public FxViewItem +{ +public: + FxGridItemSG(QQuickItem *i, QQuickGridView *v, bool own) : FxViewItem(i, own), view(v) { + attached = static_cast(qmlAttachedPropertiesObject(item)); + if (attached) + static_cast(attached)->setView(view); + } + + ~FxGridItemSG() {} + + qreal position() const { + return rowPos(); + } + + qreal endPosition() const { + return endRowPos(); + } + + qreal size() const { + return view->flow() == QQuickGridView::LeftToRight ? view->cellHeight() : view->cellWidth(); + } + + qreal sectionSize() const { + return 0.0; + } + + qreal rowPos() const { + if (view->flow() == QQuickGridView::LeftToRight) + return item->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-item->x() : item->x()); + } + + qreal colPos() const { + if (view->flow() == QQuickGridView::LeftToRight) { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) { + qreal colSize = view->cellWidth(); + int columns = view->width()/colSize; + return colSize * (columns-1) - item->x(); + } else { + return item->x(); + } + } else { + return item->y(); + } + } + qreal endRowPos() const { + if (view->flow() == QQuickGridView::LeftToRight) { + return item->y() + view->cellHeight(); + } else { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) + return -item->x(); + else + return item->x() + view->cellWidth(); + } + } + void setPosition(qreal col, qreal row) { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) { + if (view->flow() == QQuickGridView::LeftToRight) { + int columns = view->width()/view->cellWidth(); + item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row)); + } else { + item->setPos(QPointF(-view->cellWidth()-row, col)); + } + } else { + if (view->flow() == QQuickGridView::LeftToRight) + item->setPos(QPointF(col, row)); + else + item->setPos(QPointF(row, col)); + } + } + bool contains(qreal x, qreal y) const { + return (x >= item->x() && x < item->x() + view->cellWidth() && + y >= item->y() && y < item->y() + view->cellHeight()); + } + + QQuickGridView *view; +}; + +//---------------------------------------------------------------------------- + +class QQuickGridViewPrivate : public QQuickItemViewPrivate +{ + Q_DECLARE_PUBLIC(QQuickGridView) + +public: + virtual Qt::Orientation layoutOrientation() const; + virtual bool isContentFlowReversed() const; + bool isRightToLeftTopToBottom() const; + + virtual qreal positionAt(int index) const; + virtual qreal endPositionAt(int index) const; + virtual qreal originPosition() const; + virtual qreal lastPosition() const; + + qreal rowSize() const; + qreal colSize() const; + qreal colPosAt(int modelIndex) const; + qreal rowPosAt(int modelIndex) const; + qreal snapPosAt(qreal pos) const; + FxViewItem *snapItemAt(qreal pos) const; + int snapIndex() const; + + virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer); + virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo); + virtual void visibleItemsChanged(); + + virtual FxViewItem *newViewItem(int index, QQuickItem *item); + virtual void repositionPackageItemAt(QQuickItem *item, int index); + virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem); + virtual void resetFirstItemPosition(); + virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards); + + virtual void createHighlight(); + virtual void updateHighlight(); + virtual void resetHighlightPosition(); + + virtual void setPosition(qreal pos); + virtual void layoutVisibleItems(); + bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *, InsertionsResult *); + + virtual qreal headerSize() const; + virtual qreal footerSize() const; + virtual bool showHeaderForIndex(int index) const; + virtual bool showFooterForIndex(int index) const; + virtual void updateHeader(); + virtual void updateFooter(); + + virtual void changedVisibleIndex(int newIndex); + virtual void initializeCurrentItem(); + + virtual void updateViewport(); + virtual void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + virtual void fixupPosition(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + + QQuickGridView::Flow flow; + qreal cellWidth; + qreal cellHeight; + int columns; + QQuickGridView::SnapMode snapMode; + + QSmoothedAnimation *highlightXAnimator; + QSmoothedAnimation *highlightYAnimator; + + QQuickGridViewPrivate() + : flow(QQuickGridView::LeftToRight) + , cellWidth(100), cellHeight(100), columns(1) + , snapMode(QQuickGridView::NoSnap) + , highlightXAnimator(0), highlightYAnimator(0) + {} +}; + +Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const +{ + return flow == QQuickGridView::LeftToRight ? Qt::Vertical : Qt::Horizontal; +} + +bool QQuickGridViewPrivate::isContentFlowReversed() const +{ + return isRightToLeftTopToBottom(); +} + +bool QQuickGridViewPrivate::isRightToLeftTopToBottom() const +{ + Q_Q(const QQuickGridView); + return flow == QQuickGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft; +} + +void QQuickGridViewPrivate::changedVisibleIndex(int newIndex) +{ + visibleIndex = newIndex / columns * columns; +} + +void QQuickGridViewPrivate::setPosition(qreal pos) +{ + Q_Q(QQuickGridView); + if (flow == QQuickGridView::LeftToRight) { + q->QQuickFlickable::setContentY(pos); + q->QQuickFlickable::setContentX(0); + } else { + if (q->effectiveLayoutDirection() == Qt::LeftToRight) + q->QQuickFlickable::setContentX(pos); + else + q->QQuickFlickable::setContentX(-pos-size()); + q->QQuickFlickable::setContentY(0); + } +} + +qreal QQuickGridViewPrivate::originPosition() const +{ + qreal pos = 0; + if (!visibleItems.isEmpty()) + pos = static_cast(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize(); + return pos; +} + +qreal QQuickGridViewPrivate::lastPosition() const +{ + qreal pos = 0; + if (model && model->count()) { + // get end position of last item + pos = (rowPosAt(model->count() - 1) + rowSize()); + } + return pos; +} + +qreal QQuickGridViewPrivate::positionAt(int index) const +{ + return rowPosAt(index); +} + +qreal QQuickGridViewPrivate::endPositionAt(int index) const +{ + return rowPosAt(index) + rowSize(); +} + +qreal QQuickGridViewPrivate::rowSize() const { + return flow == QQuickGridView::LeftToRight ? cellHeight : cellWidth; +} +qreal QQuickGridViewPrivate::colSize() const { + return flow == QQuickGridView::LeftToRight ? cellWidth : cellHeight; +} + +qreal QQuickGridViewPrivate::colPosAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return static_cast(item)->colPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = (visibleIndex - modelIndex) % columns; + int col = static_cast(visibleItems.first())->colPos() / colSize(); + col = (columns - count + col) % columns; + return col * colSize(); + } else { + int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; + return static_cast(visibleItems.last())->colPos() - count * colSize(); + } + } + return (modelIndex % columns) * colSize(); +} + +qreal QQuickGridViewPrivate::rowPosAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return static_cast(item)->rowPos(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + int firstCol = firstItem->colPos() / colSize(); + int col = visibleIndex - modelIndex + (columns - firstCol - 1); + int rows = col / columns; + return firstItem->rowPos() - rows * rowSize(); + } else { + FxGridItemSG *lastItem = static_cast(visibleItems.last()); + int count = modelIndex - lastItem->index; + int col = lastItem->colPos() + count * colSize(); + int rows = col / (columns * colSize()); + return lastItem->rowPos() + rows * rowSize(); + } + } + return (modelIndex / columns) * rowSize(); +} + + +qreal QQuickGridViewPrivate::snapPosAt(qreal pos) const +{ + Q_Q(const QQuickGridView); + qreal snapPos = 0; + if (!visibleItems.isEmpty()) { + qreal highlightStart = highlightRangeStart; + if (isRightToLeftTopToBottom()) + highlightStart = highlightRangeEndValid ? -size() + highlightRangeEnd : -size(); + + pos += highlightStart; + pos += rowSize()/2; + snapPos = static_cast(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize(); + snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); + snapPos -= highlightStart; + qreal maxExtent; + qreal minExtent; + if (isRightToLeftTopToBottom()) { + maxExtent = q->minXExtent(); + minExtent = q->maxXExtent(); + } else { + maxExtent = flow == QQuickGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + minExtent = flow == QQuickGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + } + if (snapPos > maxExtent) + snapPos = maxExtent; + if (snapPos < minExtent) + snapPos = minExtent; + } + return snapPos; +} + +FxViewItem *QQuickGridViewPrivate::snapItemAt(qreal pos) const +{ + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == -1) + continue; + qreal itemTop = item->position(); + if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos) + return item; + } + return 0; +} + +int QQuickGridViewPrivate::snapIndex() const +{ + int index = currentIndex; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItemSG *item = static_cast(visibleItems.at(i)); + if (item->index == -1) + continue; + qreal itemTop = item->position(); + FxGridItemSG *hItem = static_cast(highlight); + if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) { + index = item->index; + if (item->colPos() >= hItem->colPos()-colSize()/2 && item->colPos() < hItem->colPos()+colSize()/2) + return item->index; + } + } + return index; +} + +FxViewItem *QQuickGridViewPrivate::newViewItem(int modelIndex, QQuickItem *item) +{ + Q_Q(QQuickGridView); + Q_UNUSED(modelIndex); + return new FxGridItemSG(item, q, false); +} + +bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer) +{ + int colPos = colPosAt(visibleIndex); + int rowPos = rowPosAt(visibleIndex); + if (visibleItems.count()) { + FxGridItemSG *lastItem = static_cast(visibleItems.last()); + rowPos = lastItem->rowPos(); + colPos = lastItem->colPos() + colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + } + + int modelIndex = findLastVisibleIndex(); + modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1; + + if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2 + || fillTo < rowPosAt(visibleIndex) - rowSize())) { + // We've jumped more than a page. Estimate which items are now + // visible and fill from there. + int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns; + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + modelIndex += count; + if (modelIndex >= model->count()) + modelIndex = model->count() - 1; + else if (modelIndex < 0) + modelIndex = 0; + modelIndex = modelIndex / columns * columns; + visibleIndex = modelIndex; + colPos = colPosAt(visibleIndex); + rowPos = rowPosAt(visibleIndex); + } + + int colNum = colPos / colSize(); + FxGridItemSG *item = 0; + bool changed = false; + + while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { +// qDebug() << "refill: append item" << modelIndex; + if (!(item = static_cast(createItem(modelIndex)))) + break; + item->setPosition(colPos, rowPos); + visibleItems.append(item); + colPos += colSize(); + colNum++; + if (colPos > colSize() * (columns-1)) { + colPos = 0; + colNum = 0; + rowPos += rowSize(); + } + ++modelIndex; + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + + if (visibleItems.count()) { + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + rowPos = firstItem->rowPos(); + colPos = firstItem->colPos() - colSize(); + if (colPos < 0) { + colPos = colSize() * (columns - 1); + rowPos -= rowSize(); + } + } + + colNum = colPos / colSize(); + while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){ +// qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos; + if (!(item = static_cast(createItem(visibleIndex-1)))) + break; + --visibleIndex; + item->setPosition(colPos, rowPos); + visibleItems.prepend(item); + colPos -= colSize(); + colNum--; + if (colPos < 0) { + colPos = colSize() * (columns - 1); + colNum = columns-1; + rowPos -= rowSize(); + } + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + + return changed; +} + +bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) +{ + FxGridItemSG *item = 0; + bool changed = false; + + while (visibleItems.count() > 1 + && (item = static_cast(visibleItems.first())) + && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (visibleItems.count() > 1 + && (item = static_cast(visibleItems.last())) + && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; + visibleItems.removeLast(); + releaseItem(item); + changed = true; + } + + return changed; +} + +void QQuickGridViewPrivate::visibleItemsChanged() +{ + updateHeader(); + updateFooter(); + updateViewport(); +} + +void QQuickGridViewPrivate::updateViewport() +{ + Q_Q(QQuickGridView); + columns = (int)qMax((flow == QQuickGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); + QQuickItemViewPrivate::updateViewport(); +} + +void QQuickGridViewPrivate::layoutVisibleItems() +{ + if (visibleItems.count()) { + FxGridItemSG *firstItem = static_cast(visibleItems.first()); + qreal rowPos = firstItem->rowPos(); + qreal colPos = firstItem->colPos(); + int col = visibleIndex % columns; + if (colPos != col * colSize()) { + colPos = col * colSize(); + firstItem->setPosition(colPos, rowPos); + } + for (int i = 1; i < visibleItems.count(); ++i) { + FxGridItemSG *item = static_cast(visibleItems.at(i)); + colPos += colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + item->setPosition(colPos, rowPos); + } + } +} + +void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index) +{ + Q_Q(QQuickGridView); + qreal pos = position(); + if (flow == QQuickGridView::LeftToRight) { + if (item->y() + item->height() > pos && item->y() < pos + q->height()) + item->setPos(QPointF(colPosAt(index), rowPosAt(index))); + } else { + if (item->x() + item->width() > pos && item->x() < pos + q->width()) { + if (isRightToLeftTopToBottom()) + item->setPos(QPointF(-rowPosAt(index)-item->width(), colPosAt(index))); + else + item->setPos(QPointF(rowPosAt(index), colPosAt(index))); + } + } +} + +void QQuickGridViewPrivate::resetItemPosition(FxViewItem *item, FxViewItem *toItem) +{ + if (item == toItem) + return; + FxGridItemSG *toGridItem = static_cast(toItem); + static_cast(item)->setPosition(toGridItem->colPos(), toGridItem->rowPos()); +} + +void QQuickGridViewPrivate::resetFirstItemPosition() +{ + FxGridItemSG *item = static_cast(visibleItems.first()); + item->setPosition(0, 0); +} + +void QQuickGridViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards) +{ + int moveCount = (forwards / rowSize()) - (backwards / rowSize()); + + FxGridItemSG *gridItem = static_cast(item); + gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize())); +} + +void QQuickGridViewPrivate::createHighlight() +{ + Q_Q(QQuickGridView); + bool changed = false; + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + delete highlight; + highlight = 0; + + delete highlightXAnimator; + delete highlightYAnimator; + highlightXAnimator = 0; + highlightYAnimator = 0; + + changed = true; + } + + if (currentItem) { + QQuickItem *item = createHighlightItem(); + if (item) { + FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true); + if (autoHighlight) + resetHighlightPosition(); + highlightXAnimator = new QSmoothedAnimation(q); + highlightXAnimator->target = QDeclarativeProperty(item, QLatin1String("x")); + highlightXAnimator->userDuration = highlightMoveDuration; + highlightYAnimator = new QSmoothedAnimation(q); + highlightYAnimator->target = QDeclarativeProperty(item, QLatin1String("y")); + highlightYAnimator->userDuration = highlightMoveDuration; + + highlight = newHighlight; + changed = true; + } + } + if (changed) + emit q->highlightItemChanged(); +} + +void QQuickGridViewPrivate::updateHighlight() +{ + applyPendingChanges(); + + if ((!currentItem && highlight) || (currentItem && !highlight)) + createHighlight(); + bool strictHighlight = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange; + if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) { + // auto-update highlight + highlightXAnimator->to = currentItem->item->x(); + highlightYAnimator->to = currentItem->item->y(); + highlight->item->setWidth(currentItem->item->width()); + highlight->item->setHeight(currentItem->item->height()); + + highlightXAnimator->restart(); + highlightYAnimator->restart(); + } + updateTrackedItem(); +} + +void QQuickGridViewPrivate::resetHighlightPosition() +{ + if (highlight && currentItem) { + FxGridItemSG *cItem = static_cast(currentItem); + static_cast(highlight)->setPosition(cItem->colPos(), cItem->rowPos()); + } +} + +qreal QQuickGridViewPrivate::headerSize() const +{ + if (!header) + return 0.0; + return flow == QQuickGridView::LeftToRight ? header->item->height() : header->item->width(); +} + +qreal QQuickGridViewPrivate::footerSize() const +{ + if (!footer) + return 0.0; + return flow == QQuickGridView::LeftToRight? footer->item->height() : footer->item->width(); +} + +bool QQuickGridViewPrivate::showHeaderForIndex(int index) const +{ + return index / columns == 0; +} + +bool QQuickGridViewPrivate::showFooterForIndex(int index) const +{ + return index / columns == (model->count()-1) / columns; +} + +void QQuickGridViewPrivate::updateFooter() +{ + Q_Q(QQuickGridView); + bool created = false; + if (!footer) { + QQuickItem *item = createComponentItem(footerComponent, true); + if (!item) + return; + item->setZ(1); + footer = new FxGridItemSG(item, q, true); + created = true; + } + + FxGridItemSG *gridItem = static_cast(footer); + qreal colOffset = 0; + qreal rowOffset = 0; + if (q->effectiveLayoutDirection() == Qt::RightToLeft) { + if (flow == QQuickGridView::TopToBottom) + rowOffset = gridItem->item->width() - cellWidth; + else + colOffset = gridItem->item->width() - cellWidth; + } + if (visibleItems.count()) { + qreal endPos = lastPosition(); + if (findLastVisibleIndex() == model->count()-1) { + gridItem->setPosition(colOffset, endPos + rowOffset); + } else { + qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size(); + if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset) + gridItem->setPosition(colOffset, endPos + rowOffset); + } + } else { + gridItem->setPosition(colOffset, rowOffset); + } + + if (created) + emit q->footerItemChanged(); +} + +void QQuickGridViewPrivate::updateHeader() +{ + Q_Q(QQuickGridView); + bool created = false; + if (!header) { + QQuickItem *item = createComponentItem(headerComponent, true); + if (!item) + return; + item->setZ(1); + header = new FxGridItemSG(item, q, true); + created = true; + } + + FxGridItemSG *gridItem = static_cast(header); + qreal colOffset = 0; + qreal rowOffset = -headerSize(); + if (q->effectiveLayoutDirection() == Qt::RightToLeft) { + if (flow == QQuickGridView::TopToBottom) + rowOffset += gridItem->item->width()-cellWidth; + else + colOffset = gridItem->item->width()-cellWidth; + } + if (visibleItems.count()) { + qreal startPos = originPosition(); + if (visibleIndex == 0) { + gridItem->setPosition(colOffset, startPos + rowOffset); + } else { + qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position(); + qreal headerPos = isRightToLeftTopToBottom() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos(); + if (tempPos <= startPos || headerPos > startPos + rowOffset) + gridItem->setPosition(colOffset, startPos + rowOffset); + } + } else { + if (isRightToLeftTopToBottom()) + gridItem->setPosition(colOffset, rowOffset); + else + gridItem->setPosition(colOffset, -headerSize()); + } + + if (created) + emit q->headerItemChanged(); +} + +void QQuickGridViewPrivate::initializeCurrentItem() +{ + if (currentItem && currentIndex >= 0) { + FxGridItemSG *gridItem = static_cast(currentItem); + if (gridItem) + gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex)); + } +} + +void QQuickGridViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QQuickGridView); + QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + if (item == q) { + if (newGeometry.height() != oldGeometry.height() || newGeometry.width() != oldGeometry.width()) { + updateViewport(); + forceLayout = true; + q->polish(); + } + } +} + +void QQuickGridViewPrivate::fixupPosition() +{ + moveReason = Other; + if (flow == QQuickGridView::LeftToRight) + fixupY(); + else + fixupX(); +} + +void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((flow == QQuickGridView::TopToBottom && &data == &vData) + || (flow == QQuickGridView::LeftToRight && &data == &hData)) + return; + + fixupMode = moveReason == Mouse ? fixupMode : Immediate; + + qreal viewPos = isRightToLeftTopToBottom() ? -position()-size() : position(); + + bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange; + if (snapMode != QQuickGridView::NoSnap) { + qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position(); + if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) { + // if we've been dragged < rowSize()/2 then bias towards the next row + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = 0; + if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2) + bias = rowSize()/2; + else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2) + bias = -rowSize()/2; + if (isRightToLeftTopToBottom()) + bias = -bias; + tempPosition -= bias; + } + FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); + if (!topItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + topItem = currentItem; + } + FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); + if (!bottomItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + bottomItem = currentItem; + } + qreal pos; + bool isInBounds = -position() > maxExtent && -position() <= minExtent; + if (topItem && (isInBounds || strictHighlightRange)) { + qreal headerPos = header ? static_cast(header)->rowPos() : 0; + if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) { + pos = isRightToLeftTopToBottom() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart; + } else { + if (isRightToLeftTopToBottom()) + pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); + } + } else if (bottomItem && isInBounds) { + if (isRightToLeftTopToBottom()) + pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent); + } else { + QQuickItemViewPrivate::fixup(data, minExtent, maxExtent); + return; + } + + qreal dist = qAbs(data.move + pos); + if (dist > 0) { + timeline.reset(data.move); + if (fixupMode != Immediate) { + timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -pos); + } + vTime = timeline.time(); + } + } else if (haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange) { + if (currentItem) { + updateHighlight(); + qreal pos = static_cast(currentItem)->rowPos(); + if (viewPos < pos + rowSize() - highlightRangeEnd) + viewPos = pos + rowSize() - highlightRangeEnd; + if (viewPos > pos - highlightRangeStart) + viewPos = pos - highlightRangeStart; + if (isRightToLeftTopToBottom()) + viewPos = -viewPos-size(); + timeline.reset(data.move); + if (viewPos != position()) { + if (fixupMode != Immediate) { + timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -viewPos); + } + } + vTime = timeline.time(); + } + } else { + QQuickItemViewPrivate::fixup(data, minExtent, maxExtent); + } + data.inOvershoot = false; + fixupMode = Normal; +} + +void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QQuickGridView); + data.fixingUp = false; + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange) + && snapMode == QQuickGridView::NoSnap) { + QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + return; + } + qreal maxDistance = 0; + qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value(); + // -ve velocity means list is moving up/left + if (velocity > 0) { + if (data.move.value() < minExtent) { + if (snapMode == QQuickGridView::SnapOneRow) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0; + if (isRightToLeftTopToBottom()) + bias = -bias; + data.flickTarget = -snapPosAt(-dataValue - bias); + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = maxVelocity; + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QQuickGridView::SnapOneRow) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0; + if (isRightToLeftTopToBottom()) + bias = -bias; + data.flickTarget = -snapPosAt(-dataValue + bias); + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = -maxVelocity; + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds; + if (maxDistance > 0 || overShoot) { + // This mode requires the grid to stop exactly on a row boundary. + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + qreal accel = deceleration; + qreal v2 = v * v; + qreal overshootDist = 0.0; + if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickGridView::SnapOneRow) { + // + rowSize()/4 to encourage moving at least one item in the flick direction + qreal dist = v2 / (accel * 2.0) + rowSize()/4; + dist = qMin(dist, maxDistance); + if (v > 0) + dist = -dist; + if (snapMode != QQuickGridView::SnapOneRow) { + qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist; + data.flickTarget = -snapPosAt(-dataValue + distTemp); + } + data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget; + if (overShoot) { + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget -= overshootDist; + } + } + qreal adjDist = -data.flickTarget + data.move.value(); + if (qAbs(adjDist) > qAbs(dist)) { + // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration + qreal adjv2 = accel * 2.0f * qAbs(adjDist); + if (adjv2 > v2) { + v2 = adjv2; + v = qSqrt(v2); + if (dist > 0) + v = -v; + } + } + dist = adjDist; + accel = v2 / (2.0f * qAbs(dist)); + } else { + data.flickTarget = velocity > 0 ? minExtent : maxExtent; + overshootDist = overShoot ? overShootDistance(vSize) : 0; + } + timeline.reset(data.move); + timeline.accel(data.move, v, accel, maxDistance + overshootDist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!hData.flicking && q->xflick()) { + hData.flicking = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + emit q->flickStarted(); + } + if (!vData.flicking && q->yflick()) { + vData.flicking = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + emit q->flickStarted(); + } + } else { + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + + +//---------------------------------------------------------------------------- +/*! + \qmlclass GridView QQuickGridView + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + + \inherits Flickable + \brief The GridView item provides a grid view of items provided by a model. + + A GridView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + A GridView has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. Items in a + GridView are laid out horizontally or vertically. Grid views are inherently flickable + as GridView inherits from \l Flickable. + + \section1 Example Usage + + The following example shows the definition of a simple list model defined + in a file called \c ContactModel.qml: + + \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0 + + \div {class="float-right"} + \inlineimage gridview-simple.png + \enddiv + + This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules} + for more information about creating reusable components like this. + + Another component can display this model data in a GridView, as in the following + example, which creates a \c ContactModel component for its model, and a \l Column element + (containing \l Image and \l Text elements) for its delegate. + + \clearfloat + \snippet doc/src/snippets/declarative/gridview/gridview.qml import + \codeline + \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple + + \div {class="float-right"} + \inlineimage gridview-highlight.png + \enddiv + + The view will create a new delegate for each item in the model. Note that the delegate + is able to access the model's \c name and \c portrait data directly. + + An improved grid view is shown below. The delegate is visually improved and is moved + into a separate \c contactDelegate component. + + \clearfloat + \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced + + The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, + and \c focus is set to \c true to enable keyboard navigation for the grid view. + The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + GridView attaches a number of properties to the root item of the delegate, for example + \c {GridView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c GridView.isCurrentItem, while the child + \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem + + \note Views do not set the \l{Item::}{clip} property automatically. + If the view is not clipped by another item or the screen, it will be necessary + to set this property to true in order to clip the items that are partially or + fully outside the view. + + \sa {declarative/modelviews/gridview}{GridView example} +*/ + +QQuickGridView::QQuickGridView(QQuickItem *parent) + : QQuickItemView(*(new QQuickGridViewPrivate), parent) +{ +} + +QQuickGridView::~QQuickGridView() +{ +} + +void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QQuickGridView); + if (d->autoHighlight != autoHighlight) { + if (!autoHighlight && d->highlightXAnimator) { + d->highlightXAnimator->stop(); + d->highlightYAnimator->stop(); + } + QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight); + } +} + +/*! + \qmlattachedproperty bool QtQuick2::GridView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty GridView QtQuick2::GridView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem +*/ + +/*! + \qmlattachedproperty bool QtQuick2::GridView::delayRemove + This attached property holds whether the delegate may be destroyed. + + It is attached to each instance of the delegate. + + It is sometimes necessary to delay the destruction of an item + until an animation completes. + + The example below ensures that the animation completes before + the item is removed from the grid. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove +*/ + +/*! + \qmlattachedsignal QtQuick2::GridView::onAdd() + This attached handler is called immediately after an item is added to the view. +*/ + +/*! + \qmlattachedsignal QtQuick2::GridView::onRemove() + This attached handler is called immediately before an item is removed from the view. +*/ + + +/*! + \qmlproperty model QtQuick2::GridView::model + This property holds the model providing data for the grid. + + The model provides the set of data that is used to create the items + in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel + or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is + used, it must be a subclass of \l QAbstractItemModel or a simple list. + + \sa {qmlmodels}{Data Models} +*/ + +/*! + \qmlproperty Component QtQuick2::GridView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + The GridView will layout the items based on the size of the root item + in the delegate. + + \note Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. +*/ + +/*! + \qmlproperty int QtQuick2::GridView::currentIndex + \qmlproperty Item QtQuick2::GridView::currentItem + + The \c currentIndex property holds the index of the current item, and + \c currentItem holds the current item. Setting the currentIndex to -1 + will clear the highlight and set currentItem to null. + + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the GridView so that the current + item becomes visible. + + Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ + + +/*! + \qmlproperty Item QtQuick2::GridView::highlightItem + + This holds the highlight item created from the \l highlight component. + + The highlightItem is managed by the view unless + \l highlightFollowsCurrentItem is set to false. + + \sa highlight, highlightFollowsCurrentItem +*/ + + +/*! + \qmlproperty int QtQuick2::GridView::count + This property holds the number of items in the view. +*/ + + +/*! + \qmlproperty Component QtQuick2::GridView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component is created for each view. + The geometry of the resulting component instance will be managed by the view + so as to stay with the current item, unless the highlightFollowsCurrentItem property is false. + + \sa highlightItem, highlightFollowsCurrentItem +*/ + +/*! + \qmlproperty bool QtQuick2::GridView::highlightFollowsCurrentItem + This property sets whether the highlight is managed by the view. + + If this property is true (the default value), the highlight is moved smoothly + to follow the current item. Otherwise, the + highlight is not moved by the view, and any movement must be implemented + by the highlight. + + Here is a highlight with its motion defined by a \l {SpringAnimation} item: + + \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem +*/ + + +/*! + \qmlproperty int QtQuick2::GridView::highlightMoveDuration + This property holds the move animation duration of the highlight delegate. + + highlightFollowsCurrentItem must be true for this property + to have effect. + + The default value for the duration is 150ms. + + \sa highlightFollowsCurrentItem +*/ + +/*! + \qmlproperty real QtQuick2::GridView::preferredHighlightBegin + \qmlproperty real QtQuick2::GridView::preferredHighlightEnd + \qmlproperty enumeration QtQuick2::GridView::highlightRangeMode + + These properties define the preferred range of the highlight (for the current item) + within the view. The \c preferredHighlightBegin value must be less than the + \c preferredHighlightEnd value. + + These properties affect the position of the current item when the view is scrolled. + For example, if the currently selected item should stay in the middle of the + view when it is scrolled, set the \c preferredHighlightBegin and + \c preferredHighlightEnd values to the top and bottom coordinates of where the middle + item would be. If the \c currentItem is changed programmatically, the view will + automatically scroll so that the current item is in the middle of the view. + Furthermore, the behavior of the current item index will occur whether or not a + highlight exists. + + Valid values for \c highlightRangeMode are: + + \list + \o GridView.ApplyRange - the view attempts to maintain the highlight within the range. + However, the highlight can move outside of the range at the ends of the view or due + to mouse interaction. + \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range. + The current item changes if a keyboard or mouse action would cause the highlight to move + outside of the range. + \o GridView.NoHighlightRange - this is the default value. + \endlist +*/ + + +/*! + \qmlproperty enumeration QtQuick2::GridView::layoutDirection + This property holds the layout direction of the grid. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is + dependent on the \l GridView::flow property. + \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent + on the \l GridView::flow property. + \endlist + + \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if + GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply + indicates that the flow is horizontal. +*/ + + +/*! + \qmlproperty enumeration QtQuick2::GridView::effectiveLayoutDirection + This property holds the effective layout direction of the grid. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the grid will be mirrored. However, the + property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged. + + \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ +/*! + \qmlproperty bool QtQuick2::GridView::keyNavigationWraps + This property holds whether the grid wraps key navigation + + If this is true, key navigation that would move the current item selection + past one end of the view instead wraps around and moves the selection to + the other end of the view. + + By default, key navigation is not wrapped. +*/ +/*! + \qmlproperty int QtQuick2::GridView::cacheBuffer + This property determines whether delegates are retained outside the + visible area of the view. + + If non-zero the view will keep as many delegates + instantiated as will fit within the buffer specified. For example, + if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is + set to 40, then up to 2 delegates above and 2 delegates below the visible + area may be retained. + + Note that cacheBuffer is not a pixel buffer - it only maintains additional + instantiated delegates. + + Setting this value can make scrolling the list smoother at the expense + of additional memory usage. It is not a substitute for creating efficient + delegates; the fewer elements in a delegate, the faster a view may be + scrolled. +*/ +void QQuickGridView::setHighlightMoveDuration(int duration) +{ + Q_D(QQuickGridView); + if (d->highlightMoveDuration != duration) { + if (d->highlightYAnimator) { + d->highlightXAnimator->userDuration = duration; + d->highlightYAnimator->userDuration = duration; + } + QQuickItemView::setHighlightMoveDuration(duration); + } +} + +/*! + \qmlproperty enumeration QtQuick2::GridView::flow + This property holds the flow of the grid. + + Possible values: + + \list + \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically + \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally + \endlist +*/ +QQuickGridView::Flow QQuickGridView::flow() const +{ + Q_D(const QQuickGridView); + return d->flow; +} + +void QQuickGridView::setFlow(Flow flow) +{ + Q_D(QQuickGridView); + if (d->flow != flow) { + d->flow = flow; + if (d->flow == LeftToRight) { + setContentWidth(-1); + setFlickableDirection(VerticalFlick); + } else { + setContentHeight(-1); + setFlickableDirection(HorizontalFlick); + } + setContentX(0); + setContentY(0); + d->regenerate(); + emit flowChanged(); + } +} + + +/*! + \qmlproperty real QtQuick2::GridView::cellWidth + \qmlproperty real QtQuick2::GridView::cellHeight + + These properties holds the width and height of each cell in the grid. + + The default cell size is 100x100. +*/ +qreal QQuickGridView::cellWidth() const +{ + Q_D(const QQuickGridView); + return d->cellWidth; +} + +void QQuickGridView::setCellWidth(qreal cellWidth) +{ + Q_D(QQuickGridView); + if (cellWidth != d->cellWidth && cellWidth > 0) { + d->cellWidth = qMax(qreal(1), cellWidth); + d->updateViewport(); + emit cellWidthChanged(); + d->forceLayout = true; + d->layout(); + } +} + +qreal QQuickGridView::cellHeight() const +{ + Q_D(const QQuickGridView); + return d->cellHeight; +} + +void QQuickGridView::setCellHeight(qreal cellHeight) +{ + Q_D(QQuickGridView); + if (cellHeight != d->cellHeight && cellHeight > 0) { + d->cellHeight = qMax(qreal(1), cellHeight); + d->updateViewport(); + emit cellHeightChanged(); + d->forceLayout = true; + d->layout(); + } +} +/*! + \qmlproperty enumeration QtQuick2::GridView::snapMode + + This property determines how the view scrolling will settle following a drag or flick. + The possible values are: + + \list + \o GridView.NoSnap (default) - the view stops anywhere within the visible area. + \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow) + aligned with the start of the view. + \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow) + away from the first visible row at the time the mouse button is released. + This mode is particularly useful for moving one page at a time. + \endlist + +*/ +QQuickGridView::SnapMode QQuickGridView::snapMode() const +{ + Q_D(const QQuickGridView); + return d->snapMode; +} + +void QQuickGridView::setSnapMode(SnapMode mode) +{ + Q_D(QQuickGridView); + if (d->snapMode != mode) { + d->snapMode = mode; + emit snapModeChanged(); + } +} + + +/*! + \qmlproperty Component QtQuick2::GridView::footer + This property holds the component to use as the footer. + + An instance of the footer component is created for each view. The + footer is positioned at the end of the view, after any items. + + \sa header +*/ +/*! + \qmlproperty Component QtQuick2::GridView::header + This property holds the component to use as the header. + + An instance of the header component is created for each view. The + header is positioned at the beginning of the view, before any items. + + \sa footer +*/ +void QQuickGridView::viewportMoved() +{ + Q_D(QQuickGridView); + QQuickItemView::viewportMoved(); + if (!d->itemCount) + return; + if (d->inViewportMoved) + return; + d->inViewportMoved = true; + + d->lazyRelease = true; + if (d->hData.flicking || d->vData.flicking) { + if (yflick()) { + if (d->vData.velocity > 0) + d->bufferMode = QQuickGridViewPrivate::BufferBefore; + else if (d->vData.velocity < 0) + d->bufferMode = QQuickGridViewPrivate::BufferAfter; + } + + if (xflick()) { + if (d->hData.velocity > 0) + d->bufferMode = QQuickGridViewPrivate::BufferBefore; + else if (d->hData.velocity < 0) + d->bufferMode = QQuickGridViewPrivate::BufferAfter; + } + } + d->refill(); + if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) + d->moveReason = QQuickGridViewPrivate::Mouse; + if (d->moveReason != QQuickGridViewPrivate::SetIndex) { + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { + // reposition highlight + qreal pos = d->highlight->position(); + qreal viewPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size() : d->position(); + if (pos > viewPos + d->highlightRangeEnd - d->highlight->size()) + pos = viewPos + d->highlightRangeEnd - d->highlight->size(); + if (pos < viewPos + d->highlightRangeStart) + pos = viewPos + d->highlightRangeStart; + + if (pos != d->highlight->position()) { + d->highlightXAnimator->stop(); + d->highlightYAnimator->stop(); + static_cast(d->highlight)->setPosition(static_cast(d->highlight)->colPos(), pos); + } else { + d->updateHighlight(); + } + + // update current index + int idx = d->snapIndex(); + if (idx >= 0 && idx != d->currentIndex) { + d->updateCurrent(idx); + if (d->currentItem && static_cast(d->currentItem)->colPos() != static_cast(d->highlight)->colPos() && d->autoHighlight) { + if (d->flow == LeftToRight) + d->highlightXAnimator->to = d->currentItem->item->x(); + else + d->highlightYAnimator->to = d->currentItem->item->y(); + } + } + } + } + + d->inViewportMoved = false; +} + +void QQuickGridView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickGridView); + if (d->model && d->model->count() && d->interactive) { + d->moveReason = QQuickGridViewPrivate::SetIndex; + int oldCurrent = currentIndex(); + switch (event->key()) { + case Qt::Key_Up: + moveCurrentIndexUp(); + break; + case Qt::Key_Down: + moveCurrentIndexDown(); + break; + case Qt::Key_Left: + moveCurrentIndexLeft(); + break; + case Qt::Key_Right: + moveCurrentIndexRight(); + break; + default: + break; + } + if (oldCurrent != currentIndex()) { + event->accept(); + return; + } + } + event->ignore(); + QQuickItemView::keyPressEvent(event); +} +/*! + \qmlmethod QtQuick2::GridView::moveCurrentIndexUp() + + Move the currentIndex up one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ + + +void QQuickGridView::moveCurrentIndexUp() +{ + Q_D(QQuickGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (d->flow == QQuickGridView::LeftToRight) { + if (currentIndex() >= d->columns || d->wrap) { + int index = currentIndex() - d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } else { + if (currentIndex() > 0 || d->wrap) { + int index = currentIndex() - 1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } +} + +/*! + \qmlmethod QtQuick2::GridView::moveCurrentIndexDown() + + Move the currentIndex down one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QQuickGridView::moveCurrentIndexDown() +{ + Q_D(QQuickGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (d->flow == QQuickGridView::LeftToRight) { + if (currentIndex() < count - d->columns || d->wrap) { + int index = currentIndex()+d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } else { + if (currentIndex() < count - 1 || d->wrap) { + int index = currentIndex() + 1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } +} + +/*! + \qmlmethod QtQuick2::GridView::moveCurrentIndexLeft() + + Move the currentIndex left one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QQuickGridView::moveCurrentIndexLeft() +{ + Q_D(QQuickGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QQuickGridView::LeftToRight) { + if (currentIndex() > 0 || d->wrap) { + int index = currentIndex() - 1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } else { + if (currentIndex() >= d->columns || d->wrap) { + int index = currentIndex() - d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } + } else { + if (d->flow == QQuickGridView::LeftToRight) { + if (currentIndex() < count - 1 || d->wrap) { + int index = currentIndex() + 1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } else { + if (currentIndex() < count - d->columns || d->wrap) { + int index = currentIndex() + d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } + } +} + + +/*! + \qmlmethod QtQuick2::GridView::moveCurrentIndexRight() + + Move the currentIndex right one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QQuickGridView::moveCurrentIndexRight() +{ + Q_D(QQuickGridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QQuickGridView::LeftToRight) { + if (currentIndex() < count - 1 || d->wrap) { + int index = currentIndex() + 1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } else { + if (currentIndex() < count - d->columns || d->wrap) { + int index = currentIndex()+d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } + } + } else { + if (d->flow == QQuickGridView::LeftToRight) { + if (currentIndex() > 0 || d->wrap) { + int index = currentIndex() - 1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } else { + if (currentIndex() >= d->columns || d->wrap) { + int index = currentIndex() - d->columns; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } + } + } +} + +bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult) +{ + Q_Q(QQuickGridView); + + int modelIndex = change.index; + int count = change.count; + + int index = visibleItems.count() ? mapFromModel(modelIndex) : 0; + + if (index < 0) { + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + if (visibleItems.at(i)->index + 1 == modelIndex) { + // Special case of appending an item to the model. + index = visibleItems.count(); + } else { + if (modelIndex <= visibleIndex) { + // Insert before visible items + visibleIndex += count; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index != -1 && item->index >= modelIndex) + item->index += count; + } + } + return true; + } + } + + qreal tempPos = isRightToLeftTopToBottom() ? -position()-size()+q->width()+1 : position(); + int colPos = 0; + int rowPos = 0; + if (visibleItems.count()) { + if (index < visibleItems.count()) { + FxGridItemSG *gridItem = static_cast(visibleItems.at(index)); + colPos = gridItem->colPos(); + rowPos = gridItem->rowPos(); + } else { + // appending items to visible list + FxGridItemSG *gridItem = static_cast(visibleItems.at(index-1)); + colPos = gridItem->colPos() + colSize(); + rowPos = gridItem->rowPos(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + } + } + + // Update the indexes of the following visible items. + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index != -1 && item->index >= modelIndex) + item->index += count; + } + + int prevAddedCount = insertResult->addedItems.count(); + if (firstVisible && rowPos < firstVisible->position()) { + // Insert items before the visible item. + int insertionIdx = index; + int i = count - 1; + int from = tempPos - buffer; + + while (i >= 0) { + if (rowPos > from) { + insertResult->sizeAddedBeforeVisible += rowSize(); + } else { + FxViewItem *item = 0; + if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { + if (item->index > modelIndex + i) + insertResult->movedBackwards.append(item); + item->index = modelIndex + i; + } + if (!item) + item = createItem(modelIndex + i); + + visibleItems.insert(insertionIdx, item); + if (!change.isMove()) { + insertResult->addedItems.append(item); + insertResult->sizeAddedBeforeVisible += rowSize(); + } + } + colPos -= colSize(); + if (colPos < 0) { + colPos = colSize() * (columns - 1); + rowPos -= rowSize(); + } + index++; + i--; + } + } else { + int i = 0; + int to = buffer+tempPos+size()-1; + while (i < count && rowPos <= to + rowSize()*(columns - (colPos/colSize()))/qreal(columns)) { + FxViewItem *item = 0; + if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { + if (item->index > modelIndex + i) + insertResult->movedBackwards.append(item); + item->index = modelIndex + i; + } + if (!item) + item = createItem(modelIndex + i); + + visibleItems.insert(index, item); + if (!change.isMove()) + insertResult->addedItems.append(item); + colPos += colSize(); + if (colPos > colSize() * (columns-1)) { + colPos = 0; + rowPos += rowSize(); + } + ++index; + ++i; + } + } + + updateVisibleIndex(); + + return insertResult->addedItems.count() > prevAddedCount; +} + +/*! + \qmlmethod QtQuick2::GridView::positionViewAtIndex(int index, PositionMode mode) + + Positions the view such that the \a index is at the position specified by + \a mode: + + \list + \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view. + \o GridView.Center - position item in the center of the view. + \o GridView.End - position item at bottom (or right for horizontal orientation) of the view. + \o GridView.Visible - if any part of the item is visible then take no action, otherwise + bring the item into view. + \o GridView.Contain - ensure the entire item is visible. If the item is larger than + the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view. + \endlist + + If positioning the view at the index would cause empty space to be displayed at + the beginning or end of the view, the view will be positioned at the boundary. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the view does not cause all other items to be repositioned. + The correct way to bring an item into view is with \c positionViewAtIndex. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end: + + \code + Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning) + \endcode +*/ + +/*! + \qmlmethod QtQuick2::GridView::positionViewAtBeginning() + \qmlmethod QtQuick2::GridView::positionViewAtEnd() + + Positions the view at the beginning or end, taking into account any header or footer. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end on startup: + + \code + Component.onCompleted: positionViewAtEnd() + \endcode +*/ + +/*! + \qmlmethod int QtQuick2::GridView::indexAt(int x, int y) + + Returns the index of the visible item containing the point \a x, \a y in content + coordinates. If there is no item at the point specified, or the item is + not visible -1 is returned. + + If the item is outside the visible area, -1 is returned, regardless of + whether an item will exist at that point when scrolled into view. + + \bold Note: methods should only be called after the Component has completed. +*/ + +QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj) +{ + return new QQuickGridViewAttached(obj); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickgridview_p.h b/src/declarative/items/qquickgridview_p.h new file mode 100644 index 0000000000..b02c0a8350 --- /dev/null +++ b/src/declarative/items/qquickgridview_p.h @@ -0,0 +1,146 @@ +// Commit: 95814418f9d6adeba365c795462e8afb00138211 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKGRIDVIEW_P_H +#define QQUICKGRIDVIEW_P_H + +#include "qquickitemview_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QQuickVisualModel; +class QQuickGridViewAttached; +class QQuickGridViewPrivate; +class Q_AUTOTEST_EXPORT QQuickGridView : public QQuickItemView +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickGridView) + + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) + Q_PROPERTY(qreal cellWidth READ cellWidth WRITE setCellWidth NOTIFY cellWidthChanged) + Q_PROPERTY(qreal cellHeight READ cellHeight WRITE setCellHeight NOTIFY cellHeightChanged) + + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) + + Q_ENUMS(SnapMode) + Q_ENUMS(Flow) + Q_CLASSINFO("DefaultProperty", "data") + +public: + QQuickGridView(QQuickItem *parent=0); + ~QQuickGridView(); + + virtual void setHighlightFollowsCurrentItem(bool); + virtual void setHighlightMoveDuration(int); + + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow); + + qreal cellWidth() const; + void setCellWidth(qreal); + + qreal cellHeight() const; + void setCellHeight(qreal); + + enum SnapMode { NoSnap, SnapToRow, SnapOneRow }; + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + + static QQuickGridViewAttached *qmlAttachedProperties(QObject *); + +public Q_SLOTS: + void moveCurrentIndexUp(); + void moveCurrentIndexDown(); + void moveCurrentIndexLeft(); + void moveCurrentIndexRight(); + +Q_SIGNALS: + void cellWidthChanged(); + void cellHeightChanged(); + void highlightMoveDurationChanged(); + void flowChanged(); + void snapModeChanged(); + +protected: + virtual void viewportMoved(); + virtual void keyPressEvent(QKeyEvent *); +}; + +class QQuickGridViewAttached : public QQuickItemViewAttached +{ + Q_OBJECT +public: + QQuickGridViewAttached(QObject *parent) + : QQuickItemViewAttached(parent), m_view(0) {} + ~QQuickGridViewAttached() {} + + Q_PROPERTY(QQuickGridView *view READ view NOTIFY viewChanged) + QQuickGridView *view() { return m_view; } + void setView(QQuickGridView *view) { + if (view != m_view) { + m_view = view; + emit viewChanged(); + } + } + +Q_SIGNALS: + void viewChanged(); + +public: + QDeclarativeGuard m_view; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickGridView) +QML_DECLARE_TYPEINFO(QQuickGridView, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QQUICKGRIDVIEW_P_H diff --git a/src/declarative/items/qquickimage.cpp b/src/declarative/items/qquickimage.cpp new file mode 100644 index 0000000000..073795768c --- /dev/null +++ b/src/declarative/items/qquickimage.cpp @@ -0,0 +1,753 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickimage_p.h" +#include "qquickimage_p_p.h" + +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickImageTextureProvider : public QSGTextureProvider +{ + Q_OBJECT +public: + QQuickImageTextureProvider() + : m_texture(0) + , m_smooth(false) + { + } + + QSGTexture *texture() const { + + if (m_texture->isAtlasTexture()) + const_cast(this)->m_texture = m_texture->removedFromAtlas(); + + if (m_texture) { + m_texture->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest); + m_texture->setMipmapFiltering(QSGTexture::Nearest); + m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge); + m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge); + } + return m_texture; + } + + friend class QQuickImage; + + QSGTexture *m_texture; + bool m_smooth; +}; + +#include "qquickimage.moc" + +QQuickImagePrivate::QQuickImagePrivate() + : fillMode(QQuickImage::Stretch) + , paintedWidth(0) + , paintedHeight(0) + , pixmapChanged(false) + , hAlign(QQuickImage::AlignHCenter) + , vAlign(QQuickImage::AlignVCenter) + , provider(0) +{ +} + +/*! + \qmlclass Image QQuickImage + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The Image element displays an image in a declarative user interface + \inherits Item + + The Image element is used to display images in a declarative user interface. + + The source of the image is specified as a URL using the \l source property. + Images can be supplied in any of the standard image formats supported by Qt, + including bitmap formats such as PNG and JPEG, and vector graphics formats + such as SVG. If you need to display animated images, use the \l AnimatedImage + element. + + If the \l{Item::width}{width} and \l{Item::height}{height} properties are not + specified, the Image element automatically uses the size of the loaded image. + By default, specifying the width and height of the element causes the image + to be scaled to that size. This behavior can be changed by setting the + \l fillMode property, allowing the image to be stretched and tiled instead. + + \section1 Example Usage + + The following example shows the simplest usage of the Image element. + + \snippet doc/src/snippets/declarative/image.qml document + + \beginfloatleft + \image declarative-qtlogo.png + \endfloat + + \clearfloat + + \section1 Performance + + By default, locally available images are loaded immediately, and the user interface + is blocked until loading is complete. If a large image is to be loaded, it may be + preferable to load the image in a low priority thread, by enabling the \l asynchronous + property. + + If the image is obtained from a network rather than a local resource, it is + automatically loaded asynchronously, and the \l progress and \l status properties + are updated as appropriate. + + Images are cached and shared internally, so if several Image elements have the same \l source, + only one copy of the image will be loaded. + + \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended + that images which do not form part of the user interface have their + size bounded via the \l sourceSize property. This is especially important for content + that is loaded from external sources or provided by the user. + + \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider +*/ + +QQuickImage::QQuickImage(QQuickItem *parent) + : QQuickImageBase(*(new QQuickImagePrivate), parent) +{ +} + +QQuickImage::QQuickImage(QQuickImagePrivate &dd, QQuickItem *parent) + : QQuickImageBase(dd, parent) +{ +} + +QQuickImage::~QQuickImage() +{ + Q_D(QQuickImage); + if (d->provider) + d->provider->deleteLater(); +} + +void QQuickImagePrivate::setPixmap(const QPixmap &pixmap) +{ + Q_Q(QQuickImage); + pix.setPixmap(pixmap); + + q->pixmapChange(); + status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready; + + q->update(); +} + +/*! + \qmlproperty enumeration QtQuick2::Image::fillMode + + Set this property to define what happens when the source image has a different size + than the item. + + \list + \o Image.Stretch - the image is scaled to fit + \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping + \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary + \o Image.Tile - the image is duplicated horizontally and vertically + \o Image.TileVertically - the image is stretched horizontally and tiled vertically + \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally + \o Image.Pad - the image is not transformed + \endlist + + \table + + \row + \o \image declarative-qtlogo-stretch.png + \o Stretch (default) + \qml + Image { + width: 130; height: 100 + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-preserveaspectfit.png + \o PreserveAspectFit + \qml + Image { + width: 130; height: 100 + fillMode: Image.PreserveAspectFit + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-preserveaspectcrop.png + \o PreserveAspectCrop + \qml + Image { + width: 130; height: 100 + fillMode: Image.PreserveAspectCrop + smooth: true + source: "qtlogo.png" + clip: true + } + \endqml + + \row + \o \image declarative-qtlogo-tile.png + \o Tile + \qml + Image { + width: 120; height: 120 + fillMode: Image.Tile + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-tilevertically.png + \o TileVertically + \qml + Image { + width: 120; height: 120 + fillMode: Image.TileVertically + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-tilehorizontally.png + \o TileHorizontally + \qml + Image { + width: 120; height: 120 + fillMode: Image.TileHorizontally + smooth: true + source: "qtlogo.png" + } + \endqml + + \endtable + + Note that \c clip is \c false by default which means that the element might + paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop. + + \sa {declarative/imageelements/image}{Image example} +*/ +QQuickImage::FillMode QQuickImage::fillMode() const +{ + Q_D(const QQuickImage); + return d->fillMode; +} + +void QQuickImage::setFillMode(FillMode mode) +{ + Q_D(QQuickImage); + if (d->fillMode == mode) + return; + d->fillMode = mode; + update(); + updatePaintedGeometry(); + emit fillModeChanged(); +} + +/*! + + \qmlproperty real QtQuick2::Image::paintedWidth + \qmlproperty real QtQuick2::Image::paintedHeight + + These properties hold the size of the image that is actually painted. + In most cases it is the same as \c width and \c height, but when using a + \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop + \c paintedWidth or \c paintedHeight can be smaller or larger than + \c width and \c height of the Image element. +*/ +qreal QQuickImage::paintedWidth() const +{ + Q_D(const QQuickImage); + return d->paintedWidth; +} + +qreal QQuickImage::paintedHeight() const +{ + Q_D(const QQuickImage); + return d->paintedHeight; +} + +/*! + \qmlproperty enumeration QtQuick2::Image::status + + This property holds the status of image loading. It can be one of: + \list + \o Image.Null - no image has been set + \o Image.Ready - the image has been loaded + \o Image.Loading - the image is currently being loaded + \o Image.Error - an error occurred while loading the image + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: image.status == Image.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + Image { + id: image + onStatusChanged: if (image.status == Image.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist + + \sa progress +*/ + +/*! + \qmlproperty real QtQuick2::Image::progress + + This property holds the progress of image loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ + +/*! + \qmlproperty bool QtQuick2::Image::smooth + + Set this property if you want the image to be smoothly filtered when scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the image is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the image is stationary on + the screen. A common pattern when animating an image is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty QSize QtQuick2::Image::sourceSize + + This property holds the actual width and height of the loaded image. + + Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale + the painting of the image, this property sets the actual number of pixels + stored for the loaded image so that large images do not use more + memory than necessary. For example, this ensures the image in memory is no + larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and + \l {Item::}{height} values: + + \code + Rectangle { + width: ... + height: ... + + Image { + anchors.fill: parent + source: "reallyBigImage.jpg" + sourceSize.width: 1024 + sourceSize.height: 1024 + } + } + \endcode + + If the image's actual size is larger than the sourceSize, the image is scaled down. + If only one dimension of the size is set to greater than 0, the + other dimension is set in proportion to preserve the source image's aspect ratio. + (The \l fillMode is independent of this.) + + If the source is an instrinsically scalable image (eg. SVG), this property + determines the size of the loaded image regardless of intrinsic size. + Avoid changing this property dynamically; rendering an SVG is \e slow compared + to an image. + + If the source is a non-scalable image (eg. JPEG), the loaded image will + be no greater than this property specifies. For some formats (currently only JPEG), + the whole image will never actually be loaded into memory. + + Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image + by setting sourceSize to \c undefined. + + \note \e {Changing this property dynamically causes the image source to be reloaded, + potentially even from the network, if it is not in the disk cache.} +*/ + +/*! + \qmlproperty url QtQuick2::Image::source + + Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt. + + The URL may be absolute, or relative to the URL of the component. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty bool QtQuick2::Image::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ + +/*! + \qmlproperty bool QtQuick2::Image::cache + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool QtQuick2::Image::mirror + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + +/*! + \qmlproperty enumeration QtQuick2::Image::horizontalAlignment + \qmlproperty enumeration QtQuick2::Image::verticalAlignment + + Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned. + + The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter. + The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom + and \c Image.AlignVCenter. +*/ +void QQuickImage::updatePaintedGeometry() +{ + Q_D(QQuickImage); + + if (d->fillMode == PreserveAspectFit) { + if (!d->pix.width() || !d->pix.height()) { + setImplicitWidth(0); + setImplicitHeight(0); + return; + } + qreal w = widthValid() ? width() : d->pix.width(); + qreal widthScale = w / qreal(d->pix.width()); + qreal h = heightValid() ? height() : d->pix.height(); + qreal heightScale = h / qreal(d->pix.height()); + if (widthScale <= heightScale) { + d->paintedWidth = w; + d->paintedHeight = widthScale * qreal(d->pix.height()); + } else if (heightScale < widthScale) { + d->paintedWidth = heightScale * qreal(d->pix.width()); + d->paintedHeight = h; + } + if (widthValid() && !heightValid()) { + setImplicitHeight(d->paintedHeight); + } else { + setImplicitHeight(d->pix.height()); + } + if (heightValid() && !widthValid()) { + setImplicitWidth(d->paintedWidth); + } else { + setImplicitWidth(d->pix.width()); + } + } else if (d->fillMode == PreserveAspectCrop) { + if (!d->pix.width() || !d->pix.height()) + return; + qreal widthScale = width() / qreal(d->pix.width()); + qreal heightScale = height() / qreal(d->pix.height()); + if (widthScale < heightScale) { + widthScale = heightScale; + } else if (heightScale < widthScale) { + heightScale = widthScale; + } + + d->paintedHeight = heightScale * qreal(d->pix.height()); + d->paintedWidth = widthScale * qreal(d->pix.width()); + } else if (d->fillMode == Pad) { + d->paintedWidth = d->pix.width(); + d->paintedHeight = d->pix.height(); + } else { + d->paintedWidth = width(); + d->paintedHeight = height(); + } + emit paintedGeometryChanged(); +} + +void QQuickImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickImageBase::geometryChanged(newGeometry, oldGeometry); + updatePaintedGeometry(); +} + +QRectF QQuickImage::boundingRect() const +{ + Q_D(const QQuickImage); + return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight)); +} + +QSGTextureProvider *QQuickImage::textureProvider() const +{ + Q_D(const QQuickImage); + if (!d->provider) { + // Make sure it gets thread affinity on the rendering thread so deletion works properly.. + Q_ASSERT_X(d->canvas + && d->sceneGraphContext() + && QThread::currentThread() == d->sceneGraphContext()->thread(), + "QQuickImage::textureProvider", + "Cannot be used outside the GUI thread"); + QQuickImagePrivate *dd = const_cast(d); + dd->provider = new QQuickImageTextureProvider; + dd->provider->m_texture = d->pix.texture(d->sceneGraphContext()); + } + + return d->provider; +} + +QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + Q_D(QQuickImage); + + QSGTexture *texture = d->pix.texture(d->sceneGraphContext()); + + // Copy over the current texture state into the texture provider... + if (d->provider) { + d->provider->m_smooth = d->smooth; + d->provider->m_texture = texture; + } + + if (!texture || width() <= 0 || height() <= 0) { + delete oldNode; + return 0; + } + + QSGImageNode *node = static_cast(oldNode); + if (!node) { + d->pixmapChanged = true; + node = d->sceneGraphContext()->createImageNode(); + node->setTexture(texture); + } + + if (d->pixmapChanged) { + // force update the texture in the node to trigger reconstruction of + // geometry and the likes when a atlas segment has changed. + node->setTexture(0); + node->setTexture(texture); + d->pixmapChanged = false; + } + + QRectF targetRect; + QRectF sourceRect; + QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge; + QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge; + + qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width(); + qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height(); + + int xOffset = 0; + if (d->hAlign == QQuickImage::AlignHCenter) + xOffset = qCeil((width() - pixWidth) / 2.); + else if (d->hAlign == QQuickImage::AlignRight) + xOffset = qCeil(width() - pixWidth); + + int yOffset = 0; + if (d->vAlign == QQuickImage::AlignVCenter) + yOffset = qCeil((height() - pixHeight) / 2.); + else if (d->vAlign == QQuickImage::AlignBottom) + yOffset = qCeil(height() - pixHeight); + + switch (d->fillMode) { + default: + case Stretch: + targetRect = QRectF(0, 0, width(), height()); + sourceRect = d->pix.rect(); + break; + + case PreserveAspectFit: + targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight); + sourceRect = d->pix.rect(); + break; + + case PreserveAspectCrop: { + targetRect = QRect(0, 0, width(), height()); + qreal wscale = width() / qreal(d->pix.width()); + qreal hscale = height() / qreal(d->pix.height()); + + if (wscale > hscale) { + int src = (hscale / wscale) * qreal(d->pix.height()); + int y = 0; + if (d->vAlign == QQuickImage::AlignVCenter) + y = qCeil((d->pix.height() - src) / 2.); + else if (d->vAlign == QQuickImage::AlignBottom) + y = qCeil(d->pix.height() - src); + sourceRect = QRectF(0, y, d->pix.width(), src); + + } else { + int src = (wscale / hscale) * qreal(d->pix.width()); + int x = 0; + if (d->hAlign == QQuickImage::AlignHCenter) + x = qCeil((d->pix.width() - src) / 2.); + else if (d->hAlign == QQuickImage::AlignRight) + x = qCeil(d->pix.width() - src); + sourceRect = QRectF(x, 0, src, d->pix.height()); + } + } + break; + + case Tile: + targetRect = QRectF(0, 0, width(), height()); + sourceRect = QRectF(-xOffset, -yOffset, width(), height()); + hWrap = QSGTexture::Repeat; + vWrap = QSGTexture::Repeat; + break; + + case TileHorizontally: + targetRect = QRectF(0, 0, width(), height()); + sourceRect = QRectF(-xOffset, 0, width(), d->pix.height()); + hWrap = QSGTexture::Repeat; + break; + + case TileVertically: + targetRect = QRectF(0, 0, width(), height()); + sourceRect = QRectF(0, -yOffset, d->pix.width(), height()); + vWrap = QSGTexture::Repeat; + break; + + case Pad: + qreal w = qMin(qreal(d->pix.width()), width()); + qreal h = qMin(qreal(d->pix.height()), height()); + qreal x = (d->pix.width() > width()) ? -xOffset : 0; + qreal y = (d->pix.height() > height()) ? -yOffset : 0; + targetRect = QRectF(x + xOffset, y + yOffset, w, h); + sourceRect = QRectF(x, y, w, h); + break; + }; + + QRectF nsrect(sourceRect.x() / d->pix.width(), + sourceRect.y() / d->pix.height(), + sourceRect.width() / d->pix.width(), + sourceRect.height() / d->pix.height()); + + if (d->mirror) { + qreal oldLeft = nsrect.left(); + nsrect.setLeft(nsrect.right()); + nsrect.setRight(oldLeft); + } + + node->setHorizontalWrapMode(hWrap); + node->setVerticalWrapMode(vWrap); + node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); + + node->setTargetRect(targetRect); + node->setSourceRect(nsrect); + node->update(); + + return node; +} + +void QQuickImage::pixmapChange() +{ + Q_D(QQuickImage); + // PreserveAspectFit calculates the implicit size differently so we + // don't call our superclass pixmapChange(), since that would + // result in the implicit size being set incorrectly, then updated + // in updatePaintedGeometry() + if (d->fillMode != PreserveAspectFit) + QQuickImageBase::pixmapChange(); + updatePaintedGeometry(); + d->pixmapChanged = true; + + // Make sure we update the texture provider when the image has changed. + if (d->provider) + update(); +} + +QQuickImage::VAlignment QQuickImage::verticalAlignment() const +{ + Q_D(const QQuickImage); + return d->vAlign; +} + +void QQuickImage::setVerticalAlignment(VAlignment align) +{ + Q_D(QQuickImage); + if (d->vAlign == align) + return; + + d->vAlign = align; + update(); + updatePaintedGeometry(); + emit verticalAlignmentChanged(align); +} + +QQuickImage::HAlignment QQuickImage::horizontalAlignment() const +{ + Q_D(const QQuickImage); + return d->hAlign; +} + +void QQuickImage::setHorizontalAlignment(HAlignment align) +{ + Q_D(QQuickImage); + if (d->hAlign == align) + return; + + d->hAlign = align; + update(); + updatePaintedGeometry(); + emit horizontalAlignmentChanged(align); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickimage_p.h b/src/declarative/items/qquickimage_p.h new file mode 100644 index 0000000000..0d1c8dcba3 --- /dev/null +++ b/src/declarative/items/qquickimage_p.h @@ -0,0 +1,122 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKIMAGE_P_H +#define QQUICKIMAGE_P_H + +#include "qquickimagebase_p.h" +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickImagePrivate; +class Q_AUTOTEST_EXPORT QQuickImage : public QQuickImageBase +{ + Q_OBJECT + Q_ENUMS(FillMode) + Q_ENUMS(HAlignment) + Q_ENUMS(VAlignment) + + Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) + Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedGeometryChanged) + Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged) + Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged) + Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged) + +public: + QQuickImage(QQuickItem *parent=0); + ~QQuickImage(); + + enum HAlignment { AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter }; + enum VAlignment { AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter }; + + enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally, Pad }; + + FillMode fillMode() const; + void setFillMode(FillMode); + + qreal paintedWidth() const; + qreal paintedHeight() const; + + QRectF boundingRect() const; + + HAlignment horizontalAlignment() const; + void setHorizontalAlignment(HAlignment align); + + VAlignment verticalAlignment() const; + void setVerticalAlignment(VAlignment align); + + bool isTextureProvider() const { return true; } + QSGTextureProvider *textureProvider() const; + +Q_SIGNALS: + void fillModeChanged(); + void paintedGeometryChanged(); + void horizontalAlignmentChanged(HAlignment alignment); + void verticalAlignmentChanged(VAlignment alignment); + +protected: + QQuickImage(QQuickImagePrivate &dd, QQuickItem *parent); + void pixmapChange(); + void updatePaintedGeometry(); + + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private: + Q_DISABLE_COPY(QQuickImage) + Q_DECLARE_PRIVATE(QQuickImage) +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QQuickImage) +QT_END_HEADER + +#endif // QQUICKIMAGE_P_H diff --git a/src/declarative/items/qquickimage_p_p.h b/src/declarative/items/qquickimage_p_p.h new file mode 100644 index 0000000000..b343821d2f --- /dev/null +++ b/src/declarative/items/qquickimage_p_p.h @@ -0,0 +1,85 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKIMAGE_P_P_H +#define QQUICKIMAGE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickimagebase_p_p.h" +#include "qquickimage_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickImageTextureProvider; + +class QQuickImagePrivate : public QQuickImageBasePrivate +{ + Q_DECLARE_PUBLIC(QQuickImage) + +public: + QQuickImagePrivate(); + + QQuickImage::FillMode fillMode; + qreal paintedWidth; + qreal paintedHeight; + void setPixmap(const QPixmap &pix); + + bool pixmapChanged : 1; + QQuickImage::HAlignment hAlign; + QQuickImage::VAlignment vAlign; + + QQuickImageTextureProvider *provider; +}; + +QT_END_NAMESPACE + +#endif // QQUICKIMAGE_P_P_H diff --git a/src/declarative/items/qquickimagebase.cpp b/src/declarative/items/qquickimagebase.cpp new file mode 100644 index 0000000000..3eb196d152 --- /dev/null +++ b/src/declarative/items/qquickimagebase.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickimagebase_p.h" +#include "qquickimagebase_p_p.h" + +#include + +QT_BEGIN_NAMESPACE + +QQuickImageBase::QQuickImageBase(QQuickItem *parent) +: QQuickImplicitSizeItem(*(new QQuickImageBasePrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickImageBase::QQuickImageBase(QQuickImageBasePrivate &dd, QQuickItem *parent) +: QQuickImplicitSizeItem(dd, parent) +{ + setFlag(ItemHasContents); +} + +QQuickImageBase::~QQuickImageBase() +{ +} + +QQuickImageBase::Status QQuickImageBase::status() const +{ + Q_D(const QQuickImageBase); + return d->status; +} + + +qreal QQuickImageBase::progress() const +{ + Q_D(const QQuickImageBase); + return d->progress; +} + + +bool QQuickImageBase::asynchronous() const +{ + Q_D(const QQuickImageBase); + return d->async; +} + +void QQuickImageBase::setAsynchronous(bool async) +{ + Q_D(QQuickImageBase); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + } +} + +QUrl QQuickImageBase::source() const +{ + Q_D(const QQuickImageBase); + return d->url; +} + +void QQuickImageBase::setSource(const QUrl &url) +{ + Q_D(QQuickImageBase); + //equality is fairly expensive, so we bypass for simple, common case + if ((d->url.isEmpty() == url.isEmpty()) && url == d->url) + return; + + d->url = url; + emit sourceChanged(d->url); + + if (isComponentComplete()) + load(); +} + +void QQuickImageBase::setSourceSize(const QSize& size) +{ + Q_D(QQuickImageBase); + if (d->sourcesize == size) + return; + + d->sourcesize = size; + d->explicitSourceSize = true; + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +QSize QQuickImageBase::sourceSize() const +{ + Q_D(const QQuickImageBase); + + int width = d->sourcesize.width(); + int height = d->sourcesize.height(); + return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height()); +} + +void QQuickImageBase::resetSourceSize() +{ + Q_D(QQuickImageBase); + if (!d->explicitSourceSize) + return; + d->explicitSourceSize = false; + d->sourcesize = QSize(); + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +bool QQuickImageBase::cache() const +{ + Q_D(const QQuickImageBase); + return d->cache; +} + +void QQuickImageBase::setCache(bool cache) +{ + Q_D(QQuickImageBase); + if (d->cache == cache) + return; + + d->cache = cache; + emit cacheChanged(); + if (isComponentComplete()) + load(); +} + +QPixmap QQuickImageBase::pixmap() const +{ + Q_D(const QQuickImageBase); + return d->pix.pixmap(); +} + +void QQuickImageBase::setMirror(bool mirror) +{ + Q_D(QQuickImageBase); + if (mirror == d->mirror) + return; + + d->mirror = mirror; + + if (isComponentComplete()) + update(); + + emit mirrorChanged(); +} + +bool QQuickImageBase::mirror() const +{ + Q_D(const QQuickImageBase); + return d->mirror; +} + +void QQuickImageBase::load() +{ + Q_D(QQuickImageBase); + + if (d->url.isEmpty()) { + d->pix.clear(this); + d->status = Null; + d->progress = 0.0; + pixmapChange(); + emit progressChanged(d->progress); + emit statusChanged(d->status); + update(); + } else { + QDeclarativePixmap::Options options; + if (d->async) + options |= QDeclarativePixmap::Asynchronous; + if (d->cache) + options |= QDeclarativePixmap::Cache; + d->pix.clear(this); + pixmapChange(); + d->pix.load(qmlEngine(this), d->url, d->explicitSourceSize ? sourceSize() : QSize(), options); + + if (d->pix.isLoading()) { + d->progress = 0.0; + d->status = Loading; + emit progressChanged(d->progress); + emit statusChanged(d->status); + + static int thisRequestProgress = -1; + static int thisRequestFinished = -1; + if (thisRequestProgress == -1) { + thisRequestProgress = + QQuickImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); + thisRequestFinished = + QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()"); + } + + d->pix.connectFinished(this, thisRequestFinished); + d->pix.connectDownloadProgress(this, thisRequestProgress); + + } else { + requestFinished(); + } + } +} + +void QQuickImageBase::requestFinished() +{ + Q_D(QQuickImageBase); + + QQuickImageBase::Status oldStatus = d->status; + qreal oldProgress = d->progress; + + if (d->pix.isError()) { + d->status = Error; + qmlInfo(this) << d->pix.error(); + } else { + d->status = Ready; + } + + d->progress = 1.0; + + pixmapChange(); + + if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height()) + emit sourceSizeChanged(); + + if (d->status != oldStatus) + emit statusChanged(d->status); + if (d->progress != oldProgress) + emit progressChanged(d->progress); + + update(); +} + +void QQuickImageBase::requestProgress(qint64 received, qint64 total) +{ + Q_D(QQuickImageBase); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QQuickImageBase::componentComplete() +{ + Q_D(QQuickImageBase); + QQuickItem::componentComplete(); + if (d->url.isValid()) + load(); +} + +void QQuickImageBase::pixmapChange() +{ + Q_D(QQuickImageBase); + setImplicitWidth(d->pix.width()); + setImplicitHeight(d->pix.height()); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickimagebase_p.h b/src/declarative/items/qquickimagebase_p.h new file mode 100644 index 0000000000..f15ab6e5b1 --- /dev/null +++ b/src/declarative/items/qquickimagebase_p.h @@ -0,0 +1,119 @@ +// Commit: af05f64d3edc860c3cf79c7f0bdf2377faae5f40 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKIMAGEBASE_P_H +#define QQUICKIMAGEBASE_P_H + +#include "qquickimplicitsizeitem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickImageBasePrivate; +class Q_AUTOTEST_EXPORT QQuickImageBase : public QQuickImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(Status) + + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged) + Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged) + Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged) + +public: + QQuickImageBase(QQuickItem *parent=0); + ~QQuickImageBase(); + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + QUrl source() const; + virtual void setSource(const QUrl &url); + + bool asynchronous() const; + void setAsynchronous(bool); + + bool cache() const; + void setCache(bool); + + QPixmap pixmap() const; + + virtual void setSourceSize(const QSize&); + QSize sourceSize() const; + void resetSourceSize(); + + virtual void setMirror(bool mirror); + bool mirror() const; + +Q_SIGNALS: + void sourceChanged(const QUrl &); + void sourceSizeChanged(); + void statusChanged(QQuickImageBase::Status); + void progressChanged(qreal progress); + void asynchronousChanged(); + void cacheChanged(); + void mirrorChanged(); + +protected: + virtual void load(); + virtual void componentComplete(); + virtual void pixmapChange(); + QQuickImageBase(QQuickImageBasePrivate &dd, QQuickItem *parent); + +private Q_SLOTS: + virtual void requestFinished(); + void requestProgress(qint64,qint64); + +private: + Q_DISABLE_COPY(QQuickImageBase) + Q_DECLARE_PRIVATE(QQuickImageBase) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKIMAGEBASE_P_H diff --git a/src/declarative/items/qquickimagebase_p_p.h b/src/declarative/items/qquickimagebase_p_p.h new file mode 100644 index 0000000000..2347a82e92 --- /dev/null +++ b/src/declarative/items/qquickimagebase_p_p.h @@ -0,0 +1,93 @@ +// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKIMAGEBASE_P_P_H +#define QQUICKIMAGEBASE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickimplicitsizeitem_p_p.h" +#include "qquickimagebase_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickImageBase) + +public: + QQuickImageBasePrivate() + : status(QQuickImageBase::Null), + progress(0.0), + explicitSourceSize(false), + async(false), + cache(true), + mirror(false) + { + } + + QDeclarativePixmap pix; + QQuickImageBase::Status status; + QUrl url; + qreal progress; + QSize sourcesize; + bool explicitSourceSize : 1; + bool async : 1; + bool cache : 1; + bool mirror: 1; +}; + +QT_END_NAMESPACE + +#endif // QQUICKIMAGEBASE_P_P_H diff --git a/src/declarative/items/qquickimplicitsizeitem.cpp b/src/declarative/items/qquickimplicitsizeitem.cpp new file mode 100644 index 0000000000..807fb48cb6 --- /dev/null +++ b/src/declarative/items/qquickimplicitsizeitem.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickimplicitsizeitem_p.h" +#include "qquickimplicitsizeitem_p_p.h" + +QT_BEGIN_NAMESPACE + +void QQuickImplicitSizeItemPrivate::implicitWidthChanged() +{ + Q_Q(QQuickImplicitSizeItem); + emit q->implicitWidthChanged(); +} + +void QQuickImplicitSizeItemPrivate::implicitHeightChanged() +{ + Q_Q(QQuickImplicitSizeItem); + emit q->implicitHeightChanged(); +} + +QQuickImplicitSizeItem::QQuickImplicitSizeItem(QQuickItem *parent) + : QQuickItem(*(new QQuickImplicitSizeItemPrivate), parent) +{ +} + +QQuickImplicitSizeItem::QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent) + : QQuickItem(dd, parent) +{ +} + + +void QQuickImplicitSizePaintedItemPrivate::implicitWidthChanged() +{ + Q_Q(QQuickImplicitSizePaintedItem); + emit q->implicitWidthChanged(); +} + +void QQuickImplicitSizePaintedItemPrivate::implicitHeightChanged() +{ + Q_Q(QQuickImplicitSizePaintedItem); + emit q->implicitHeightChanged(); +} + +QQuickImplicitSizePaintedItem::QQuickImplicitSizePaintedItem(QQuickItem *parent) + : QQuickPaintedItem(*(new QQuickImplicitSizePaintedItemPrivate), parent) +{ +} + +QQuickImplicitSizePaintedItem::QQuickImplicitSizePaintedItem(QQuickImplicitSizePaintedItemPrivate &dd, QQuickItem *parent) + : QQuickPaintedItem(dd, parent) +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickimplicitsizeitem_p.h b/src/declarative/items/qquickimplicitsizeitem_p.h new file mode 100644 index 0000000000..eaa9b3909b --- /dev/null +++ b/src/declarative/items/qquickimplicitsizeitem_p.h @@ -0,0 +1,101 @@ +// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKIMPLICITSIZEITEM_H +#define QQUICKIMPLICITSIZEITEM_H + +#include "qquickpainteditem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickImplicitSizeItemPrivate; +class Q_AUTOTEST_EXPORT QQuickImplicitSizeItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged) + +public: + QQuickImplicitSizeItem(QQuickItem *parent = 0); + +protected: + QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent); + +Q_SIGNALS: + void implicitWidthChanged(); + void implicitHeightChanged(); + +private: + Q_DISABLE_COPY(QQuickImplicitSizeItem) + Q_DECLARE_PRIVATE(QQuickImplicitSizeItem) +}; + +class QQuickImplicitSizePaintedItemPrivate; +class Q_AUTOTEST_EXPORT QQuickImplicitSizePaintedItem : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged) + +public: + QQuickImplicitSizePaintedItem(QQuickItem *parent = 0); + +protected: + QQuickImplicitSizePaintedItem(QQuickImplicitSizePaintedItemPrivate &dd, QQuickItem *parent); + virtual void drawContents(QPainter *, const QRect &) {}; + +Q_SIGNALS: + void implicitWidthChanged(); + void implicitHeightChanged(); + +private: + Q_DISABLE_COPY(QQuickImplicitSizePaintedItem) + Q_DECLARE_PRIVATE(QQuickImplicitSizePaintedItem) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKIMPLICITSIZEITEM_H diff --git a/src/declarative/items/qquickimplicitsizeitem_p_p.h b/src/declarative/items/qquickimplicitsizeitem_p_p.h new file mode 100644 index 0000000000..683fcc1618 --- /dev/null +++ b/src/declarative/items/qquickimplicitsizeitem_p_p.h @@ -0,0 +1,92 @@ +// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKIMPLICITSIZEITEM_P_H +#define QQUICKIMPLICITSIZEITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem_p.h" +#include "qquickpainteditem_p.h" +#include "qquickimplicitsizeitem_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickImplicitSizeItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickImplicitSizeItem) + +public: + QQuickImplicitSizeItemPrivate() + { + } + + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); +}; + + +class QQuickImplicitSizePaintedItemPrivate : public QQuickPaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickImplicitSizePaintedItem) + +public: + QQuickImplicitSizePaintedItemPrivate() + { + } + + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); +}; + +QT_END_NAMESPACE + +#endif // QQUICKIMPLICITSIZEITEM_P_H diff --git a/src/declarative/items/qquickitem.cpp b/src/declarative/items/qquickitem.cpp new file mode 100644 index 0000000000..b6951d9a77 --- /dev/null +++ b/src/declarative/items/qquickitem.cpp @@ -0,0 +1,5038 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickitem.h" + +#include "qquickcanvas.h" +#include +#include "qquickcanvas_p.h" + +#include "qquickevents_p_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +// XXX todo Readd parentNotifier for faster parent bindings +// XXX todo Check that elements that create items handle memory correctly after visual ownership change + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transform QQuickTransform + \inqmlmodule QtQuick 2 + \ingroup qml-transform-elements + \brief The Transform elements provide a way of building advanced transformations on Items. + + The Transform element is a base type which cannot be instantiated directly. + The following concrete Transform types are available: + + \list + \o \l Rotation + \o \l Scale + \o \l Translate + \endlist + + The Transform elements let you create and control advanced transformations that can be configured + independently using specialized properties. + + You can assign any number of Transform elements to an \l Item. Each Transform is applied in order, + one at a time. +*/ + +/*! + \qmlclass Translate QQuickTranslate + \inqmlmodule QtQuick 2 + \ingroup qml-transform-elements + \brief The Translate object provides a way to move an Item without changing its x or y properties. + + The Translate object provides independent control over position in addition to the Item's x and y properties. + + The following example moves the Y axis of the \l Rectangle elements while still allowing the \l Row element + to lay the items out as if they had not been transformed: + \qml + import QtQuick 1.0 + + Row { + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Translate { y: 20 } + } + Rectangle { + width: 100; height: 100 + color: "red" + transform: Translate { y: -20 } + } + } + \endqml + + \image translate.png +*/ + +/*! + \qmlproperty real QtQuick2::Translate::x + + The translation along the X axis. +*/ + +/*! + \qmlproperty real QtQuick2::Translate::y + + The translation along the Y axis. +*/ + +/*! + \qmlclass Scale QQuickScale + \inqmlmodule QtQuick 2 + \ingroup qml-transform-elements + \brief The Scale element provides a way to scale an Item. + + The Scale element gives more control over scaling than using \l Item's \l{Item::scale}{scale} property. Specifically, + it allows a different scale for the x and y axes, and allows the scale to be relative to an + arbitrary point. + + The following example scales the X axis of the Rectangle, relative to its interior point 25, 25: + \qml + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Scale { origin.x: 25; origin.y: 25; xScale: 3} + } + \endqml + + \sa Rotation, Translate +*/ + +/*! + \qmlproperty real QtQuick2::Scale::origin.x + \qmlproperty real QtQuick2::Scale::origin.y + + The point that the item is scaled from (i.e., the point that stays fixed relative to the parent as + the rest of the item grows). By default the origin is 0, 0. +*/ + +/*! + \qmlproperty real QtQuick2::Scale::xScale + + The scaling factor for the X axis. +*/ + +/*! + \qmlproperty real QtQuick2::Scale::yScale + + The scaling factor for the Y axis. +*/ + +/*! + \qmlclass Rotation QQuickRotation + \inqmlmodule QtQuick 2 + \ingroup qml-transform-elements + \brief The Rotation object provides a way to rotate an Item. + + The Rotation object gives more control over rotation than using \l Item's \l{Item::rotation}{rotation} property. + Specifically, it allows (z axis) rotation to be relative to an arbitrary point. + + The following example rotates a Rectangle around its interior point 25, 25: + \qml + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Rotation { origin.x: 25; origin.y: 25; angle: 45} + } + \endqml + + Rotation also provides a way to specify 3D-like rotations for Items. For these types of + rotations you must specify the axis to rotate around in addition to the origin point. + + The following example shows various 3D-like rotations applied to an \l Image. + \snippet doc/src/snippets/declarative/rotation.qml 0 + + \image axisrotation.png + + \sa {declarative/ui-components/dialcontrol}{Dial Control example}, {declarative/toys/clocks}{Clocks example} +*/ + +/*! + \qmlproperty real QtQuick2::Rotation::origin.x + \qmlproperty real QtQuick2::Rotation::origin.y + + The origin point of the rotation (i.e., the point that stays fixed relative to the parent as + the rest of the item rotates). By default the origin is 0, 0. +*/ + +/*! + \qmlproperty real QtQuick2::Rotation::axis.x + \qmlproperty real QtQuick2::Rotation::axis.y + \qmlproperty real QtQuick2::Rotation::axis.z + + The axis to rotate around. For simple (2D) rotation around a point, you do not need to specify an axis, + as the default axis is the z axis (\c{ axis { x: 0; y: 0; z: 1 } }). + + For a typical 3D-like rotation you will usually specify both the origin and the axis. + + \image 3d-rotation-axis.png +*/ + +/*! + \qmlproperty real QtQuick2::Rotation::angle + + The angle to rotate, in degrees clockwise. +*/ + +QQuickTransformPrivate::QQuickTransformPrivate() +{ +} + +QQuickTransform::QQuickTransform(QObject *parent) +: QObject(*(new QQuickTransformPrivate), parent) +{ +} + +QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent) +: QObject(dd, parent) +{ +} + +QQuickTransform::~QQuickTransform() +{ + Q_D(QQuickTransform); + for (int ii = 0; ii < d->items.count(); ++ii) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii)); + p->transforms.removeOne(this); + p->dirty(QQuickItemPrivate::Transform); + } +} + +void QQuickTransform::update() +{ + Q_D(QQuickTransform); + for (int ii = 0; ii < d->items.count(); ++ii) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii)); + p->dirty(QQuickItemPrivate::Transform); + } +} + +QQuickContents::QQuickContents(QQuickItem *item) +: m_item(item), m_x(0), m_y(0), m_width(0), m_height(0) +{ + //### optimize + connect(this, SIGNAL(rectChanged(QRectF)), m_item, SIGNAL(childrenRectChanged(QRectF))); +} + +QQuickContents::~QQuickContents() +{ + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); + QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + } +} + +QRectF QQuickContents::rectF() const +{ + return QRectF(m_x, m_y, m_width, m_height); +} + +void QQuickContents::calcHeight(QQuickItem *changed) +{ + qreal oldy = m_y; + qreal oldheight = m_height; + + if (changed) { + qreal top = oldy; + qreal bottom = oldy + oldheight; + qreal y = changed->y(); + if (y + changed->height() > bottom) + bottom = y + changed->height(); + if (y < top) + top = y; + m_y = top; + m_height = bottom - top; + } else { + qreal top = FLT_MAX; + qreal bottom = 0; + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); + qreal y = child->y(); + if (y + child->height() > bottom) + bottom = y + child->height(); + if (y < top) + top = y; + } + if (!children.isEmpty()) + m_y = top; + m_height = qMax(bottom - top, qreal(0.0)); + } + + if (m_height != oldheight || m_y != oldy) + emit rectChanged(rectF()); +} + +void QQuickContents::calcWidth(QQuickItem *changed) +{ + qreal oldx = m_x; + qreal oldwidth = m_width; + + if (changed) { + qreal left = oldx; + qreal right = oldx + oldwidth; + qreal x = changed->x(); + if (x + changed->width() > right) + right = x + changed->width(); + if (x < left) + left = x; + m_x = left; + m_width = right - left; + } else { + qreal left = FLT_MAX; + qreal right = 0; + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); + qreal x = child->x(); + if (x + child->width() > right) + right = x + child->width(); + if (x < left) + left = x; + } + if (!children.isEmpty()) + m_x = left; + m_width = qMax(right - left, qreal(0.0)); + } + + if (m_width != oldwidth || m_x != oldx) + emit rectChanged(rectF()); +} + +void QQuickContents::complete() +{ + QList children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QQuickItem *child = children.at(i); + QQuickItemPrivate::get(child)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + //###what about changes to visibility? + } + + calcGeometry(); +} + +void QQuickContents::itemGeometryChanged(QQuickItem *changed, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_UNUSED(changed) + //### we can only pass changed if the left edge has moved left, or the right edge has moved right + if (newGeometry.width() != oldGeometry.width() || newGeometry.x() != oldGeometry.x()) + calcWidth(/*changed*/); + if (newGeometry.height() != oldGeometry.height() || newGeometry.y() != oldGeometry.y()) + calcHeight(/*changed*/); +} + +void QQuickContents::itemDestroyed(QQuickItem *item) +{ + if (item) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + calcGeometry(); +} + +void QQuickContents::childRemoved(QQuickItem *item) +{ + if (item) + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + calcGeometry(); +} + +void QQuickContents::childAdded(QQuickItem *item) +{ + if (item) + QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + calcWidth(item); + calcHeight(item); +} + +QQuickItemKeyFilter::QQuickItemKeyFilter(QQuickItem *item) +: m_processPost(false), m_next(0) +{ + QQuickItemPrivate *p = item?QQuickItemPrivate::get(item):0; + if (p) { + m_next = p->keyHandler; + p->keyHandler = this; + } +} + +QQuickItemKeyFilter::~QQuickItemKeyFilter() +{ +} + +void QQuickItemKeyFilter::keyPressed(QKeyEvent *event, bool post) +{ + if (m_next) m_next->keyPressed(event, post); +} + +void QQuickItemKeyFilter::keyReleased(QKeyEvent *event, bool post) +{ + if (m_next) m_next->keyReleased(event, post); +} + +void QQuickItemKeyFilter::inputMethodEvent(QInputMethodEvent *event, bool post) +{ + if (m_next) + m_next->inputMethodEvent(event, post); + else + event->ignore(); +} + +QVariant QQuickItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (m_next) return m_next->inputMethodQuery(query); + return QVariant(); +} + +void QQuickItemKeyFilter::componentComplete() +{ + if (m_next) m_next->componentComplete(); +} +/*! + \qmlclass KeyNavigation QQuickKeyNavigationAttached + \inqmlmodule QtQuick 2 + \ingroup qml-basic-interaction-elements + \brief The KeyNavigation attached property supports key navigation by arrow keys. + + Key-based user interfaces commonly allow the use of arrow keys to navigate between + focusable items. The KeyNavigation attached property enables this behavior by providing a + convenient way to specify the item that should gain focus when an arrow or tab key is pressed. + + The following example provides key navigation for a 2x2 grid of items: + + \snippet doc/src/snippets/declarative/keynavigation.qml 0 + + The top-left item initially receives focus by setting \l {Item::}{focus} to + \c true. When an arrow key is pressed, the focus will move to the + appropriate item, as defined by the value that has been set for + the KeyNavigation \l left, \l right, \l up or \l down properties. + + Note that if a KeyNavigation attached property receives the key press and release + events for a requested arrow or tab key, the event is accepted and does not + propagate any further. + + By default, KeyNavigation receives key events after the item to which it is attached. + If the item accepts the key event, the KeyNavigation attached property will not + receive an event for that key. Setting the \l priority property to + \c KeyNavigation.BeforeItem allows the event to be used for key navigation + before the item, rather than after. + + If item to which the focus is switching is not enabled or visible, an attempt will + be made to skip this item and focus on the next. This is possible if there are + a chain of items with the same KeyNavigation handler. If multiple items in a row are not enabled + or visible, they will also be skipped. + + KeyNavigation will implicitly set the other direction to return focus to this item. So if you set + \l left to another item, \l right will be set on that item's KeyNavigation to set focus back to this + item. However, if that item's KeyNavigation has had right explicitly set then no change will occur. + This means that the above example could have been written, with the same behaviour, without specifing + KeyNavigation.right or KeyNavigation.down for any of the items. + + \sa {Keys}{Keys attached property} +*/ + +/*! + \qmlproperty Item QtQuick2::KeyNavigation::left + \qmlproperty Item QtQuick2::KeyNavigation::right + \qmlproperty Item QtQuick2::KeyNavigation::up + \qmlproperty Item QtQuick2::KeyNavigation::down + \qmlproperty Item QtQuick2::KeyNavigation::tab + \qmlproperty Item QtQuick2::KeyNavigation::backtab + + These properties hold the item to assign focus to + when the left, right, up or down cursor keys, or the + tab key are pressed. +*/ + +/*! + \qmlproperty Item QtQuick2::KeyNavigation::tab + \qmlproperty Item QtQuick2::KeyNavigation::backtab + + These properties hold the item to assign focus to + when the Tab key or Shift+Tab key combination (Backtab) are pressed. +*/ + +QQuickKeyNavigationAttached::QQuickKeyNavigationAttached(QObject *parent) +: QObject(*(new QQuickKeyNavigationAttachedPrivate), parent), + QQuickItemKeyFilter(qobject_cast(parent)) +{ + m_processPost = true; +} + +QQuickKeyNavigationAttached * +QQuickKeyNavigationAttached::qmlAttachedProperties(QObject *obj) +{ + return new QQuickKeyNavigationAttached(obj); +} + +QQuickItem *QQuickKeyNavigationAttached::left() const +{ + Q_D(const QQuickKeyNavigationAttached); + return d->left; +} + +void QQuickKeyNavigationAttached::setLeft(QQuickItem *i) +{ + Q_D(QQuickKeyNavigationAttached); + if (d->left == i) + return; + d->left = i; + d->leftSet = true; + QQuickKeyNavigationAttached* other = + qobject_cast(qmlAttachedPropertiesObject(i)); + if (other && !other->d_func()->rightSet){ + other->d_func()->right = qobject_cast(parent()); + emit other->rightChanged(); + } + emit leftChanged(); +} + +QQuickItem *QQuickKeyNavigationAttached::right() const +{ + Q_D(const QQuickKeyNavigationAttached); + return d->right; +} + +void QQuickKeyNavigationAttached::setRight(QQuickItem *i) +{ + Q_D(QQuickKeyNavigationAttached); + if (d->right == i) + return; + d->right = i; + d->rightSet = true; + QQuickKeyNavigationAttached* other = + qobject_cast(qmlAttachedPropertiesObject(i)); + if (other && !other->d_func()->leftSet){ + other->d_func()->left = qobject_cast(parent()); + emit other->leftChanged(); + } + emit rightChanged(); +} + +QQuickItem *QQuickKeyNavigationAttached::up() const +{ + Q_D(const QQuickKeyNavigationAttached); + return d->up; +} + +void QQuickKeyNavigationAttached::setUp(QQuickItem *i) +{ + Q_D(QQuickKeyNavigationAttached); + if (d->up == i) + return; + d->up = i; + d->upSet = true; + QQuickKeyNavigationAttached* other = + qobject_cast(qmlAttachedPropertiesObject(i)); + if (other && !other->d_func()->downSet){ + other->d_func()->down = qobject_cast(parent()); + emit other->downChanged(); + } + emit upChanged(); +} + +QQuickItem *QQuickKeyNavigationAttached::down() const +{ + Q_D(const QQuickKeyNavigationAttached); + return d->down; +} + +void QQuickKeyNavigationAttached::setDown(QQuickItem *i) +{ + Q_D(QQuickKeyNavigationAttached); + if (d->down == i) + return; + d->down = i; + d->downSet = true; + QQuickKeyNavigationAttached* other = + qobject_cast(qmlAttachedPropertiesObject(i)); + if (other && !other->d_func()->upSet) { + other->d_func()->up = qobject_cast(parent()); + emit other->upChanged(); + } + emit downChanged(); +} + +QQuickItem *QQuickKeyNavigationAttached::tab() const +{ + Q_D(const QQuickKeyNavigationAttached); + return d->tab; +} + +void QQuickKeyNavigationAttached::setTab(QQuickItem *i) +{ + Q_D(QQuickKeyNavigationAttached); + if (d->tab == i) + return; + d->tab = i; + d->tabSet = true; + QQuickKeyNavigationAttached* other = + qobject_cast(qmlAttachedPropertiesObject(i)); + if (other && !other->d_func()->backtabSet) { + other->d_func()->backtab = qobject_cast(parent()); + emit other->backtabChanged(); + } + emit tabChanged(); +} + +QQuickItem *QQuickKeyNavigationAttached::backtab() const +{ + Q_D(const QQuickKeyNavigationAttached); + return d->backtab; +} + +void QQuickKeyNavigationAttached::setBacktab(QQuickItem *i) +{ + Q_D(QQuickKeyNavigationAttached); + if (d->backtab == i) + return; + d->backtab = i; + d->backtabSet = true; + QQuickKeyNavigationAttached* other = + qobject_cast(qmlAttachedPropertiesObject(i)); + if (other && !other->d_func()->tabSet) { + other->d_func()->tab = qobject_cast(parent()); + emit other->tabChanged(); + } + emit backtabChanged(); +} + +/*! + \qmlproperty enumeration QtQuick2::KeyNavigation::priority + + This property determines whether the keys are processed before + or after the attached item's own key handling. + + \list + \o KeyNavigation.BeforeItem - process the key events before normal + item key processing. If the event is used for key navigation, it will be accepted and will not + be passed on to the item. + \o KeyNavigation.AfterItem (default) - process the key events after normal item key + handling. If the item accepts the key event it will not be + handled by the KeyNavigation attached property handler. + \endlist +*/ +QQuickKeyNavigationAttached::Priority QQuickKeyNavigationAttached::priority() const +{ + return m_processPost ? AfterItem : BeforeItem; +} + +void QQuickKeyNavigationAttached::setPriority(Priority order) +{ + bool processPost = order == AfterItem; + if (processPost != m_processPost) { + m_processPost = processPost; + emit priorityChanged(); + } +} + +void QQuickKeyNavigationAttached::keyPressed(QKeyEvent *event, bool post) +{ + Q_D(QQuickKeyNavigationAttached); + event->ignore(); + + if (post != m_processPost) { + QQuickItemKeyFilter::keyPressed(event, post); + return; + } + + bool mirror = false; + switch (event->key()) { + case Qt::Key_Left: { + if (QQuickItem *parentItem = qobject_cast(parent())) + mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror; + QQuickItem* leftItem = mirror ? d->right : d->left; + if (leftItem) { + setFocusNavigation(leftItem, mirror ? "right" : "left"); + event->accept(); + } + break; + } + case Qt::Key_Right: { + if (QQuickItem *parentItem = qobject_cast(parent())) + mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror; + QQuickItem* rightItem = mirror ? d->left : d->right; + if (rightItem) { + setFocusNavigation(rightItem, mirror ? "left" : "right"); + event->accept(); + } + break; + } + case Qt::Key_Up: + if (d->up) { + setFocusNavigation(d->up, "up"); + event->accept(); + } + break; + case Qt::Key_Down: + if (d->down) { + setFocusNavigation(d->down, "down"); + event->accept(); + } + break; + case Qt::Key_Tab: + if (d->tab) { + setFocusNavigation(d->tab, "tab"); + event->accept(); + } + break; + case Qt::Key_Backtab: + if (d->backtab) { + setFocusNavigation(d->backtab, "backtab"); + event->accept(); + } + break; + default: + break; + } + + if (!event->isAccepted()) QQuickItemKeyFilter::keyPressed(event, post); +} + +void QQuickKeyNavigationAttached::keyReleased(QKeyEvent *event, bool post) +{ + Q_D(QQuickKeyNavigationAttached); + event->ignore(); + + if (post != m_processPost) { + QQuickItemKeyFilter::keyReleased(event, post); + return; + } + + bool mirror = false; + switch (event->key()) { + case Qt::Key_Left: + if (QQuickItem *parentItem = qobject_cast(parent())) + mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror; + if (mirror ? d->right : d->left) + event->accept(); + break; + case Qt::Key_Right: + if (QQuickItem *parentItem = qobject_cast(parent())) + mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror; + if (mirror ? d->left : d->right) + event->accept(); + break; + case Qt::Key_Up: + if (d->up) { + event->accept(); + } + break; + case Qt::Key_Down: + if (d->down) { + event->accept(); + } + break; + case Qt::Key_Tab: + if (d->tab) { + event->accept(); + } + break; + case Qt::Key_Backtab: + if (d->backtab) { + event->accept(); + } + break; + default: + break; + } + + if (!event->isAccepted()) QQuickItemKeyFilter::keyReleased(event, post); +} + +void QQuickKeyNavigationAttached::setFocusNavigation(QQuickItem *currentItem, const char *dir) +{ + QQuickItem *initialItem = currentItem; + bool isNextItem = false; + do { + isNextItem = false; + if (currentItem->isVisible() && currentItem->isEnabled()) { + currentItem->setFocus(true); + } else { + QObject *attached = + qmlAttachedPropertiesObject(currentItem, false); + if (attached) { + QQuickItem *tempItem = qvariant_cast(attached->property(dir)); + if (tempItem) { + currentItem = tempItem; + isNextItem = true; + } + } + } + } + while (currentItem != initialItem && isNextItem); +} + +const QQuickKeysAttached::SigMap QQuickKeysAttached::sigMap[] = { + { Qt::Key_Left, "leftPressed" }, + { Qt::Key_Right, "rightPressed" }, + { Qt::Key_Up, "upPressed" }, + { Qt::Key_Down, "downPressed" }, + { Qt::Key_Tab, "tabPressed" }, + { Qt::Key_Backtab, "backtabPressed" }, + { Qt::Key_Asterisk, "asteriskPressed" }, + { Qt::Key_NumberSign, "numberSignPressed" }, + { Qt::Key_Escape, "escapePressed" }, + { Qt::Key_Return, "returnPressed" }, + { Qt::Key_Enter, "enterPressed" }, + { Qt::Key_Delete, "deletePressed" }, + { Qt::Key_Space, "spacePressed" }, + { Qt::Key_Back, "backPressed" }, + { Qt::Key_Cancel, "cancelPressed" }, + { Qt::Key_Select, "selectPressed" }, + { Qt::Key_Yes, "yesPressed" }, + { Qt::Key_No, "noPressed" }, + { Qt::Key_Context1, "context1Pressed" }, + { Qt::Key_Context2, "context2Pressed" }, + { Qt::Key_Context3, "context3Pressed" }, + { Qt::Key_Context4, "context4Pressed" }, + { Qt::Key_Call, "callPressed" }, + { Qt::Key_Hangup, "hangupPressed" }, + { Qt::Key_Flip, "flipPressed" }, + { Qt::Key_Menu, "menuPressed" }, + { Qt::Key_VolumeUp, "volumeUpPressed" }, + { Qt::Key_VolumeDown, "volumeDownPressed" }, + { 0, 0 } +}; + +bool QQuickKeysAttachedPrivate::isConnected(const char *signalName) +{ + return isSignalConnected(signalIndex(signalName)); +} + +/*! + \qmlclass Keys QQuickKeysAttached + \inqmlmodule QtQuick 2 + \ingroup qml-basic-interaction-elements + \brief The Keys attached property provides key handling to Items. + + All visual primitives support key handling via the Keys + attached property. Keys can be handled via the onPressed + and onReleased signal properties. + + The signal properties have a \l KeyEvent parameter, named + \e event which contains details of the event. If a key is + handled \e event.accepted should be set to true to prevent the + event from propagating up the item hierarchy. + + \section1 Example Usage + + The following example shows how the general onPressed handler can + be used to test for a certain key; in this case, the left cursor + key: + + \snippet doc/src/snippets/declarative/keys/keys-pressed.qml key item + + Some keys may alternatively be handled via specific signal properties, + for example \e onSelectPressed. These handlers automatically set + \e event.accepted to true. + + \snippet doc/src/snippets/declarative/keys/keys-handler.qml key item + + See \l{Qt::Key}{Qt.Key} for the list of keyboard codes. + + \section1 Key Handling Priorities + + The Keys attached property can be configured to handle key events + before or after the item it is attached to. This makes it possible + to intercept events in order to override an item's default behavior, + or act as a fallback for keys not handled by the item. + + If \l priority is Keys.BeforeItem (default) the order of key event processing is: + + \list 1 + \o Items specified in \c forwardTo + \o specific key handlers, e.g. onReturnPressed + \o onKeyPress, onKeyRelease handlers + \o Item specific key handling, e.g. TextInput key handling + \o parent item + \endlist + + If priority is Keys.AfterItem the order of key event processing is: + + \list 1 + \o Item specific key handling, e.g. TextInput key handling + \o Items specified in \c forwardTo + \o specific key handlers, e.g. onReturnPressed + \o onKeyPress, onKeyRelease handlers + \o parent item + \endlist + + If the event is accepted during any of the above steps, key + propagation stops. + + \sa KeyEvent, {KeyNavigation}{KeyNavigation attached property} +*/ + +/*! + \qmlproperty bool QtQuick2::Keys::enabled + + This flags enables key handling if true (default); otherwise + no key handlers will be called. +*/ + +/*! + \qmlproperty enumeration QtQuick2::Keys::priority + + This property determines whether the keys are processed before + or after the attached item's own key handling. + + \list + \o Keys.BeforeItem (default) - process the key events before normal + item key processing. If the event is accepted it will not + be passed on to the item. + \o Keys.AfterItem - process the key events after normal item key + handling. If the item accepts the key event it will not be + handled by the Keys attached property handler. + \endlist +*/ + +/*! + \qmlproperty list QtQuick2::Keys::forwardTo + + This property provides a way to forward key presses, key releases, and keyboard input + coming from input methods to other items. This can be useful when you want + one item to handle some keys (e.g. the up and down arrow keys), and another item to + handle other keys (e.g. the left and right arrow keys). Once an item that has been + forwarded keys accepts the event it is no longer forwarded to items later in the + list. + + This example forwards key events to two lists: + \qml + Item { + ListView { + id: list1 + // ... + } + ListView { + id: list2 + // ... + } + Keys.forwardTo: [list1, list2] + focus: true + } + \endqml +*/ + +/*! + \qmlsignal QtQuick2::Keys::onPressed(KeyEvent event) + + This handler is called when a key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onReleased(KeyEvent event) + + This handler is called when a key has been released. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit0Pressed(KeyEvent event) + + This handler is called when the digit '0' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit1Pressed(KeyEvent event) + + This handler is called when the digit '1' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit2Pressed(KeyEvent event) + + This handler is called when the digit '2' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit3Pressed(KeyEvent event) + + This handler is called when the digit '3' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit4Pressed(KeyEvent event) + + This handler is called when the digit '4' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit5Pressed(KeyEvent event) + + This handler is called when the digit '5' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit6Pressed(KeyEvent event) + + This handler is called when the digit '6' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit7Pressed(KeyEvent event) + + This handler is called when the digit '7' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit8Pressed(KeyEvent event) + + This handler is called when the digit '8' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDigit9Pressed(KeyEvent event) + + This handler is called when the digit '9' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onLeftPressed(KeyEvent event) + + This handler is called when the Left arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onRightPressed(KeyEvent event) + + This handler is called when the Right arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onUpPressed(KeyEvent event) + + This handler is called when the Up arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDownPressed(KeyEvent event) + + This handler is called when the Down arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onTabPressed(KeyEvent event) + + This handler is called when the Tab key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onBacktabPressed(KeyEvent event) + + This handler is called when the Shift+Tab key combination (Backtab) has + been pressed. The \a event parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onAsteriskPressed(KeyEvent event) + + This handler is called when the Asterisk '*' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onEscapePressed(KeyEvent event) + + This handler is called when the Escape key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onReturnPressed(KeyEvent event) + + This handler is called when the Return key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onEnterPressed(KeyEvent event) + + This handler is called when the Enter key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onDeletePressed(KeyEvent event) + + This handler is called when the Delete key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onSpacePressed(KeyEvent event) + + This handler is called when the Space key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onBackPressed(KeyEvent event) + + This handler is called when the Back key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onCancelPressed(KeyEvent event) + + This handler is called when the Cancel key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onSelectPressed(KeyEvent event) + + This handler is called when the Select key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onYesPressed(KeyEvent event) + + This handler is called when the Yes key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onNoPressed(KeyEvent event) + + This handler is called when the No key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onContext1Pressed(KeyEvent event) + + This handler is called when the Context1 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onContext2Pressed(KeyEvent event) + + This handler is called when the Context2 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onContext3Pressed(KeyEvent event) + + This handler is called when the Context3 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onContext4Pressed(KeyEvent event) + + This handler is called when the Context4 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onCallPressed(KeyEvent event) + + This handler is called when the Call key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onHangupPressed(KeyEvent event) + + This handler is called when the Hangup key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onFlipPressed(KeyEvent event) + + This handler is called when the Flip key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onMenuPressed(KeyEvent event) + + This handler is called when the Menu key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onVolumeUpPressed(KeyEvent event) + + This handler is called when the VolumeUp key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal QtQuick2::Keys::onVolumeDownPressed(KeyEvent event) + + This handler is called when the VolumeDown key has been pressed. The \a event + parameter provides information about the event. +*/ + +QQuickKeysAttached::QQuickKeysAttached(QObject *parent) +: QObject(*(new QQuickKeysAttachedPrivate), parent), + QQuickItemKeyFilter(qobject_cast(parent)) +{ + Q_D(QQuickKeysAttached); + m_processPost = false; + d->item = qobject_cast(parent); +} + +QQuickKeysAttached::~QQuickKeysAttached() +{ +} + +QQuickKeysAttached::Priority QQuickKeysAttached::priority() const +{ + return m_processPost ? AfterItem : BeforeItem; +} + +void QQuickKeysAttached::setPriority(Priority order) +{ + bool processPost = order == AfterItem; + if (processPost != m_processPost) { + m_processPost = processPost; + emit priorityChanged(); + } +} + +void QQuickKeysAttached::componentComplete() +{ + Q_D(QQuickKeysAttached); + if (d->item) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *targetItem = d->targets.at(ii); + if (targetItem && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) { + d->item->setFlag(QQuickItem::ItemAcceptsInputMethod); + break; + } + } + } +} + +void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post) +{ + Q_D(QQuickKeysAttached); + if (post != m_processPost || !d->enabled || d->inPress) { + event->ignore(); + QQuickItemKeyFilter::keyPressed(event, post); + return; + } + + // first process forwards + if (d->item && d->item->canvas()) { + d->inPress = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *i = d->targets.at(ii); + if (i && i->isVisible()) { + d->item->canvas()->sendEvent(i, event); + if (event->isAccepted()) { + d->inPress = false; + return; + } + } + } + d->inPress = false; + } + + QQuickKeyEvent ke(*event); + QByteArray keySignal = keyToSignal(event->key()); + if (!keySignal.isEmpty()) { + keySignal += "(QQuickKeyEvent*)"; + if (d->isConnected(keySignal)) { + // If we specifically handle a key then default to accepted + ke.setAccepted(true); + int idx = QQuickKeysAttached::staticMetaObject.indexOfSignal(keySignal); + metaObject()->method(idx).invoke(this, Qt::DirectConnection, Q_ARG(QQuickKeyEvent*, &ke)); + } + } + if (!ke.isAccepted()) + emit pressed(&ke); + event->setAccepted(ke.isAccepted()); + + if (!event->isAccepted()) QQuickItemKeyFilter::keyPressed(event, post); +} + +void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post) +{ + Q_D(QQuickKeysAttached); + if (post != m_processPost || !d->enabled || d->inRelease) { + event->ignore(); + QQuickItemKeyFilter::keyReleased(event, post); + return; + } + + if (d->item && d->item->canvas()) { + d->inRelease = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *i = d->targets.at(ii); + if (i && i->isVisible()) { + d->item->canvas()->sendEvent(i, event); + if (event->isAccepted()) { + d->inRelease = false; + return; + } + } + } + d->inRelease = false; + } + + QQuickKeyEvent ke(*event); + emit released(&ke); + event->setAccepted(ke.isAccepted()); + + if (!event->isAccepted()) QQuickItemKeyFilter::keyReleased(event, post); +} + +void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) +{ + Q_D(QQuickKeysAttached); + if (post == m_processPost && d->item && !d->inIM && d->item->canvas()) { + d->inIM = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *i = d->targets.at(ii); + if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod)) { + d->item->canvas()->sendEvent(i, event); + if (event->isAccepted()) { + d->imeItem = i; + d->inIM = false; + return; + } + } + } + d->inIM = false; + } + QQuickItemKeyFilter::inputMethodEvent(event, post); +} + +QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QQuickKeysAttached); + if (d->item) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QQuickItem *i = d->targets.at(ii); + if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod) && i == d->imeItem) { + //### how robust is i == d->imeItem check? + QVariant v = i->inputMethodQuery(query); + if (v.userType() == QVariant::RectF) + v = d->item->mapRectFromItem(i, v.toRectF()); //### cost? + return v; + } + } + } + return QQuickItemKeyFilter::inputMethodQuery(query); +} + +QQuickKeysAttached *QQuickKeysAttached::qmlAttachedProperties(QObject *obj) +{ + return new QQuickKeysAttached(obj); +} + +/*! + \qmlclass LayoutMirroring QQuickLayoutMirroringAttached + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \brief The LayoutMirroring attached property is used to mirror layout behavior. + + The LayoutMirroring attached property is used to horizontally mirror \l {anchor-layout}{Item anchors}, + \l{Using QML Positioner and Repeater Items}{positioner} elements (such as \l Row and \l Grid) + and views (such as \l GridView and horizontal \l ListView). Mirroring is a visual change: left + anchors become right anchors, and positioner elements like \l Grid and \l Row reverse the + horizontal layout of child items. + + Mirroring is enabled for an item by setting the \l enabled property to true. By default, this + only affects the item itself; setting the \l childrenInherit property to true propagates the mirroring + behavior to all child elements as well. If the \c LayoutMirroring attached property has not been defined + for an item, mirroring is not enabled. + + The following example shows mirroring in action. The \l Row below is specified as being anchored + to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally + reversed and it is now anchored to the right. Also, since items in a \l Row are positioned + from left to right by default, they are now positioned from right to left instead, as demonstrated + by the numbering and opacity of the items: + + \snippet doc/src/snippets/declarative/layoutmirroring.qml 0 + + \image layoutmirroring.png + + Layout mirroring is useful when it is necessary to support both left-to-right and right-to-left + layout versions of an application to target different language areas. The \l childrenInherit + property allows layout mirroring to be applied without manually setting layout configurations + for every item in an application. Keep in mind, however, that mirroring does not affect any + positioning that is defined by the \l Item \l {Item::}{x} coordinate value, so even with + mirroring enabled, it will often be necessary to apply some layout fixes to support the + desired layout direction. Also, it may be necessary to disable the mirroring of individual + child items (by setting \l {enabled}{LayoutMirroring.enabled} to false for such items) if + mirroring is not the desired behavior, or if the child item already implements mirroring in + some custom way. + + See \l {QML Right-to-left User Interfaces} for further details on using \c LayoutMirroring and + other related features to implement right-to-left support for an application. +*/ + +/*! + \qmlproperty bool QtQuick2::LayoutMirroring::enabled + + This property holds whether the item's layout is mirrored horizontally. Setting this to true + horizontally reverses \l {anchor-layout}{anchor} settings such that left anchors become right, + and right anchors become left. For \l{Using QML Positioner and Repeater Items}{positioner} elements + (such as \l Row and \l Grid) and view elements (such as \l {GridView}{GridView} and \l {ListView}{ListView}) + this also mirrors the horizontal layout direction of the item. + + The default value is false. +*/ + +/*! + \qmlproperty bool QtQuick2::LayoutMirroring::childrenInherit + + This property holds whether the \l {enabled}{LayoutMirroring.enabled} value for this item + is inherited by its children. + + The default value is false. +*/ + + +QQuickLayoutMirroringAttached::QQuickLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0) +{ + if (QQuickItem *item = qobject_cast(parent)) { + itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->attachedLayoutDirection = this; + } else + qmlInfo(parent) << tr("LayoutDirection attached property only works with Items"); +} + +QQuickLayoutMirroringAttached * QQuickLayoutMirroringAttached::qmlAttachedProperties(QObject *object) +{ + return new QQuickLayoutMirroringAttached(object); +} + +bool QQuickLayoutMirroringAttached::enabled() const +{ + return itemPrivate ? itemPrivate->effectiveLayoutMirror : false; +} + +void QQuickLayoutMirroringAttached::setEnabled(bool enabled) +{ + if (!itemPrivate) + return; + + itemPrivate->isMirrorImplicit = false; + if (enabled != itemPrivate->effectiveLayoutMirror) { + itemPrivate->setLayoutMirror(enabled); + if (itemPrivate->inheritMirrorFromItem) + itemPrivate->resolveLayoutMirror(); + } +} + +void QQuickLayoutMirroringAttached::resetEnabled() +{ + if (itemPrivate && !itemPrivate->isMirrorImplicit) { + itemPrivate->isMirrorImplicit = true; + itemPrivate->resolveLayoutMirror(); + } +} + +bool QQuickLayoutMirroringAttached::childrenInherit() const +{ + return itemPrivate ? itemPrivate->inheritMirrorFromItem : false; +} + +void QQuickLayoutMirroringAttached::setChildrenInherit(bool childrenInherit) { + if (itemPrivate && childrenInherit != itemPrivate->inheritMirrorFromItem) { + itemPrivate->inheritMirrorFromItem = childrenInherit; + itemPrivate->resolveLayoutMirror(); + childrenInheritChanged(); + } +} + +void QQuickItemPrivate::resolveLayoutMirror() +{ + Q_Q(QQuickItem); + if (QQuickItem *parentItem = q->parentItem()) { + QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parentItem); + setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); + } else { + setImplicitLayoutMirror(isMirrorImplicit ? false : effectiveLayoutMirror, inheritMirrorFromItem); + } +} + +void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit) +{ + inherit = inherit || inheritMirrorFromItem; + if (!isMirrorImplicit && inheritMirrorFromItem) + mirror = effectiveLayoutMirror; + if (mirror == inheritedLayoutMirror && inherit == inheritMirrorFromParent) + return; + + inheritMirrorFromParent = inherit; + inheritedLayoutMirror = inheritMirrorFromParent ? mirror : false; + + if (isMirrorImplicit) + setLayoutMirror(inherit ? inheritedLayoutMirror : false); + for (int i = 0; i < childItems.count(); ++i) { + if (QQuickItem *child = qobject_cast(childItems.at(i))) { + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); + childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); + } + } +} + +void QQuickItemPrivate::setLayoutMirror(bool mirror) +{ + if (mirror != effectiveLayoutMirror) { + effectiveLayoutMirror = mirror; + if (_anchors) { + QQuickAnchorsPrivate *anchor_d = QQuickAnchorsPrivate::get(_anchors); + anchor_d->fillChanged(); + anchor_d->centerInChanged(); + anchor_d->updateHorizontalAnchors(); + emit _anchors->mirroredChanged(); + } + mirrorChange(); + if (attachedLayoutDirection) { + emit attachedLayoutDirection->enabledChanged(); + } + } +} + +/*! + \class QQuickItem + \brief The QQuickItem class provides the most basic of all visual items in QML. + + All visual items in Qt Declarative inherit from QQuickItem. Although QQuickItem + has no visual appearance, it defines all the properties that are + common across visual items - such as the x and y position, the + width and height, \l {anchor-layout}{anchoring} and key handling. + + You can subclass QQuickItem to provide your own custom visual item that inherits + these features. Note that, because it does not draw anything, QQuickItem sets the + QGraphicsItem::ItemHasNoContents flag. If you subclass QQuickItem to create a visual + item, you will need to unset this flag. + +*/ + +/*! + \qmlclass Item QQuickItem + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The Item is the most basic of all visual items in QML. + + All visual items in Qt Declarative inherit from Item. Although Item + has no visual appearance, it defines all the properties that are + common across visual items - such as the x and y position, the + width and height, \l {anchor-layout}{anchoring} and key handling. + + Item is also useful for grouping items together. + + \qml + Item { + Image { + source: "tile.png" + } + Image { + x: 80 + width: 100 + height: 100 + source: "tile.png" + } + Image { + x: 190 + width: 100 + height: 100 + fillMode: Image.Tile + source: "tile.png" + } + } + \endqml + + + \section1 Key Handling + + Key handling is available to all Item-based visual elements via the \l {Keys}{Keys} + attached property. The \e Keys attached property provides basic handlers such + as \l {Keys::onPressed}{onPressed} and \l {Keys::onReleased}{onReleased}, + as well as handlers for specific keys, such as + \l {Keys::onCancelPressed}{onCancelPressed}. The example below + assigns \l {qmlfocus}{focus} to the item and handles + the Left key via the general \e onPressed handler and the Select key via the + onSelectPressed handler: + + \qml + Item { + focus: true + Keys.onPressed: { + if (event.key == Qt.Key_Left) { + console.log("move left"); + event.accepted = true; + } + } + Keys.onSelectPressed: console.log("Selected"); + } + \endqml + + See the \l {Keys}{Keys} attached property for detailed documentation. + + \section1 Layout Mirroring + + Item layouts can be mirrored using the \l {LayoutMirroring}{LayoutMirroring} attached property. + +*/ + +/*! + \fn void QQuickItem::childrenRectChanged(const QRectF &) + \internal +*/ + +/*! + \fn void QQuickItem::baselineOffsetChanged(qreal) + \internal +*/ + +/*! + \fn void QQuickItem::stateChanged(const QString &state) + \internal +*/ + +/*! + \fn void QQuickItem::parentChanged(QQuickItem *) + \internal +*/ + +/*! + \fn void QQuickItem::smoothChanged(bool) + \internal +*/ + +/*! + \fn void QQuickItem::clipChanged(bool) + \internal +*/ + +/*! \fn void QQuickItem::transformOriginChanged(TransformOrigin) + \internal +*/ + +/*! + \fn void QQuickItem::focusChanged(bool) + \internal +*/ + +/*! + \fn void QQuickItem::activeFocusChanged(bool) + \internal +*/ +/*! + \fn QQuickItem::QQuickItem(QQuickItem *parent) + + Constructs a QQuickItem with the given \a parent. +*/ +QQuickItem::QQuickItem(QQuickItem* parent) +: QObject(*(new QQuickItemPrivate), parent) +{ + Q_D(QQuickItem); + d->init(parent); +} + +/*! \internal +*/ +QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent) +: QObject(dd, parent) +{ + Q_D(QQuickItem); + d->init(parent); +} + +#ifndef QT_NO_DEBUG +static int qt_item_count = 0; + +static void qt_print_item_count() +{ + qDebug("Number of leaked items: %i", qt_item_count); + qt_item_count = -1; +} +#endif + +/*! + Destroys the QQuickItem. +*/ +QQuickItem::~QQuickItem() +{ +#ifndef QT_NO_DEBUG + --qt_item_count; + if (qt_item_count < 0) + qDebug("Item destroyed after qt_print_item_count() was called."); +#endif + + Q_D(QQuickItem); + + if (d->parentItem) + setParentItem(0); + else if (d->canvas && d->itemNodeInstance) + QQuickCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance); // cleanup root + // XXX todo - optimize + while (!d->childItems.isEmpty()) + d->childItems.first()->setParentItem(0); + + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + if (anchor) + anchor->clearItem(this); + } + + // XXX todo - the original checks if the parent is being destroyed + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + if (anchor && anchor->item && anchor->item->parent() != this) //child will be deleted anyway + anchor->updateOnComplete(); + } + + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Destroyed) + change.listener->itemDestroyed(this); + } + d->changeListeners.clear(); + delete d->_anchorLines; d->_anchorLines = 0; + delete d->_anchors; d->_anchors = 0; + delete d->_stateGroup; d->_stateGroup = 0; + delete d->_contents; d->_contents = 0; +} + +/*! + \qmlproperty enumeration QtQuick2::Item::transformOrigin + This property holds the origin point around which scale and rotation transform. + + Nine transform origins are available, as shown in the image below. + + \image declarative-transformorigin.png + + This example rotates an image around its bottom-right corner. + \qml + Image { + source: "myimage.png" + transformOrigin: Item.BottomRight + rotation: 45 + } + \endqml + + The default transform origin is \c Item.Center. + + To set an arbitrary transform origin point use the \l Scale or \l Rotation + transform elements. +*/ + +/*! + \qmlproperty Item QtQuick2::Item::parent + This property holds the parent of the item. +*/ + +/*! + \property QQuickItem::parent + This property holds the parent of the item. +*/ +void QQuickItem::setParentItem(QQuickItem *parentItem) +{ + Q_D(QQuickItem); + if (parentItem == d->parentItem) + return; + + d->removeFromDirtyList(); + + QQuickItem *oldParentItem = d->parentItem; + QQuickItem *scopeFocusedItem = 0; + + if (oldParentItem) { + QQuickItemPrivate *op = QQuickItemPrivate::get(oldParentItem); + + QQuickItem *scopeItem = 0; + + if (d->canvas && hasFocus()) { + scopeItem = oldParentItem; + while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + scopeFocusedItem = this; + } else if (d->canvas && !isFocusScope() && d->subFocusItem) { + scopeItem = oldParentItem; + while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + scopeFocusedItem = d->subFocusItem; + } + + if (scopeFocusedItem) + QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem, + QQuickCanvasPrivate::DontChangeFocusProperty); + + op->removeChild(this); + } + + d->parentItem = parentItem; + + QQuickCanvas *parentCanvas = parentItem?QQuickItemPrivate::get(parentItem)->canvas:0; + if (d->canvas != parentCanvas) { + QQuickItemPrivate::InitializationState initState; + initState.clear(); + d->initCanvas(&initState, parentCanvas); + } + + d->dirty(QQuickItemPrivate::ParentChanged); + + if (d->parentItem) + QQuickItemPrivate::get(d->parentItem)->addChild(this); + + d->setEffectiveVisibleRecur(d->calcEffectiveVisible()); + d->setEffectiveEnableRecur(d->calcEffectiveEnable()); + + if (scopeFocusedItem && d->parentItem && d->canvas) { + // We need to test whether this item becomes scope focused + QQuickItem *scopeItem = 0; + scopeItem = d->parentItem; + while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); + + if (scopeItem->scopedFocusItem()) { + QQuickItemPrivate::get(scopeFocusedItem)->focus = false; + emit scopeFocusedItem->focusChanged(false); + } else { + QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem, + QQuickCanvasPrivate::DontChangeFocusProperty); + } + } + + d->resolveLayoutMirror(); + + d->itemChange(ItemParentHasChanged, d->parentItem); + + emit parentChanged(d->parentItem); +} + +void QQuickItem::stackBefore(const QQuickItem *sibling) +{ + Q_D(QQuickItem); + if (!sibling || sibling == this || !d->parentItem || d->parentItem != QQuickItemPrivate::get(sibling)->parentItem) { + qWarning("QQuickItem::stackBefore: Cannot stack before %p, which must be a sibling", sibling); + return; + } + + QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(d->parentItem); + + int myIndex = parentPrivate->childItems.indexOf(this); + int siblingIndex = parentPrivate->childItems.indexOf(const_cast(sibling)); + + Q_ASSERT(myIndex != -1 && siblingIndex != -1); + + if (myIndex == siblingIndex - 1) + return; + + parentPrivate->childItems.removeAt(myIndex); + + if (myIndex < siblingIndex) --siblingIndex; + + parentPrivate->childItems.insert(siblingIndex, this); + + parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged); + + for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.count(); ++ii) + QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged(); +} + +void QQuickItem::stackAfter(const QQuickItem *sibling) +{ + Q_D(QQuickItem); + if (!sibling || sibling == this || !d->parentItem || d->parentItem != QQuickItemPrivate::get(sibling)->parentItem) { + qWarning("QQuickItem::stackAfter: Cannot stack after %p, which must be a sibling", sibling); + return; + } + + QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(d->parentItem); + + int myIndex = parentPrivate->childItems.indexOf(this); + int siblingIndex = parentPrivate->childItems.indexOf(const_cast(sibling)); + + Q_ASSERT(myIndex != -1 && siblingIndex != -1); + + if (myIndex == siblingIndex + 1) + return; + + parentPrivate->childItems.removeAt(myIndex); + + if (myIndex < siblingIndex) --siblingIndex; + + parentPrivate->childItems.insert(siblingIndex + 1, this); + + parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged); + + for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.count(); ++ii) + QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged(); +} + +/*! + Returns the QQuickItem parent of this item. +*/ +QQuickItem *QQuickItem::parentItem() const +{ + Q_D(const QQuickItem); + return d->parentItem; +} + +QSGEngine *QQuickItem::sceneGraphEngine() const +{ + return canvas()->sceneGraphEngine(); +} + +QQuickCanvas *QQuickItem::canvas() const +{ + Q_D(const QQuickItem); + return d->canvas; +} + +static bool itemZOrder_sort(QQuickItem *lhs, QQuickItem *rhs) +{ + return lhs->z() < rhs->z(); +} + +QList QQuickItemPrivate::paintOrderChildItems() const +{ + // XXX todo - optimize, don't sort and return items that are + // ignored anyway, like invisible or disabled items. + QList items = childItems; + qStableSort(items.begin(), items.end(), itemZOrder_sort); + return items; +} + +void QQuickItemPrivate::addChild(QQuickItem *child) +{ + Q_Q(QQuickItem); + + Q_ASSERT(!childItems.contains(child)); + + childItems.append(child); + + dirty(QQuickItemPrivate::ChildrenChanged); + + itemChange(QQuickItem::ItemChildAddedChange, child); + + emit q->childrenChanged(); +} + +void QQuickItemPrivate::removeChild(QQuickItem *child) +{ + Q_Q(QQuickItem); + + Q_ASSERT(child); + Q_ASSERT(childItems.contains(child)); + childItems.removeOne(child); + Q_ASSERT(!childItems.contains(child)); + + dirty(QQuickItemPrivate::ChildrenChanged); + + itemChange(QQuickItem::ItemChildRemovedChange, child); + + emit q->childrenChanged(); +} + +void QQuickItemPrivate::InitializationState::clear() +{ + focusScope = 0; +} + +void QQuickItemPrivate::InitializationState::clear(QQuickItem *fs) +{ + focusScope = fs; +} + +QQuickItem *QQuickItemPrivate::InitializationState::getFocusScope(QQuickItem *item) +{ + if (!focusScope) { + QQuickItem *fs = item->parentItem(); + while (!fs->isFocusScope()) + fs = fs->parentItem(); + focusScope = fs; + } + return focusScope; +} + +void QQuickItemPrivate::initCanvas(InitializationState *state, QQuickCanvas *c) +{ + Q_Q(QQuickItem); + + if (canvas) { + removeFromDirtyList(); + QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas); + if (polishScheduled) + c->itemsToPolish.remove(q); + if (c->mouseGrabberItem == q) + c->mouseGrabberItem = 0; + if ( hoverEnabled ) + c->hoverItems.removeAll(q); + if (itemNodeInstance) + c->cleanup(itemNodeInstance); + } + + canvas = c; + + if (canvas && polishScheduled) + QQuickCanvasPrivate::get(canvas)->itemsToPolish.insert(q); + + itemNodeInstance = 0; + opacityNode = 0; + clipNode = 0; + rootNode = 0; + groupNode = 0; + paintNode = 0; + beforePaintNode = 0; + + InitializationState _dummy; + InitializationState *childState = state; + + if (c && q->isFocusScope()) { + _dummy.clear(q); + childState = &_dummy; + } + + for (int ii = 0; ii < childItems.count(); ++ii) { + QQuickItem *child = childItems.at(ii); + QQuickItemPrivate::get(child)->initCanvas(childState, c); + } + + if (c && focus) { + // Fixup + if (state->getFocusScope(q)->scopedFocusItem()) { + focus = false; + emit q->focusChanged(false); + } else { + QQuickCanvasPrivate::get(canvas)->setFocusInScope(state->getFocusScope(q), q); + } + } + + dirty(Canvas); + + itemChange(QQuickItem::ItemSceneChange, c); +} + +/*! +Returns a transform that maps points from canvas space into item space. +*/ +QTransform QQuickItemPrivate::canvasToItemTransform() const +{ + // XXX todo - optimize + return itemToCanvasTransform().inverted(); +} + +/*! +Returns a transform that maps points from item space into canvas space. +*/ +QTransform QQuickItemPrivate::itemToCanvasTransform() const +{ + // XXX todo + QTransform rv = parentItem?QQuickItemPrivate::get(parentItem)->itemToCanvasTransform():QTransform(); + itemToParentTransform(rv); + return rv; +} + +/*! +Motifies \a t with this items local transform relative to its parent. +*/ +void QQuickItemPrivate::itemToParentTransform(QTransform &t) const +{ + if (x || y) + t.translate(x, y); + + if (!transforms.isEmpty()) { + QMatrix4x4 m(t); + for (int ii = transforms.count() - 1; ii >= 0; --ii) + transforms.at(ii)->applyTo(&m); + t = m.toTransform(); + } + + if (scale != 1. || rotation != 0.) { + QPointF tp = computeTransformOrigin(); + t.translate(tp.x(), tp.y()); + t.scale(scale, scale); + t.rotate(rotation); + t.translate(-tp.x(), -tp.y()); + } +} + + +/*! + \qmlproperty real QtQuick2::Item::childrenRect.x + \qmlproperty real QtQuick2::Item::childrenRect.y + \qmlproperty real QtQuick2::Item::childrenRect.width + \qmlproperty real QtQuick2::Item::childrenRect.height + + The childrenRect properties allow an item access to the geometry of its + children. This property is useful if you have an item that needs to be + sized to fit its children. +*/ + + +/*! + \qmlproperty list QtQuick2::Item::children + \qmlproperty list QtQuick2::Item::resources + + The children property contains the list of visual children of this item. + The resources property contains non-visual resources that you want to + reference by name. + + Generally you can rely on Item's default property to handle all this for + you, but it can come in handy in some cases. + + \qml + Item { + children: [ + Text {}, + Rectangle {} + ] + resources: [ + Component { + id: myComponent + Text {} + } + ] + } + \endqml +*/ + +/*! + Returns true if construction of the QML component is complete; otherwise + returns false. + + It is often desirable to delay some processing until the component is + completed. + + \sa componentComplete() +*/ +bool QQuickItem::isComponentComplete() const +{ + Q_D(const QQuickItem); + return d->componentComplete; +} + +QQuickItemPrivate::QQuickItemPrivate() +: _anchors(0), _contents(0), baselineOffset(0), _anchorLines(0), _stateGroup(0), origin(QQuickItem::Center), + + flags(0), widthValid(false), heightValid(false), componentComplete(true), + keepMouse(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false), + notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true), + effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false), + inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), + inheritMirrorFromParent(false), inheritMirrorFromItem(false), childrenDoNotOverlap(false), + + canvas(0), parentItem(0), + + subFocusItem(0), + + x(0), y(0), width(0), height(0), implicitWidth(0), implicitHeight(0), + z(0), scale(1), rotation(0), opacity(1), + + attachedLayoutDirection(0), acceptedMouseButtons(0), + imHints(Qt::ImhMultiLine), + + keyHandler(0), + + dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0), + + itemNodeInstance(0), opacityNode(0), clipNode(0), rootNode(0), groupNode(0), paintNode(0) + , beforePaintNode(0), effectRefCount(0), hideRefCount(0) +{ +} + +void QQuickItemPrivate::init(QQuickItem *parent) +{ +#ifndef QT_NO_DEBUG + ++qt_item_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_item_count); + atexit_registered = true; + } +#endif + + Q_Q(QQuickItem); + baselineOffset.invalidate(); + + if (parent) { + q->setParentItem(parent); + QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent); + setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); + } +} + +void QQuickItemPrivate::data_append(QDeclarativeListProperty *prop, QObject *o) +{ + if (!o) + return; + + QQuickItem *that = static_cast(prop->object); + + // This test is measurably (albeit only slightly) faster than qobject_cast<>() + const QMetaObject *mo = o->metaObject(); + while (mo && mo != &QQuickItem::staticMetaObject) { + mo = mo->d.superdata; + } + + if (mo) { + QQuickItem *item = static_cast(o); + item->setParentItem(that); + } else { + if (o->inherits("QGraphicsItem")) + qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className()); + + // XXX todo - do we really want this behavior? + o->setParent(that); + } +} + +/*! + \qmlproperty list QtQuick2::Item::data + \default + + The data property allows you to freely mix visual children and resources + in an item. If you assign a visual item to the data list it becomes + a child and if you assign any other object type, it is added as a resource. + + So you can write: + \qml + Item { + Text {} + Rectangle {} + Timer {} + } + \endqml + + instead of: + \qml + Item { + children: [ + Text {}, + Rectangle {} + ] + resources: [ + Timer {} + ] + } + \endqml + + data is a behind-the-scenes property: you should never need to explicitly + specify it. + */ + +int QQuickItemPrivate::data_count(QDeclarativeListProperty *prop) +{ + Q_UNUSED(prop); + // XXX todo + return 0; +} + +QObject *QQuickItemPrivate::data_at(QDeclarativeListProperty *prop, int i) +{ + Q_UNUSED(prop); + Q_UNUSED(i); + // XXX todo + return 0; +} + +void QQuickItemPrivate::data_clear(QDeclarativeListProperty *prop) +{ + Q_UNUSED(prop); + // XXX todo +} + +QObject *QQuickItemPrivate::resources_at(QDeclarativeListProperty *prop, int index) +{ + const QObjectList children = prop->object->children(); + if (index < children.count()) + return children.at(index); + else + return 0; +} + +void QQuickItemPrivate::resources_append(QDeclarativeListProperty *prop, QObject *o) +{ + // XXX todo - do we really want this behavior? + o->setParent(prop->object); +} + +int QQuickItemPrivate::resources_count(QDeclarativeListProperty *prop) +{ + return prop->object->children().count(); +} + +void QQuickItemPrivate::resources_clear(QDeclarativeListProperty *prop) +{ + // XXX todo - do we really want this behavior? + const QObjectList children = prop->object->children(); + for (int index = 0; index < children.count(); index++) + children.at(index)->setParent(0); +} + +QQuickItem *QQuickItemPrivate::children_at(QDeclarativeListProperty *prop, int index) +{ + QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast(prop->object)); + if (index >= p->childItems.count() || index < 0) + return 0; + else + return p->childItems.at(index); +} + +void QQuickItemPrivate::children_append(QDeclarativeListProperty *prop, QQuickItem *o) +{ + if (!o) + return; + + QQuickItem *that = static_cast(prop->object); + if (o->parentItem() == that) + o->setParentItem(0); + + o->setParentItem(that); +} + +int QQuickItemPrivate::children_count(QDeclarativeListProperty *prop) +{ + QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast(prop->object)); + return p->childItems.count(); +} + +void QQuickItemPrivate::children_clear(QDeclarativeListProperty *prop) +{ + QQuickItem *that = static_cast(prop->object); + QQuickItemPrivate *p = QQuickItemPrivate::get(that); + while (!p->childItems.isEmpty()) + p->childItems.at(0)->setParentItem(0); +} + +int QQuickItemPrivate::transform_count(QDeclarativeListProperty *prop) +{ + QQuickItem *that = static_cast(prop->object); + return QQuickItemPrivate::get(that)->transforms.count(); +} + +void QQuickTransform::appendToItem(QQuickItem *item) +{ + Q_D(QQuickTransform); + if (!item) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + + if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) { + p->transforms.removeOne(this); + p->transforms.append(this); + } else { + p->transforms.append(this); + d->items.append(item); + } + + p->dirty(QQuickItemPrivate::Transform); +} + +void QQuickTransform::prependToItem(QQuickItem *item) +{ + Q_D(QQuickTransform); + if (!item) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + + if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) { + p->transforms.removeOne(this); + p->transforms.prepend(this); + } else { + p->transforms.prepend(this); + d->items.append(item); + } + + p->dirty(QQuickItemPrivate::Transform); +} + +void QQuickItemPrivate::transform_append(QDeclarativeListProperty *prop, QQuickTransform *transform) +{ + if (!transform) + return; + + QQuickItem *that = static_cast(prop->object); + transform->appendToItem(that); +} + +QQuickTransform *QQuickItemPrivate::transform_at(QDeclarativeListProperty *prop, int idx) +{ + QQuickItem *that = static_cast(prop->object); + QQuickItemPrivate *p = QQuickItemPrivate::get(that); + + if (idx < 0 || idx >= p->transforms.count()) + return 0; + else + return p->transforms.at(idx); +} + +void QQuickItemPrivate::transform_clear(QDeclarativeListProperty *prop) +{ + QQuickItem *that = static_cast(prop->object); + QQuickItemPrivate *p = QQuickItemPrivate::get(that); + + for (int ii = 0; ii < p->transforms.count(); ++ii) { + QQuickTransform *t = p->transforms.at(ii); + QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t); + tp->items.removeOne(that); + } + + p->transforms.clear(); + + p->dirty(QQuickItemPrivate::Transform); +} + +/*! + \property QQuickItem::childrenRect + \brief The geometry of an item's children. + + This property holds the (collective) position and size of the item's children. +*/ + +/*! + \qmlproperty real QtQuick2::Item::x + \qmlproperty real QtQuick2::Item::y + \qmlproperty real QtQuick2::Item::width + \qmlproperty real QtQuick2::Item::height + + Defines the item's position and size relative to its parent. + + \qml + Item { x: 100; y: 100; width: 100; height: 100 } + \endqml + */ + +/*! + \qmlproperty real QtQuick2::Item::z + + Sets the stacking order of sibling items. By default the stacking order is 0. + + Items with a higher stacking value are drawn on top of siblings with a + lower stacking order. Items with the same stacking value are drawn + bottom up in the order they appear. Items with a negative stacking + value are drawn under their parent's content. + + The following example shows the various effects of stacking order. + + \table + \row + \o \image declarative-item_stacking1.png + \o Same \c z - later children above earlier children: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + } + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + \endqml + \row + \o \image declarative-item_stacking2.png + \o Higher \c z on top: + \qml + Item { + Rectangle { + z: 1 + color: "red" + width: 100; height: 100 + } + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + \endqml + \row + \o \image declarative-item_stacking3.png + \o Same \c z - children above parents: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \row + \o \image declarative-item_stacking4.png + \o Lower \c z below: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + z: -1 + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \endtable + */ + +/*! + \qmlproperty bool QtQuick2::Item::visible + + This property holds whether the item is visible. By default this is true. + + Setting this property directly affects the \c visible value of child + items. When set to \c false, the \c visible values of all child items also + become \c false. When set to \c true, the \c visible values of child items + are returned to \c true, unless they have explicitly been set to \c false. + + (Because of this flow-on behavior, using the \c visible property may not + have the intended effect if a property binding should only respond to + explicit property changes. In such cases it may be better to use the + \l opacity property instead.) + + Setting this property to \c false automatically causes \l focus to be set + to \c false, and this item will longer receive mouse and keyboard events. + (In contrast, setting the \l opacity to 0 does not affect the \l focus + property and the receiving of key events.) + + \note This property's value is only affected by changes to this property or + the parent's \c visible property. It does not change, for example, if this + item moves off-screen, or if the \l opacity changes to 0. +*/ + + +/*! + \qmlproperty AnchorLine QtQuick2::Item::anchors.top + \qmlproperty AnchorLine QtQuick2::Item::anchors.bottom + \qmlproperty AnchorLine QtQuick2::Item::anchors.left + \qmlproperty AnchorLine QtQuick2::Item::anchors.right + \qmlproperty AnchorLine QtQuick2::Item::anchors.horizontalCenter + \qmlproperty AnchorLine QtQuick2::Item::anchors.verticalCenter + \qmlproperty AnchorLine QtQuick2::Item::anchors.baseline + + \qmlproperty Item QtQuick2::Item::anchors.fill + \qmlproperty Item QtQuick2::Item::anchors.centerIn + + \qmlproperty real QtQuick2::Item::anchors.margins + \qmlproperty real QtQuick2::Item::anchors.topMargin + \qmlproperty real QtQuick2::Item::anchors.bottomMargin + \qmlproperty real QtQuick2::Item::anchors.leftMargin + \qmlproperty real QtQuick2::Item::anchors.rightMargin + \qmlproperty real QtQuick2::Item::anchors.horizontalCenterOffset + \qmlproperty real QtQuick2::Item::anchors.verticalCenterOffset + \qmlproperty real QtQuick2::Item::anchors.baselineOffset + + \qmlproperty bool QtQuick2::Item::anchors.mirrored + + Anchors provide a way to position an item by specifying its + relationship with other items. + + Margins apply to top, bottom, left, right, and fill anchors. + The \c anchors.margins property can be used to set all of the various margins at once, to the same value. + Note that margins are anchor-specific and are not applied if an item does not + use anchors. + + Offsets apply for horizontal center, vertical center, and baseline anchors. + + \table + \row + \o \image declarative-anchors_example.png + \o Text anchored to Image, horizontally centered and vertically below, with a margin. + \qml + Item { + Image { + id: pic + // ... + } + Text { + id: label + anchors.horizontalCenter: pic.horizontalCenter + anchors.top: pic.bottom + anchors.topMargin: 5 + // ... + } + } + \endqml + \row + \o \image declarative-anchors_example2.png + \o + Left of Text anchored to right of Image, with a margin. The y + property of both defaults to 0. + + \qml + Item { + Image { + id: pic + // ... + } + Text { + id: label + anchors.left: pic.right + anchors.leftMargin: 5 + // ... + } + } + \endqml + \endtable + + \c anchors.fill provides a convenient way for one item to have the + same geometry as another item, and is equivalent to connecting all + four directional anchors. + + To clear an anchor value, set it to \c undefined. + + \c anchors.mirrored returns true it the layout has been \l {LayoutMirroring}{mirrored}. + + \note You can only anchor an item to siblings or a parent. + + For more information see \l {anchor-layout}{Anchor Layouts}. +*/ + +/*! + \property QQuickItem::baselineOffset + \brief The position of the item's baseline in local coordinates. + + The baseline of a \l Text item is the imaginary line on which the text + sits. Controls containing text usually set their baseline to the + baseline of their text. + + For non-text items, a default baseline offset of 0 is used. +*/ +QQuickAnchors *QQuickItemPrivate::anchors() const +{ + if (!_anchors) { + Q_Q(const QQuickItem); + _anchors = new QQuickAnchors(const_cast(q)); + if (!componentComplete) + _anchors->classBegin(); + } + return _anchors; +} + +QQuickItemPrivate::AnchorLines *QQuickItemPrivate::anchorLines() const +{ + Q_Q(const QQuickItem); + if (!_anchorLines) _anchorLines = + new AnchorLines(const_cast(q)); + return _anchorLines; +} + +void QQuickItemPrivate::siblingOrderChanged() +{ + Q_Q(QQuickItem); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::SiblingOrder) { + change.listener->itemSiblingOrderChanged(q); + } + } +} + +QDeclarativeListProperty QQuickItemPrivate::data() +{ + return QDeclarativeListProperty(q_func(), 0, QQuickItemPrivate::data_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + QQuickItemPrivate::data_clear); +} + +QRectF QQuickItem::childrenRect() +{ + Q_D(QQuickItem); + if (!d->_contents) { + d->_contents = new QQuickContents(this); + if (d->componentComplete) + d->_contents->complete(); + } + return d->_contents->rectF(); +} + +QList QQuickItem::childItems() const +{ + Q_D(const QQuickItem); + return d->childItems; +} + +bool QQuickItem::clip() const +{ + return flags() & ItemClipsChildrenToShape; +} + +void QQuickItem::setClip(bool c) +{ + if (clip() == c) + return; + + setFlag(ItemClipsChildrenToShape, c); + + emit clipChanged(c); +} + + +/*! + This function is called to handle this item's changes in + geometry from \a oldGeometry to \a newGeometry. If the two + geometries are the same, it doesn't do anything. + */ +void QQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickItem); + + if (d->_anchors) + QQuickAnchorsPrivate::get(d->_anchors)->updateMe(); + + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Geometry) + change.listener->itemGeometryChanged(this, newGeometry, oldGeometry); + } + + if (newGeometry.x() != oldGeometry.x()) + emit xChanged(); + if (newGeometry.y() != oldGeometry.y()) + emit yChanged(); + if (newGeometry.width() != oldGeometry.width()) + emit widthChanged(); + if (newGeometry.height() != oldGeometry.height()) + emit heightChanged(); +} + +/*! + Called by the rendering thread when it is time to sync the state of the QML objects with the + scene graph objects. The function should return the root of the scene graph subtree for + this item. \a oldNode is the node that was returned the last time the function was called. + + The main thread is blocked while this function is executed so it is safe to read + values from the QQuickItem instance and other objects in the main thread. + + \warning This is the only function in which it is allowed to make use of scene graph + objects from the main thread. Use of scene graph objects outside this function will + result in race conditions and potential crashes. + */ + +QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + delete oldNode; + return 0; +} + +QSGTransformNode *QQuickItemPrivate::createTransformNode() +{ + return new QSGTransformNode; +} + +void QQuickItem::updatePolish() +{ +} + +void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types) +{ + ChangeListener change(listener, types); + changeListeners.removeOne(change); +} + +void QQuickItem::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QQuickItem::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QQuickItem::inputMethodEvent(QInputMethodEvent *event) +{ + event->ignore(); +} + +void QQuickItem::focusInEvent(QFocusEvent *) +{ +} + +void QQuickItem::focusOutEvent(QFocusEvent *) +{ +} + +void QQuickItem::mousePressEvent(QMouseEvent *event) +{ + event->ignore(); +} + +void QQuickItem::mouseMoveEvent(QMouseEvent *event) +{ + event->ignore(); +} + +void QQuickItem::mouseReleaseEvent(QMouseEvent *event) +{ + event->ignore(); +} + +void QQuickItem::mouseDoubleClickEvent(QMouseEvent *event) +{ + mousePressEvent(event); +} + +void QQuickItem::mouseUngrabEvent() +{ + // XXX todo +} + +void QQuickItem::wheelEvent(QWheelEvent *event) +{ + event->ignore(); +} + +void QQuickItem::touchEvent(QTouchEvent *event) +{ + event->ignore(); +} + +void QQuickItem::hoverEnterEvent(QHoverEvent *event) +{ + Q_UNUSED(event); +} + +void QQuickItem::hoverMoveEvent(QHoverEvent *event) +{ + Q_UNUSED(event); +} + +void QQuickItem::hoverLeaveEvent(QHoverEvent *event) +{ + Q_UNUSED(event); +} + +void QQuickItem::dragEnterEvent(QDragEnterEvent *event) +{ + Q_UNUSED(event); +} + +void QQuickItem::dragMoveEvent(QDragMoveEvent *event) +{ + + Q_UNUSED(event); +} + +void QQuickItem::dragLeaveEvent(QDragLeaveEvent *event) +{ + + Q_UNUSED(event); +} + +void QQuickItem::dropEvent(QDropEvent *event) +{ + Q_UNUSED(event); +} + +bool QQuickItem::childMouseEventFilter(QQuickItem *, QEvent *) +{ + return false; +} + +void QQuickItem::windowDeactivateEvent() +{ + foreach (QQuickItem* item, childItems()) { + item->windowDeactivateEvent(); + } +} + +Qt::InputMethodHints QQuickItem::inputMethodHints() const +{ + Q_D(const QQuickItem); + return d->imHints; +} + +void QQuickItem::setInputMethodHints(Qt::InputMethodHints hints) +{ + Q_D(QQuickItem); + d->imHints = hints; + + if (!d->canvas || d->canvas->activeFocusItem() != this) + return; + + QInputPanel *p = qApp->inputPanel(); + if (p->inputItem() == this) + qApp->inputPanel()->update(Qt::ImHints); +} + +void QQuickItem::updateMicroFocus() +{ + QInputPanel *p = qApp->inputPanel(); + if (p->inputItem() == this) + qApp->inputPanel()->update(Qt::ImQueryInput); +} + +QVariant QQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QQuickItem); + QVariant v; + + switch (query) { + case Qt::ImEnabled: + v = (bool)(flags() & ItemAcceptsInputMethod); + break; + case Qt::ImHints: + v = (int)inputMethodHints(); + break; + case Qt::ImCursorRectangle: + case Qt::ImFont: + case Qt::ImCursorPosition: + case Qt::ImSurroundingText: + case Qt::ImCurrentSelection: + case Qt::ImMaximumTextLength: + case Qt::ImAnchorPosition: + case Qt::ImPreferredLanguage: + if (d->keyHandler) + v = d->keyHandler->inputMethodQuery(query); + default: + break; + } + + return v; +} + +QQuickAnchorLine QQuickItemPrivate::left() const +{ + return anchorLines()->left; +} + +QQuickAnchorLine QQuickItemPrivate::right() const +{ + return anchorLines()->right; +} + +QQuickAnchorLine QQuickItemPrivate::horizontalCenter() const +{ + return anchorLines()->hCenter; +} + +QQuickAnchorLine QQuickItemPrivate::top() const +{ + return anchorLines()->top; +} + +QQuickAnchorLine QQuickItemPrivate::bottom() const +{ + return anchorLines()->bottom; +} + +QQuickAnchorLine QQuickItemPrivate::verticalCenter() const +{ + return anchorLines()->vCenter; +} + +QQuickAnchorLine QQuickItemPrivate::baseline() const +{ + return anchorLines()->baseline; +} + +qreal QQuickItem::baselineOffset() const +{ + Q_D(const QQuickItem); + if (!d->baselineOffset.isValid()) { + return 0.0; + } else + return d->baselineOffset; +} + +void QQuickItem::setBaselineOffset(qreal offset) +{ + Q_D(QQuickItem); + if (offset == d->baselineOffset) + return; + + d->baselineOffset = offset; + + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Geometry) { + QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate(); + if (anchor) + anchor->updateVerticalAnchors(); + } + } + emit baselineOffsetChanged(offset); +} + +void QQuickItem::update() +{ + Q_D(QQuickItem); + Q_ASSERT(flags() & ItemHasContents); + d->dirty(QQuickItemPrivate::Content); +} + +void QQuickItem::polish() +{ + Q_D(QQuickItem); + if (!d->polishScheduled) { + d->polishScheduled = true; + if (d->canvas) { + QQuickCanvasPrivate *p = QQuickCanvasPrivate::get(d->canvas); + bool maybeupdate = p->itemsToPolish.isEmpty(); + p->itemsToPolish.insert(this); + if (maybeupdate) d->canvas->maybeUpdate(); + } + } +} + +void QQuickItem::mapFromItem(QDeclarativeV8Function *args) const +{ + if (args->Length() != 0) { + v8::Local item = (*args)[0]; + QV8Engine *engine = args->engine(); + + QQuickItem *itemObj = 0; + if (!item->IsNull()) + itemObj = qobject_cast(engine->toQObject(item)); + + if (!itemObj && !item->IsNull()) { + qmlInfo(this) << "mapFromItem() given argument \"" << engine->toString(item->ToString()) + << "\" which is neither null nor an Item"; + return; + } + + v8::Local rv = v8::Object::New(); + args->returnValue(rv); + + qreal x = (args->Length() > 1)?(*args)[1]->NumberValue():0; + qreal y = (args->Length() > 2)?(*args)[2]->NumberValue():0; + + QPointF p = mapFromItem(itemObj, QPointF(x, y)); + + rv->Set(v8::String::New("x"), v8::Number::New(p.x())); + rv->Set(v8::String::New("y"), v8::Number::New(p.y())); + } +} + +QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const +{ + Q_D(const QQuickItem); + + // XXX todo - we need to be able to handle common parents better and detect + // invalid cases + if (ok) *ok = true; + + QTransform t = d->itemToCanvasTransform(); + if (other) t *= QQuickItemPrivate::get(other)->canvasToItemTransform(); + + return t; +} + +void QQuickItem::mapToItem(QDeclarativeV8Function *args) const +{ + if (args->Length() != 0) { + v8::Local item = (*args)[0]; + QV8Engine *engine = args->engine(); + + QQuickItem *itemObj = 0; + if (!item->IsNull()) + itemObj = qobject_cast(engine->toQObject(item)); + + if (!itemObj && !item->IsNull()) { + qmlInfo(this) << "mapToItem() given argument \"" << engine->toString(item->ToString()) + << "\" which is neither null nor an Item"; + return; + } + + v8::Local rv = v8::Object::New(); + args->returnValue(rv); + + qreal x = (args->Length() > 1)?(*args)[1]->NumberValue():0; + qreal y = (args->Length() > 2)?(*args)[2]->NumberValue():0; + + QPointF p = mapToItem(itemObj, QPointF(x, y)); + + rv->Set(v8::String::New("x"), v8::Number::New(p.x())); + rv->Set(v8::String::New("y"), v8::Number::New(p.y())); + } +} + +void QQuickItem::forceActiveFocus() +{ + setFocus(true); + QQuickItem *parent = parentItem(); + while (parent) { + if (parent->flags() & QQuickItem::ItemIsFocusScope) { + parent->setFocus(true); + } + parent = parent->parentItem(); + } +} + +QQuickItem *QQuickItem::childAt(qreal x, qreal y) const +{ + // XXX todo - should this include transform etc.? + const QList children = childItems(); + for (int i = children.count()-1; i >= 0; --i) { + QQuickItem *child = children.at(i); + if (child->isVisible() && child->x() <= x + && child->x() + child->width() >= x + && child->y() <= y + && child->y() + child->height() >= y) + return child; + } + return 0; +} + +QDeclarativeListProperty QQuickItemPrivate::resources() +{ + return QDeclarativeListProperty(q_func(), 0, QQuickItemPrivate::resources_append, + QQuickItemPrivate::resources_count, + QQuickItemPrivate::resources_at, + QQuickItemPrivate::resources_clear); +} + +QDeclarativeListProperty QQuickItemPrivate::children() +{ + return QDeclarativeListProperty(q_func(), 0, QQuickItemPrivate::children_append, + QQuickItemPrivate::children_count, + QQuickItemPrivate::children_at, + QQuickItemPrivate::children_clear); + +} + +QDeclarativeListProperty QQuickItemPrivate::states() +{ + return _states()->statesProperty(); +} + +QDeclarativeListProperty QQuickItemPrivate::transitions() +{ + return _states()->transitionsProperty(); +} + +QString QQuickItemPrivate::state() const +{ + if (!_stateGroup) + return QString(); + else + return _stateGroup->state(); +} + +void QQuickItemPrivate::setState(const QString &state) +{ + _states()->setState(state); +} + +QString QQuickItem::state() const +{ + Q_D(const QQuickItem); + return d->state(); +} + +void QQuickItem::setState(const QString &state) +{ + Q_D(QQuickItem); + d->setState(state); +} + +QDeclarativeListProperty QQuickItem::transform() +{ + Q_D(QQuickItem); + return QDeclarativeListProperty(this, 0, d->transform_append, d->transform_count, + d->transform_at, d->transform_clear); +} + +void QQuickItem::classBegin() +{ + Q_D(QQuickItem); + d->componentComplete = false; + if (d->_stateGroup) + d->_stateGroup->classBegin(); + if (d->_anchors) + d->_anchors->classBegin(); +} + +void QQuickItem::componentComplete() +{ + Q_D(QQuickItem); + d->componentComplete = true; + if (d->_stateGroup) + d->_stateGroup->componentComplete(); + if (d->_anchors) { + d->_anchors->componentComplete(); + QQuickAnchorsPrivate::get(d->_anchors)->updateOnComplete(); + } + if (d->keyHandler) + d->keyHandler->componentComplete(); + if (d->_contents) + d->_contents->complete(); +} + +QDeclarativeStateGroup *QQuickItemPrivate::_states() +{ + Q_Q(QQuickItem); + if (!_stateGroup) { + _stateGroup = new QDeclarativeStateGroup; + if (!componentComplete) + _stateGroup->classBegin(); + FAST_CONNECT(_stateGroup, SIGNAL(stateChanged(QString)), + q, SIGNAL(stateChanged(QString))) + } + + return _stateGroup; +} + +QQuickItemPrivate::AnchorLines::AnchorLines(QQuickItem *q) +{ + left.item = q; + left.anchorLine = QQuickAnchorLine::Left; + right.item = q; + right.anchorLine = QQuickAnchorLine::Right; + hCenter.item = q; + hCenter.anchorLine = QQuickAnchorLine::HCenter; + top.item = q; + top.anchorLine = QQuickAnchorLine::Top; + bottom.item = q; + bottom.anchorLine = QQuickAnchorLine::Bottom; + vCenter.item = q; + vCenter.anchorLine = QQuickAnchorLine::VCenter; + baseline.item = q; + baseline.anchorLine = QQuickAnchorLine::Baseline; +} + +QPointF QQuickItemPrivate::computeTransformOrigin() const +{ + switch (origin) { + default: + case QQuickItem::TopLeft: + return QPointF(0, 0); + case QQuickItem::Top: + return QPointF(width / 2., 0); + case QQuickItem::TopRight: + return QPointF(width, 0); + case QQuickItem::Left: + return QPointF(0, height / 2.); + case QQuickItem::Center: + return QPointF(width / 2., height / 2.); + case QQuickItem::Right: + return QPointF(width, height / 2.); + case QQuickItem::BottomLeft: + return QPointF(0, height); + case QQuickItem::Bottom: + return QPointF(width / 2., height); + case QQuickItem::BottomRight: + return QPointF(width, height); + } +} + +void QQuickItemPrivate::transformChanged() +{ +} + +void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e) +{ + Q_Q(QQuickItem); + + Q_ASSERT(e->isAccepted()); + if (keyHandler) { + if (e->type() == QEvent::KeyPress) + keyHandler->keyPressed(e, false); + else + keyHandler->keyReleased(e, false); + + if (e->isAccepted()) + return; + else + e->accept(); + } + + if (e->type() == QEvent::KeyPress) + q->keyPressEvent(e); + else + q->keyReleaseEvent(e); + + if (e->isAccepted()) + return; + + if (keyHandler) { + e->accept(); + + if (e->type() == QEvent::KeyPress) + keyHandler->keyPressed(e, true); + else + keyHandler->keyReleased(e, true); + } +} + +void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e) +{ + Q_Q(QQuickItem); + + Q_ASSERT(e->isAccepted()); + if (keyHandler) { + keyHandler->inputMethodEvent(e, false); + + if (e->isAccepted()) + return; + else + e->accept(); + } + + q->inputMethodEvent(e); + + if (e->isAccepted()) + return; + + if (keyHandler) { + e->accept(); + + keyHandler->inputMethodEvent(e, true); + } +} + +void QQuickItemPrivate::deliverFocusEvent(QFocusEvent *e) +{ + Q_Q(QQuickItem); + + if (e->type() == QEvent::FocusIn) { + q->focusInEvent(e); + } else { + q->focusOutEvent(e); + } +} + +void QQuickItemPrivate::deliverMouseEvent(QMouseEvent *e) +{ + Q_Q(QQuickItem); + + Q_ASSERT(e->isAccepted()); + + switch (e->type()) { + default: + Q_ASSERT(!"Unknown event type"); + case QEvent::MouseMove: + q->mouseMoveEvent(e); + break; + case QEvent::MouseButtonPress: + q->mousePressEvent(e); + break; + case QEvent::MouseButtonRelease: + q->mouseReleaseEvent(e); + break; + case QEvent::MouseButtonDblClick: + q->mouseDoubleClickEvent(e); + break; + } +} + +void QQuickItemPrivate::deliverWheelEvent(QWheelEvent *e) +{ + Q_Q(QQuickItem); + q->wheelEvent(e); +} + +void QQuickItemPrivate::deliverTouchEvent(QTouchEvent *e) +{ + Q_Q(QQuickItem); + q->touchEvent(e); +} + +void QQuickItemPrivate::deliverHoverEvent(QHoverEvent *e) +{ + Q_Q(QQuickItem); + switch (e->type()) { + default: + Q_ASSERT(!"Unknown event type"); + case QEvent::HoverEnter: + q->hoverEnterEvent(e); + break; + case QEvent::HoverLeave: + q->hoverLeaveEvent(e); + break; + case QEvent::HoverMove: + q->hoverMoveEvent(e); + break; + } +} + +void QQuickItemPrivate::deliverDragEvent(QEvent *e) +{ + Q_Q(QQuickItem); + switch (e->type()) { + default: + Q_ASSERT(!"Unknown event type"); + case QEvent::DragEnter: + q->dragEnterEvent(static_cast(e)); + break; + case QEvent::DragLeave: + q->dragLeaveEvent(static_cast(e)); + break; + case QEvent::DragMove: + q->dragMoveEvent(static_cast(e)); + break; + case QEvent::Drop: + q->dropEvent(static_cast(e)); + break; + } +} + +void QQuickItem::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_UNUSED(change); + Q_UNUSED(value); +} + +/*! \internal */ +// XXX todo - do we want/need this anymore? +// Note that it's now used for varying clip rect +QRectF QQuickItem::boundingRect() const +{ + Q_D(const QQuickItem); + return QRectF(0, 0, d->width, d->height); +} + +QQuickItem::TransformOrigin QQuickItem::transformOrigin() const +{ + Q_D(const QQuickItem); + return d->origin; +} + +void QQuickItem::setTransformOrigin(TransformOrigin origin) +{ + Q_D(QQuickItem); + if (origin == d->origin) + return; + + d->origin = origin; + d->dirty(QQuickItemPrivate::TransformOrigin); + + emit transformOriginChanged(d->origin); +} + +QPointF QQuickItem::transformOriginPoint() const +{ + Q_D(const QQuickItem); + if (!d->transformOriginPoint.isNull()) + return d->transformOriginPoint; + return d->computeTransformOrigin(); +} + +void QQuickItem::setTransformOriginPoint(const QPointF &point) +{ + Q_D(QQuickItem); + if (d->transformOriginPoint == point) + return; + + d->transformOriginPoint = point; + d->dirty(QQuickItemPrivate::TransformOrigin); +} + +qreal QQuickItem::z() const +{ + Q_D(const QQuickItem); + return d->z; +} + +void QQuickItem::setZ(qreal v) +{ + Q_D(QQuickItem); + if (d->z == v) + return; + + d->z = v; + + d->dirty(QQuickItemPrivate::ZValue); + if (d->parentItem) + QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged); + + emit zChanged(); +} + + +/*! + \qmlproperty real QtQuick2::Item::rotation + This property holds the rotation of the item in degrees clockwise. + + This specifies how many degrees to rotate the item around its transformOrigin. + The default rotation is 0 degrees (i.e. not rotated at all). + + \table + \row + \o \image declarative-rotation.png + \o + \qml + Rectangle { + color: "blue" + width: 100; height: 100 + Rectangle { + color: "red" + x: 25; y: 25; width: 50; height: 50 + rotation: 30 + } + } + \endqml + \endtable + + \sa transform, Rotation +*/ + +/*! + \qmlproperty real QtQuick2::Item::scale + This property holds the scale of the item. + + A scale of less than 1 means the item will be displayed smaller than + normal, and a scale of greater than 1 means the item will be + displayed larger than normal. A negative scale means the item will + be mirrored. + + By default, items are displayed at a scale of 1 (i.e. at their + normal size). + + Scaling is from the item's transformOrigin. + + \table + \row + \o \image declarative-scale.png + \o + \qml + Rectangle { + color: "blue" + width: 100; height: 100 + Rectangle { + color: "green" + width: 25; height: 25 + } + Rectangle { + color: "red" + x: 25; y: 25; width: 50; height: 50 + scale: 1.4 + } + } + \endqml + \endtable + + \sa transform, Scale +*/ + +/*! + \qmlproperty real QtQuick2::Item::opacity + + This property holds the opacity of the item. Opacity is specified as a + number between 0 (fully transparent) and 1 (fully opaque). The default is 1. + + When this property is set, the specified opacity is also applied + individually to child items. In almost all cases this is what you want, + but in some cases it may produce undesired results. For example in the + second set of rectangles below, the red rectangle has specified an opacity + of 0.5, which affects the opacity of its blue child rectangle even though + the child has not specified an opacity. + + \table + \row + \o \image declarative-item_opacity1.png + \o + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \row + \o \image declarative-item_opacity2.png + \o + \qml + Item { + Rectangle { + opacity: 0.5 + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \endtable + + If an item's opacity is set to 0, the item will no longer receive mouse + events, but will continue to receive key events and will retain the keyboard + \l focus if it has been set. (In contrast, setting the \l visible property + to \c false stops both mouse and keyboard events, and also removes focus + from the item.) +*/ + +/*! + Returns a value indicating whether mouse input should + remain with this item exclusively. + + \sa setKeepMouseGrab() + */ + +qreal QQuickItem::rotation() const +{ + Q_D(const QQuickItem); + return d->rotation; +} + +void QQuickItem::setRotation(qreal r) +{ + Q_D(QQuickItem); + if (d->rotation == r) + return; + + d->rotation = r; + + d->dirty(QQuickItemPrivate::BasicTransform); + + d->itemChange(ItemRotationHasChanged, r); + + emit rotationChanged(); +} + +qreal QQuickItem::scale() const +{ + Q_D(const QQuickItem); + return d->scale; +} + +void QQuickItem::setScale(qreal s) +{ + Q_D(QQuickItem); + if (d->scale == s) + return; + + d->scale = s; + + d->dirty(QQuickItemPrivate::BasicTransform); + + emit scaleChanged(); +} + +qreal QQuickItem::opacity() const +{ + Q_D(const QQuickItem); + return d->opacity; +} + +void QQuickItem::setOpacity(qreal o) +{ + Q_D(QQuickItem); + if (d->opacity == o) + return; + + d->opacity = o; + + d->dirty(QQuickItemPrivate::OpacityValue); + + d->itemChange(ItemOpacityHasChanged, o); + + emit opacityChanged(); +} + +bool QQuickItem::isVisible() const +{ + Q_D(const QQuickItem); + return d->effectiveVisible; +} + +void QQuickItem::setVisible(bool v) +{ + Q_D(QQuickItem); + if (v == d->explicitVisible) + return; + + d->explicitVisible = v; + + d->setEffectiveVisibleRecur(d->calcEffectiveVisible()); +} + +bool QQuickItem::isEnabled() const +{ + Q_D(const QQuickItem); + return d->effectiveEnable; +} + +void QQuickItem::setEnabled(bool e) +{ + Q_D(QQuickItem); + if (e == d->explicitEnable) + return; + + d->explicitEnable = e; + + d->setEffectiveEnableRecur(d->calcEffectiveEnable()); +} + +bool QQuickItemPrivate::calcEffectiveVisible() const +{ + // XXX todo - Should the effective visible of an element with no parent just be the current + // effective visible? This would prevent pointless re-processing in the case of an element + // moving to/from a no-parent situation, but it is different from what graphics view does. + return explicitVisible && (!parentItem || QQuickItemPrivate::get(parentItem)->effectiveVisible); +} + +void QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) +{ + Q_Q(QQuickItem); + + if (newEffectiveVisible && !explicitVisible) { + // This item locally overrides visibility + return; + } + + if (newEffectiveVisible == effectiveVisible) { + // No change necessary + return; + } + + effectiveVisible = newEffectiveVisible; + dirty(Visible); + if (parentItem) QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); + + if (canvas) { + QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(canvas); + if (canvasPriv->mouseGrabberItem == q) + q->ungrabMouse(); + } + + for (int ii = 0; ii < childItems.count(); ++ii) + QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible); + + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Visibility) + change.listener->itemVisibilityChanged(q); + } + + emit q->visibleChanged(); +} + +bool QQuickItemPrivate::calcEffectiveEnable() const +{ + // XXX todo - Should the effective enable of an element with no parent just be the current + // effective enable? This would prevent pointless re-processing in the case of an element + // moving to/from a no-parent situation, but it is different from what graphics view does. + return explicitEnable && (!parentItem || QQuickItemPrivate::get(parentItem)->effectiveEnable); +} + +void QQuickItemPrivate::setEffectiveEnableRecur(bool newEffectiveEnable) +{ + Q_Q(QQuickItem); + + // XXX todo - need to fixup focus + + if (newEffectiveEnable && !explicitEnable) { + // This item locally overrides enable + return; + } + + if (newEffectiveEnable == effectiveEnable) { + // No change necessary + return; + } + + effectiveEnable = newEffectiveEnable; + + if (canvas) { + QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(canvas); + if (canvasPriv->mouseGrabberItem == q) + q->ungrabMouse(); + } + + for (int ii = 0; ii < childItems.count(); ++ii) + QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(newEffectiveEnable); + + emit q->enabledChanged(); +} + +QString QQuickItemPrivate::dirtyToString() const +{ +#define DIRTY_TO_STRING(value) if (dirtyAttributes & value) { \ + if (!rv.isEmpty()) \ + rv.append(QLatin1String("|")); \ + rv.append(QLatin1String(#value)); \ +} + +// QString rv = QLatin1String("0x") + QString::number(dirtyAttributes, 16); + QString rv; + + DIRTY_TO_STRING(TransformOrigin); + DIRTY_TO_STRING(Transform); + DIRTY_TO_STRING(BasicTransform); + DIRTY_TO_STRING(Position); + DIRTY_TO_STRING(Size); + DIRTY_TO_STRING(ZValue); + DIRTY_TO_STRING(Content); + DIRTY_TO_STRING(Smooth); + DIRTY_TO_STRING(OpacityValue); + DIRTY_TO_STRING(ChildrenChanged); + DIRTY_TO_STRING(ChildrenStackingChanged); + DIRTY_TO_STRING(ParentChanged); + DIRTY_TO_STRING(Clip); + DIRTY_TO_STRING(Canvas); + DIRTY_TO_STRING(EffectReference); + DIRTY_TO_STRING(Visible); + DIRTY_TO_STRING(HideReference); + + return rv; +} + +void QQuickItemPrivate::dirty(DirtyType type) +{ + Q_Q(QQuickItem); + if (type & (TransformOrigin | Transform | BasicTransform | Position | Size)) + transformChanged(); + + if (!(dirtyAttributes & type) || (canvas && !prevDirtyItem)) { + dirtyAttributes |= type; + if (canvas) { + addToDirtyList(); + QQuickCanvasPrivate::get(canvas)->dirtyItem(q); + } + } +} + +void QQuickItemPrivate::addToDirtyList() +{ + Q_Q(QQuickItem); + + Q_ASSERT(canvas); + if (!prevDirtyItem) { + Q_ASSERT(!nextDirtyItem); + + QQuickCanvasPrivate *p = QQuickCanvasPrivate::get(canvas); + nextDirtyItem = p->dirtyItemList; + if (nextDirtyItem) QQuickItemPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem; + prevDirtyItem = &p->dirtyItemList; + p->dirtyItemList = q; + p->dirtyItem(q); + } + Q_ASSERT(prevDirtyItem); +} + +void QQuickItemPrivate::removeFromDirtyList() +{ + if (prevDirtyItem) { + if (nextDirtyItem) QQuickItemPrivate::get(nextDirtyItem)->prevDirtyItem = prevDirtyItem; + *prevDirtyItem = nextDirtyItem; + prevDirtyItem = 0; + nextDirtyItem = 0; + } + Q_ASSERT(!prevDirtyItem); + Q_ASSERT(!nextDirtyItem); +} + +void QQuickItemPrivate::refFromEffectItem(bool hide) +{ + ++effectRefCount; + if (1 == effectRefCount) { + dirty(EffectReference); + if (parentItem) QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); + } + if (hide) { + if (++hideRefCount == 1) + dirty(HideReference); + } +} + +void QQuickItemPrivate::derefFromEffectItem(bool unhide) +{ + Q_ASSERT(effectRefCount); + --effectRefCount; + if (0 == effectRefCount) { + dirty(EffectReference); + if (parentItem) QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); + } + if (unhide) { + if (--hideRefCount == 0) + dirty(HideReference); + } +} + +void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) +{ + Q_Q(QQuickItem); + switch (change) { + case QQuickItem::ItemChildAddedChange: + q->itemChange(change, data); + if (_contents && componentComplete) + _contents->childAdded(data.item); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Children) { + change.listener->itemChildAdded(q, data.item); + } + } + break; + case QQuickItem::ItemChildRemovedChange: + q->itemChange(change, data); + if (_contents && componentComplete) + _contents->childRemoved(data.item); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Children) { + change.listener->itemChildRemoved(q, data.item); + } + } + break; + case QQuickItem::ItemSceneChange: + q->itemChange(change, data); + break; + case QQuickItem::ItemVisibleHasChanged: + q->itemChange(change, data); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Visibility) { + change.listener->itemVisibilityChanged(q); + } + } + break; + case QQuickItem::ItemParentHasChanged: + q->itemChange(change, data); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Parent) { + change.listener->itemParentChanged(q, data.item); + } + } + break; + case QQuickItem::ItemOpacityHasChanged: + q->itemChange(change, data); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Opacity) { + change.listener->itemOpacityChanged(q); + } + } + break; + case QQuickItem::ItemActiveFocusHasChanged: + q->itemChange(change, data); + break; + case QQuickItem::ItemRotationHasChanged: + q->itemChange(change, data); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::Rotation) { + change.listener->itemRotationChanged(q); + } + } + break; + } +} + +/*! + \property QQuickItem::smooth + \brief whether the item is smoothly transformed. + + This property is provided purely for the purpose of optimization. Turning + smooth transforms off is faster, but looks worse; turning smooth + transformations on is slower, but looks better. + + By default smooth transformations are off. +*/ + +/*! + Returns true if the item should be drawn with antialiasing and + smooth pixmap filtering, false otherwise. + + The default is false. + + \sa setSmooth() +*/ +bool QQuickItem::smooth() const +{ + Q_D(const QQuickItem); + return d->smooth; +} + +/*! + Sets whether the item should be drawn with antialiasing and + smooth pixmap filtering to \a smooth. + + \sa smooth() +*/ +void QQuickItem::setSmooth(bool smooth) +{ + Q_D(QQuickItem); + if (d->smooth == smooth) + return; + + d->smooth = smooth; + d->dirty(QQuickItemPrivate::Smooth); + + emit smoothChanged(smooth); +} + +QQuickItem::Flags QQuickItem::flags() const +{ + Q_D(const QQuickItem); + return (QQuickItem::Flags)d->flags; +} + +void QQuickItem::setFlag(Flag flag, bool enabled) +{ + Q_D(QQuickItem); + if (enabled) + setFlags((Flags)(d->flags | (quint32)flag)); + else + setFlags((Flags)(d->flags & ~(quint32)flag)); +} + +void QQuickItem::setFlags(Flags flags) +{ + Q_D(QQuickItem); + + if ((flags & ItemIsFocusScope) != (d->flags & ItemIsFocusScope)) { + if (flags & ItemIsFocusScope && !d->childItems.isEmpty() && d->canvas) { + qWarning("QQuickItem: Cannot set FocusScope once item has children and is in a canvas."); + flags &= ~ItemIsFocusScope; + } else if (d->flags & ItemIsFocusScope) { + qWarning("QQuickItem: Cannot unset FocusScope flag."); + flags |= ItemIsFocusScope; + } + } + + if ((flags & ItemClipsChildrenToShape ) != (d->flags & ItemClipsChildrenToShape)) + d->dirty(QQuickItemPrivate::Clip); + + d->flags = flags; +} + +qreal QQuickItem::x() const +{ + Q_D(const QQuickItem); + return d->x; +} + +qreal QQuickItem::y() const +{ + Q_D(const QQuickItem); + return d->y; +} + +QPointF QQuickItem::pos() const +{ + Q_D(const QQuickItem); + return QPointF(d->x, d->y); +} + +void QQuickItem::setX(qreal v) +{ + Q_D(QQuickItem); + if (d->x == v) + return; + + qreal oldx = d->x; + d->x = v; + + d->dirty(QQuickItemPrivate::Position); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(oldx, y(), width(), height())); +} + +void QQuickItem::setY(qreal v) +{ + Q_D(QQuickItem); + if (d->y == v) + return; + + qreal oldy = d->y; + d->y = v; + + d->dirty(QQuickItemPrivate::Position); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), oldy, width(), height())); +} + +void QQuickItem::setPos(const QPointF &pos) +{ + Q_D(QQuickItem); + if (QPointF(d->x, d->y) == pos) + return; + + qreal oldx = d->x; + qreal oldy = d->y; + + d->x = pos.x(); + d->y = pos.y(); + + d->dirty(QQuickItemPrivate::Position); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(oldx, oldy, width(), height())); +} + +qreal QQuickItem::width() const +{ + Q_D(const QQuickItem); + return d->width; +} + +void QQuickItem::setWidth(qreal w) +{ + Q_D(QQuickItem); + if (qIsNaN(w)) + return; + + d->widthValid = true; + if (d->width == w) + return; + + qreal oldWidth = d->width; + d->width = w; + + d->dirty(QQuickItemPrivate::Size); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, height())); +} + +void QQuickItem::resetWidth() +{ + Q_D(QQuickItem); + d->widthValid = false; + setImplicitWidth(implicitWidth()); +} + +void QQuickItemPrivate::implicitWidthChanged() +{ + Q_Q(QQuickItem); + emit q->implicitWidthChanged(); +} + +qreal QQuickItemPrivate::getImplicitWidth() const +{ + return implicitWidth; +} +/*! + Returns the width of the item that is implied by other properties that determine the content. +*/ +qreal QQuickItem::implicitWidth() const +{ + Q_D(const QQuickItem); + return d->getImplicitWidth(); +} + +/*! + \qmlproperty real QtQuick2::Item::implicitWidth + \qmlproperty real QtQuick2::Item::implicitHeight + + Defines the natural width or height of the Item if no \l width or \l height is specified. + + The default implicit size for most items is 0x0, however some elements have an inherent + implicit size which cannot be overridden, e.g. Image, Text. + + Setting the implicit size is useful for defining components that have a preferred size + based on their content, for example: + + \qml + // Label.qml + import QtQuick 1.1 + + Item { + property alias icon: image.source + property alias label: text.text + implicitWidth: text.implicitWidth + image.implicitWidth + implicitHeight: Math.max(text.implicitHeight, image.implicitHeight) + Image { id: image } + Text { + id: text + wrapMode: Text.Wrap + anchors.left: image.right; anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + } + \endqml + + \bold Note: using implicitWidth of Text or TextEdit and setting the width explicitly + incurs a performance penalty as the text must be laid out twice. +*/ + +/*! + Sets the implied width of the item to \a w. + This is the width implied by other properties that determine the content. +*/ +void QQuickItem::setImplicitWidth(qreal w) +{ + Q_D(QQuickItem); + bool changed = w != d->implicitWidth; + d->implicitWidth = w; + if (d->width == w || widthValid()) { + if (changed) + d->implicitWidthChanged(); + return; + } + + qreal oldWidth = d->width; + d->width = w; + + d->dirty(QQuickItemPrivate::Size); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, height())); + + if (changed) + d->implicitWidthChanged(); +} + +/*! + Returns whether the width property has been set explicitly. +*/ +bool QQuickItem::widthValid() const +{ + Q_D(const QQuickItem); + return d->widthValid; +} + +qreal QQuickItem::height() const +{ + Q_D(const QQuickItem); + return d->height; +} + +void QQuickItem::setHeight(qreal h) +{ + Q_D(QQuickItem); + if (qIsNaN(h)) + return; + + d->heightValid = true; + if (d->height == h) + return; + + qreal oldHeight = d->height; + d->height = h; + + d->dirty(QQuickItemPrivate::Size); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), width(), oldHeight)); +} + +void QQuickItem::resetHeight() +{ + Q_D(QQuickItem); + d->heightValid = false; + setImplicitHeight(implicitHeight()); +} + +void QQuickItemPrivate::implicitHeightChanged() +{ + Q_Q(QQuickItem); + emit q->implicitHeightChanged(); +} + +qreal QQuickItemPrivate::getImplicitHeight() const +{ + return implicitHeight; +} + +/*! + Returns the height of the item that is implied by other properties that determine the content. +*/ +qreal QQuickItem::implicitHeight() const +{ + Q_D(const QQuickItem); + return d->getImplicitHeight(); +} + + +/*! + Sets the implied height of the item to \a h. + This is the height implied by other properties that determine the content. +*/ +void QQuickItem::setImplicitHeight(qreal h) +{ + Q_D(QQuickItem); + bool changed = h != d->implicitHeight; + d->implicitHeight = h; + if (d->height == h || heightValid()) { + if (changed) + d->implicitHeightChanged(); + return; + } + + qreal oldHeight = d->height; + d->height = h; + + d->dirty(QQuickItemPrivate::Size); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), width(), oldHeight)); + + if (changed) + d->implicitHeightChanged(); +} + +/*! + Returns whether the height property has been set explicitly. +*/ +bool QQuickItem::heightValid() const +{ + Q_D(const QQuickItem); + return d->heightValid; +} + +void QQuickItem::setSize(const QSizeF &size) +{ + Q_D(QQuickItem); + d->heightValid = true; + d->widthValid = true; + + if (QSizeF(d->width, d->height) == size) + return; + + qreal oldHeight = d->height; + qreal oldWidth = d->width; + d->height = size.height(); + d->width = size.width(); + + d->dirty(QQuickItemPrivate::Size); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, oldHeight)); +} + +bool QQuickItem::hasActiveFocus() const +{ + Q_D(const QQuickItem); + return d->activeFocus; +} + +bool QQuickItem::hasFocus() const +{ + Q_D(const QQuickItem); + return d->focus; +} + +void QQuickItem::setFocus(bool focus) +{ + Q_D(QQuickItem); + if (d->focus == focus) + return; + + if (d->canvas) { + // Need to find our nearest focus scope + QQuickItem *scope = parentItem(); + while (scope && !scope->isFocusScope()) + scope = scope->parentItem(); + if (focus) + QQuickCanvasPrivate::get(d->canvas)->setFocusInScope(scope, this); + else + QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scope, this); + } else { + d->focus = focus; + emit focusChanged(focus); + } +} + +bool QQuickItem::isFocusScope() const +{ + return flags() & ItemIsFocusScope; +} + +QQuickItem *QQuickItem::scopedFocusItem() const +{ + Q_D(const QQuickItem); + if (!isFocusScope()) + return 0; + else + return d->subFocusItem; +} + + +Qt::MouseButtons QQuickItem::acceptedMouseButtons() const +{ + Q_D(const QQuickItem); + return d->acceptedMouseButtons; +} + +void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons) +{ + Q_D(QQuickItem); + d->acceptedMouseButtons = buttons; +} + +bool QQuickItem::filtersChildMouseEvents() const +{ + Q_D(const QQuickItem); + return d->filtersChildMouseEvents; +} + +void QQuickItem::setFiltersChildMouseEvents(bool filter) +{ + Q_D(QQuickItem); + d->filtersChildMouseEvents = filter; +} + +bool QQuickItem::isUnderMouse() const +{ + Q_D(const QQuickItem); + if (!d->canvas) + return false; + + QPoint cursorPos = QCursor::pos(); + if (QRectF(0, 0, width(), height()).contains(mapFromScene(cursorPos))) // ### refactor: d->canvas->mapFromGlobal(cursorPos)))) + return true; + return false; +} + +bool QQuickItem::acceptHoverEvents() const +{ + Q_D(const QQuickItem); + return d->hoverEnabled; +} + +void QQuickItem::setAcceptHoverEvents(bool enabled) +{ + Q_D(QQuickItem); + d->hoverEnabled = enabled; +} + +void QQuickItem::grabMouse() +{ + Q_D(QQuickItem); + if (!d->canvas) + return; + QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(d->canvas); + if (canvasPriv->mouseGrabberItem == this) + return; + + QQuickItem *oldGrabber = canvasPriv->mouseGrabberItem; + canvasPriv->mouseGrabberItem = this; + if (oldGrabber) + oldGrabber->mouseUngrabEvent(); +} + +void QQuickItem::ungrabMouse() +{ + Q_D(QQuickItem); + if (!d->canvas) + return; + QQuickCanvasPrivate *canvasPriv = QQuickCanvasPrivate::get(d->canvas); + if (canvasPriv->mouseGrabberItem != this) { + qWarning("QQuickItem::ungrabMouse(): Item is not the mouse grabber."); + return; + } + + canvasPriv->mouseGrabberItem = 0; + mouseUngrabEvent(); +} + +bool QQuickItem::keepMouseGrab() const +{ + Q_D(const QQuickItem); + return d->keepMouse; +} + +/*! + The flag indicating whether the mouse should remain + with this item is set to \a keep. + + This is useful for items that wish to grab and keep mouse + interaction following a predefined gesture. For example, + an item that is interested in horizontal mouse movement + may set keepMouseGrab to true once a threshold has been + exceeded. Once keepMouseGrab has been set to true, filtering + items will not react to mouse events. + + If the item does not indicate that it wishes to retain mouse grab, + a filtering item may steal the grab. For example, Flickable may attempt + to steal a mouse grab if it detects that the user has begun to + move the viewport. + + \sa keepMouseGrab() + */ +void QQuickItem::setKeepMouseGrab(bool keep) +{ + Q_D(QQuickItem); + d->keepMouse = keep; +} + +/*! + \qmlmethod object QtQuick2::Item::mapFromItem(Item item, real x, real y) + + Maps the point (\a x, \a y), which is in \a item's coordinate system, to + this item's coordinate system, and returns an object with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps the point from the coordinate + system of the root QML view. +*/ +/*! + \qmlmethod object QtQuick2::Item::mapToItem(Item item, real x, real y) + + Maps the point (\a x, \a y), which is in this item's coordinate system, to + \a item's coordinate system, and returns an object with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps \a x and \a y to the coordinate + system of the root QML view. +*/ +QPointF QQuickItem::mapToItem(const QQuickItem *item, const QPointF &point) const +{ + QPointF p = mapToScene(point); + if (item) + p = item->mapFromScene(p); + return p; +} + +QPointF QQuickItem::mapToScene(const QPointF &point) const +{ + Q_D(const QQuickItem); + return d->itemToCanvasTransform().map(point); +} + +QRectF QQuickItem::mapRectToItem(const QQuickItem *item, const QRectF &rect) const +{ + Q_D(const QQuickItem); + QTransform t = d->itemToCanvasTransform(); + if (item) + t *= QQuickItemPrivate::get(item)->canvasToItemTransform(); + return t.mapRect(rect); +} + +QRectF QQuickItem::mapRectToScene(const QRectF &rect) const +{ + Q_D(const QQuickItem); + return d->itemToCanvasTransform().mapRect(rect); +} + +QPointF QQuickItem::mapFromItem(const QQuickItem *item, const QPointF &point) const +{ + QPointF p = item?item->mapToScene(point):point; + return mapFromScene(p); +} + +QPointF QQuickItem::mapFromScene(const QPointF &point) const +{ + Q_D(const QQuickItem); + return d->canvasToItemTransform().map(point); +} + +QRectF QQuickItem::mapRectFromItem(const QQuickItem *item, const QRectF &rect) const +{ + Q_D(const QQuickItem); + QTransform t = item?QQuickItemPrivate::get(item)->itemToCanvasTransform():QTransform(); + t *= d->canvasToItemTransform(); + return t.mapRect(rect); +} + +QRectF QQuickItem::mapRectFromScene(const QRectF &rect) const +{ + Q_D(const QQuickItem); + return d->canvasToItemTransform().mapRect(rect); +} + + +/*! + \qmlmethod QtQuick2::Item::forceActiveFocus() + + Forces active focus on the item. + + This method sets focus on the item and makes sure that all the focus scopes + higher in the object hierarchy are also given the focus. +*/ + +/*! + Forces active focus on the item. + + This method sets focus on the item and makes sure that all the focus scopes + higher in the object hierarchy are also given the focus. +*/ + +/*! + \qmlmethod QtQuick2::Item::childAt(real x, real y) + + Returns the visible child item at point (\a x, \a y), which is in this + item's coordinate system, or \c null if there is no such item. +*/ + +/*! + Returns the visible child item at point (\a x, \a y), which is in this + item's coordinate system, or 0 if there is no such item. +*/ + +/*! + \qmlproperty list QtQuick2::Item::states + This property holds a list of states defined by the item. + + \qml + Item { + states: [ + State { + // ... + }, + State { + // ... + } + // ... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ +/*! + \qmlproperty list QtQuick2::Item::transitions + This property holds a list of transitions defined by the item. + + \qml + Item { + transitions: [ + Transition { + // ... + }, + Transition { + // ... + } + // ... + ] + } + \endqml + + \sa {QML Animation and Transitions}{Transitions} +*/ +/* + \qmlproperty list QtQuick2::Item::filter + This property holds a list of graphical filters to be applied to the item. + + \l {Filter}{Filters} include things like \l {Blur}{blurring} + the item, or giving it a \l Reflection. Some + filters may not be available on all canvases; if a filter is not + available on a certain canvas, it will simply not be applied for + that canvas (but the QML will still be considered valid). + + \qml + Item { + filter: [ + Blur { + // ... + }, + Reflection { + // ... + } + // ... + ] + } + \endqml +*/ + +/*! + \qmlproperty bool QtQuick2::Item::clip + This property holds whether clipping is enabled. The default clip value is \c false. + + If clipping is enabled, an item will clip its own painting, as well + as the painting of its children, to its bounding rectangle. + + Non-rectangular clipping regions are not supported for performance reasons. +*/ + +/*! + \property QQuickItem::clip + This property holds whether clipping is enabled. The default clip value is \c false. + + If clipping is enabled, an item will clip its own painting, as well + as the painting of its children, to its bounding rectangle. If you set + clipping during an item's paint operation, remember to re-set it to + prevent clipping the rest of your scene. + + Non-rectangular clipping regions are not supported for performance reasons. +*/ + +/*! + \qmlproperty string QtQuick2::Item::state + + This property holds the name of the current state of the item. + + This property is often used in scripts to change between states. For + example: + + \js + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + \endjs + + If the item is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return an + item to its base state by setting its current state to \c ''. + + \sa {qmlstates}{States} +*/ + +/*! + \qmlproperty list QtQuick2::Item::transform + This property holds the list of transformations to apply. + + For more information see \l Transform. +*/ + +/*! + \enum QQuickItem::TransformOrigin + + Controls the point about which simple transforms like scale apply. + + \value TopLeft The top-left corner of the item. + \value Top The center point of the top of the item. + \value TopRight The top-right corner of the item. + \value Left The left most point of the vertical middle. + \value Center The center of the item. + \value Right The right most point of the vertical middle. + \value BottomLeft The bottom-left corner of the item. + \value Bottom The center point of the bottom of the item. + \value BottomRight The bottom-right corner of the item. +*/ + + +/*! + \qmlproperty bool QtQuick2::Item::activeFocus + + This property indicates whether the item has active focus. + + An item with active focus will receive keyboard input, + or is a FocusScope ancestor of the item that will receive keyboard input. + + Usually, activeFocus is gained by setting focus on an item and its enclosing + FocusScopes. In the following example \c input will have activeFocus. + \qml + Rectangle { + FocusScope { + focus: true + TextInput { + id: input + focus: true + } + } + } + \endqml + + \sa focus, {qmlfocus}{Keyboard Focus} +*/ + +/*! + \qmlproperty bool QtQuick2::Item::focus + This property indicates whether the item has focus within the enclosing focus scope. If true, this item + will gain active focus when the enclosing focus scope gains active focus. + In the following example, \c input will be given active focus when \c scope gains active focus. + \qml + Rectangle { + FocusScope { + id: scope + TextInput { + id: input + focus: true + } + } + } + \endqml + + For the purposes of this property, the scene as a whole is assumed to act like a focus scope. + On a practical level, that means the following QML will give active focus to \c input on startup. + + \qml + Rectangle { + TextInput { + id: input + focus: true + } + } + \endqml + + \sa activeFocus, {qmlfocus}{Keyboard Focus} +*/ + + +/*! + \property QQuickItem::anchors + \internal +*/ + +/*! + \property QQuickItem::left + \internal +*/ + +/*! + \property QQuickItem::right + \internal +*/ + +/*! + \property QQuickItem::horizontalCenter + \internal +*/ + +/*! + \property QQuickItem::top + \internal +*/ + +/*! + \property QQuickItem::bottom + \internal +*/ + +/*! + \property QQuickItem::verticalCenter + \internal +*/ + +/*! + \property QQuickItem::focus + \internal +*/ + +/*! + \property QQuickItem::transform + \internal +*/ + +/*! + \property QQuickItem::transformOrigin + \internal +*/ + +/*! + \property QQuickItem::activeFocus + \internal +*/ + +/*! + \property QQuickItem::baseline + \internal +*/ + +/*! + \property QQuickItem::data + \internal +*/ + +/*! + \property QQuickItem::resources + \internal +*/ + +/*! + \property QQuickItem::state + \internal +*/ + +/*! + \property QQuickItem::states + \internal +*/ + +/*! + \property QQuickItem::transformOriginPoint + \internal +*/ + +/*! + \property QQuickItem::transitions + \internal +*/ + +bool QQuickItem::event(QEvent *ev) +{ +#if 0 + if (ev->type() == QEvent::PolishRequest) { + Q_D(QQuickItem); + d->polishScheduled = false; + updatePolish(); + return true; + } else { + return QObject::event(ev); + } +#endif + if (ev->type() == QEvent::InputMethodQuery) { + QInputMethodQueryEvent *query = static_cast(ev); + Qt::InputMethodQueries queries = query->queries(); + for (uint i = 0; i < 32; ++i) { + Qt::InputMethodQuery q = (Qt::InputMethodQuery)(int)(queries & (1<setValue(q, v); + } + } + query->accept(); + return true; + } else if (ev->type() == QEvent::InputMethod) { + inputMethodEvent(static_cast(ev)); + return true; + } + return QObject::event(ev); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, QQuickItem *item) +{ + if (!item) { + debug << "QQuickItem(0)"; + return debug; + } + + debug << item->metaObject()->className() << "(this =" << ((void*)item) + << ", name=" << item->objectName() + << ", parent =" << ((void*)item->parentItem()) + << ", geometry =" << QRectF(item->pos(), QSizeF(item->width(), item->height())) + << ", z =" << item->z() << ')'; + return debug; +} +#endif + +qint64 QQuickItemPrivate::consistentTime = -1; +void QQuickItemPrivate::setConsistentTime(qint64 t) +{ + consistentTime = t; +} + +class QElapsedTimerConsistentTimeHack +{ +public: + void start() { + t1 = QQuickItemPrivate::consistentTime; + t2 = 0; + } + qint64 elapsed() { + return QQuickItemPrivate::consistentTime - t1; + } + qint64 restart() { + qint64 val = QQuickItemPrivate::consistentTime - t1; + t1 = QQuickItemPrivate::consistentTime; + t2 = 0; + return val; + } + +private: + qint64 t1; + qint64 t2; +}; + +void QQuickItemPrivate::start(QElapsedTimer &t) +{ + if (QQuickItemPrivate::consistentTime == -1) + t.start(); + else + ((QElapsedTimerConsistentTimeHack*)&t)->start(); +} + +qint64 QQuickItemPrivate::elapsed(QElapsedTimer &t) +{ + if (QQuickItemPrivate::consistentTime == -1) + return t.elapsed(); + else + return ((QElapsedTimerConsistentTimeHack*)&t)->elapsed(); +} + +qint64 QQuickItemPrivate::restart(QElapsedTimer &t) +{ + if (QQuickItemPrivate::consistentTime == -1) + return t.restart(); + else + return ((QElapsedTimerConsistentTimeHack*)&t)->restart(); +} + +/*! + \fn bool QQuickItem::isTextureProvider() const + + Returns true if this item is a texture provider. The default + implementation returns false. + + This function can be called from any thread. + */ + +/*! + \fn QSGTextureProvider *QQuickItem::textureProvider() const + + Returns the texture provider for an item. The default implementation + returns 0. + + This function may only be called on the rendering thread. + */ + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/items/qquickitem.h b/src/declarative/items/qquickitem.h new file mode 100644 index 0000000000..be50677bf1 --- /dev/null +++ b/src/declarative/items/qquickitem.h @@ -0,0 +1,414 @@ +// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEM_H +#define QQUICKITEM_H + +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickItem; +class QQuickTransformPrivate; +class QQuickTransform : public QObject +{ + Q_OBJECT +public: + QQuickTransform(QObject *parent = 0); + ~QQuickTransform(); + + void appendToItem(QQuickItem *); + void prependToItem(QQuickItem *); + + virtual void applyTo(QMatrix4x4 *matrix) const = 0; + +protected Q_SLOTS: + void update(); + +protected: + QQuickTransform(QQuickTransformPrivate &dd, QObject *parent); + +private: + Q_DECLARE_PRIVATE(QQuickTransform) +}; + +class QDeclarativeV8Function; +class QDeclarativeState; +class QQuickAnchorLine; +class QDeclarativeTransition; +class QQuickKeyEvent; +class QQuickAnchors; +class QQuickItemPrivate; +class QQuickCanvas; +class QSGEngine; +class QTouchEvent; +class QSGNode; +class QSGTransformNode; +class QSGTextureProvider; + +class Q_DECLARATIVE_EXPORT QQuickItem : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + + Q_PROPERTY(QQuickItem *parent READ parentItem WRITE setParentItem NOTIFY parentChanged DESIGNABLE false FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty data READ data DESIGNABLE false) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty resources READ resources DESIGNABLE false) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) + + Q_PROPERTY(QPointF pos READ pos FINAL) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged FINAL) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged FINAL) + Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY zChanged FINAL) + Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged RESET resetWidth FINAL) + Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged RESET resetHeight FINAL) + + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL) + + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty states READ states DESIGNABLE false) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QDeclarativeListProperty transitions READ transitions DESIGNABLE false) + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QRectF childrenRect READ childrenRect NOTIFY childrenRectChanged DESIGNABLE false FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchors * anchors READ anchors DESIGNABLE false CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchorLine left READ left CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchorLine right READ right CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchorLine horizontalCenter READ horizontalCenter CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchorLine top READ top CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchorLine bottom READ bottom CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchorLine verticalCenter READ verticalCenter CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickAnchorLine baseline READ baseline CONSTANT FINAL) + Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged) + + Q_PROPERTY(bool clip READ clip WRITE setClip NOTIFY clipChanged) + + Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL) + Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL) + + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) + Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) + Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin NOTIFY transformOriginChanged) + Q_PROPERTY(QPointF transformOriginPoint READ transformOriginPoint) // XXX todo - notify? + Q_PROPERTY(QDeclarativeListProperty transform READ transform DESIGNABLE false FINAL) + + Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged) + + Q_ENUMS(TransformOrigin) + Q_CLASSINFO("DefaultProperty", "data") + +public: + enum Flag { + ItemClipsChildrenToShape = 0x01, + ItemAcceptsInputMethod = 0x02, + ItemIsFocusScope = 0x04, + ItemHasContents = 0x08, + ItemAcceptsDrops = 0x10 + // Remember to increment the size of QQuickItemPrivate::flags + }; + Q_DECLARE_FLAGS(Flags, Flag) + + enum ItemChange { + ItemChildAddedChange, // value.item + ItemChildRemovedChange, // value.item + ItemSceneChange, // value.canvas + ItemVisibleHasChanged, // value.realValue + ItemParentHasChanged, // value.item + ItemOpacityHasChanged, // value.realValue + ItemActiveFocusHasChanged, // value.boolValue + ItemRotationHasChanged // value.realValue + }; + + union ItemChangeData { + ItemChangeData(QQuickItem *v) : item(v) {} + ItemChangeData(QQuickCanvas *v) : canvas(v) {} + ItemChangeData(qreal v) : realValue(v) {} + ItemChangeData(bool v) : boolValue(v) {} + + QQuickItem *item; + QQuickCanvas *canvas; + qreal realValue; + bool boolValue; + }; + + enum TransformOrigin { + TopLeft, Top, TopRight, + Left, Center, Right, + BottomLeft, Bottom, BottomRight + }; + + QQuickItem(QQuickItem *parent = 0); + virtual ~QQuickItem(); + + QSGEngine *sceneGraphEngine() const; + + QQuickCanvas *canvas() const; + QQuickItem *parentItem() const; + void setParentItem(QQuickItem *parent); + void stackBefore(const QQuickItem *); + void stackAfter(const QQuickItem *); + + QRectF childrenRect(); + QList childItems() const; + + bool clip() const; + void setClip(bool); + + QString state() const; + void setState(const QString &); + + qreal baselineOffset() const; + void setBaselineOffset(qreal); + + QDeclarativeListProperty transform(); + + qreal x() const; + qreal y() const; + QPointF pos() const; + void setX(qreal); + void setY(qreal); + void setPos(const QPointF &); + + qreal width() const; + void setWidth(qreal); + void resetWidth(); + qreal implicitWidth() const; + + qreal height() const; + void setHeight(qreal); + void resetHeight(); + qreal implicitHeight() const; + + void setSize(const QSizeF &size); + + TransformOrigin transformOrigin() const; + void setTransformOrigin(TransformOrigin); + QPointF transformOriginPoint() const; + void setTransformOriginPoint(const QPointF &); + + qreal z() const; + void setZ(qreal); + + qreal rotation() const; + void setRotation(qreal); + qreal scale() const; + void setScale(qreal); + + qreal opacity() const; + void setOpacity(qreal); + + bool isVisible() const; + void setVisible(bool); + + bool isEnabled() const; + void setEnabled(bool); + + bool smooth() const; + void setSmooth(bool); + + Flags flags() const; + void setFlag(Flag flag, bool enabled = true); + void setFlags(Flags flags); + + virtual QRectF boundingRect() const; + + bool hasActiveFocus() const; + bool hasFocus() const; + void setFocus(bool); + bool isFocusScope() const; + QQuickItem *scopedFocusItem() const; + + Qt::MouseButtons acceptedMouseButtons() const; + void setAcceptedMouseButtons(Qt::MouseButtons buttons); + bool acceptHoverEvents() const; + void setAcceptHoverEvents(bool enabled); + + bool isUnderMouse() const; + void grabMouse(); + void ungrabMouse(); + bool keepMouseGrab() const; + void setKeepMouseGrab(bool); + bool filtersChildMouseEvents() const; + void setFiltersChildMouseEvents(bool filter); + + QTransform itemTransform(QQuickItem *, bool *) const; + QPointF mapToItem(const QQuickItem *item, const QPointF &point) const; + QPointF mapToScene(const QPointF &point) const; + QRectF mapRectToItem(const QQuickItem *item, const QRectF &rect) const; + QRectF mapRectToScene(const QRectF &rect) const; + QPointF mapFromItem(const QQuickItem *item, const QPointF &point) const; + QPointF mapFromScene(const QPointF &point) const; + QRectF mapRectFromItem(const QQuickItem *item, const QRectF &rect) const; + QRectF mapRectFromScene(const QRectF &rect) const; + + void polish(); + + Q_INVOKABLE void mapFromItem(QDeclarativeV8Function*) const; + Q_INVOKABLE void mapToItem(QDeclarativeV8Function*) const; + Q_INVOKABLE void forceActiveFocus(); + Q_INVOKABLE QQuickItem *childAt(qreal x, qreal y) const; + + Qt::InputMethodHints inputMethodHints() const; + void setInputMethodHints(Qt::InputMethodHints hints); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + struct UpdatePaintNodeData { + QSGTransformNode *transformNode; + private: + friend class QQuickCanvasPrivate; + UpdatePaintNodeData(); + }; + + virtual bool isTextureProvider() const { return false; } + virtual QSGTextureProvider *textureProvider() const { return 0; } + +public Q_SLOTS: + void update(); + void updateMicroFocus(); + +Q_SIGNALS: + void childrenRectChanged(const QRectF &); + void baselineOffsetChanged(qreal); + void stateChanged(const QString &); + void focusChanged(bool); + void activeFocusChanged(bool); + void parentChanged(QQuickItem *); + void transformOriginChanged(TransformOrigin); + void smoothChanged(bool); + void clipChanged(bool); + + // XXX todo + void childrenChanged(); + void opacityChanged(); + void enabledChanged(); + void visibleChanged(); + void rotationChanged(); + void scaleChanged(); + + void xChanged(); + void yChanged(); + void widthChanged(); + void heightChanged(); + void zChanged(); + void implicitWidthChanged(); + void implicitHeightChanged(); + +protected: + virtual bool event(QEvent *); + + bool isComponentComplete() const; + virtual void itemChange(ItemChange, const ItemChangeData &); + + void setImplicitWidth(qreal); + bool widthValid() const; // ### better name? + void setImplicitHeight(qreal); + bool heightValid() const; // ### better name? + + virtual void classBegin(); + virtual void componentComplete(); + + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void inputMethodEvent(QInputMethodEvent *); + virtual void focusInEvent(QFocusEvent *); + virtual void focusOutEvent(QFocusEvent *); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mouseUngrabEvent(); // XXX todo - params? + virtual void wheelEvent(QWheelEvent *event); + virtual void touchEvent(QTouchEvent *event); + virtual void hoverEnterEvent(QHoverEvent *event); + virtual void hoverMoveEvent(QHoverEvent *event); + virtual void hoverLeaveEvent(QHoverEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *); + virtual void dragMoveEvent(QDragMoveEvent *); + virtual void dragLeaveEvent(QDragLeaveEvent *); + virtual void dropEvent(QDropEvent *); + virtual bool childMouseEventFilter(QQuickItem *, QEvent *); + virtual void windowDeactivateEvent(); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void updatePolish(); + +protected: + QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent = 0); + +private: + friend class QQuickCanvas; + friend class QQuickCanvasPrivate; + friend class QSGRenderer; + Q_DISABLE_COPY(QQuickItem) + Q_DECLARE_PRIVATE(QQuickItem) +}; + +// XXX todo +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItem::Flags) + +#ifndef QT_NO_DEBUG_STREAM +QDebug Q_DECLARATIVE_EXPORT operator<<(QDebug debug, QQuickItem *item); +#endif + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickItem) +QML_DECLARE_TYPE(QQuickTransform) + +QT_END_HEADER + +#endif // QQUICKITEM_H diff --git a/src/declarative/items/qquickitem_p.h b/src/declarative/items/qquickitem_p.h new file mode 100644 index 0000000000..94b195f2d8 --- /dev/null +++ b/src/declarative/items/qquickitem_p.h @@ -0,0 +1,716 @@ +// Commit: 5c783d0a9a912816813945387903857a314040b5 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEM_P_H +#define QQUICKITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" + +#include "qquickanchors_p.h" +#include "qquickanchors_p_p.h" +#include "qquickitemchangelistener_p.h" + +#include "qquickcanvas_p.h" + +#include +#include "qquickclipnode_p.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QQuickItemKeyFilter; +class QQuickLayoutMirroringAttached; + +//### merge into private? +class QQuickContents : public QObject, public QQuickItemChangeListener +{ + Q_OBJECT +public: + QQuickContents(QQuickItem *item); + ~QQuickContents(); + + QRectF rectF() const; + + void childRemoved(QQuickItem *item); + void childAdded(QQuickItem *item); + + void calcGeometry() { calcWidth(); calcHeight(); } + void complete(); + +Q_SIGNALS: + void rectChanged(QRectF); + +protected: + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void itemDestroyed(QQuickItem *item); + //void itemVisibilityChanged(QQuickItem *item) + +private: + void calcHeight(QQuickItem *changed = 0); + void calcWidth(QQuickItem *changed = 0); + + QQuickItem *m_item; + qreal m_x; + qreal m_y; + qreal m_width; + qreal m_height; +}; + +class QQuickTransformPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickTransform); +public: + static QQuickTransformPrivate* get(QQuickTransform *transform) { return transform->d_func(); } + + QQuickTransformPrivate(); + + QList items; +}; + +class Q_DECLARATIVE_EXPORT QQuickItemPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickItem) + +public: + static QQuickItemPrivate* get(QQuickItem *item) { return item->d_func(); } + static const QQuickItemPrivate* get(const QQuickItem *item) { return item->d_func(); } + + QQuickItemPrivate(); + void init(QQuickItem *parent); + + QDeclarativeListProperty data(); + QDeclarativeListProperty resources(); + QDeclarativeListProperty children(); + + QDeclarativeListProperty states(); + QDeclarativeListProperty transitions(); + + QString state() const; + void setState(const QString &); + + QQuickAnchorLine left() const; + QQuickAnchorLine right() const; + QQuickAnchorLine horizontalCenter() const; + QQuickAnchorLine top() const; + QQuickAnchorLine bottom() const; + QQuickAnchorLine verticalCenter() const; + QQuickAnchorLine baseline() const; + + // data property + static void data_append(QDeclarativeListProperty *, QObject *); + static int data_count(QDeclarativeListProperty *); + static QObject *data_at(QDeclarativeListProperty *, int); + static void data_clear(QDeclarativeListProperty *); + + // resources property + static QObject *resources_at(QDeclarativeListProperty *, int); + static void resources_append(QDeclarativeListProperty *, QObject *); + static int resources_count(QDeclarativeListProperty *); + static void resources_clear(QDeclarativeListProperty *); + + // children property + static void children_append(QDeclarativeListProperty *, QQuickItem *); + static int children_count(QDeclarativeListProperty *); + static QQuickItem *children_at(QDeclarativeListProperty *, int); + static void children_clear(QDeclarativeListProperty *); + + // transform property + static int transform_count(QDeclarativeListProperty *list); + static void transform_append(QDeclarativeListProperty *list, QQuickTransform *); + static QQuickTransform *transform_at(QDeclarativeListProperty *list, int); + static void transform_clear(QDeclarativeListProperty *list); + + QQuickAnchors *anchors() const; + mutable QQuickAnchors *_anchors; + QQuickContents *_contents; + + QDeclarativeNullableValue baselineOffset; + + struct AnchorLines { + AnchorLines(QQuickItem *); + QQuickAnchorLine left; + QQuickAnchorLine right; + QQuickAnchorLine hCenter; + QQuickAnchorLine top; + QQuickAnchorLine bottom; + QQuickAnchorLine vCenter; + QQuickAnchorLine baseline; + }; + mutable AnchorLines *_anchorLines; + AnchorLines *anchorLines() const; + + enum ChangeType { + Geometry = 0x01, + SiblingOrder = 0x02, + Visibility = 0x04, + Opacity = 0x08, + Destroyed = 0x10, + Parent = 0x20, + Children = 0x40, + Rotation = 0x80, + }; + + Q_DECLARE_FLAGS(ChangeTypes, ChangeType) + + struct ChangeListener { + ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::ChangeTypes t) : listener(l), types(t) {} + QQuickItemChangeListener *listener; + QQuickItemPrivate::ChangeTypes types; + bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; } + }; + + void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types) { + changeListeners.append(ChangeListener(listener, types)); + } + void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types); + QPODVector changeListeners; + + QDeclarativeStateGroup *_states(); + QDeclarativeStateGroup *_stateGroup; + + QQuickItem::TransformOrigin origin:5; + quint32 flags:5; + bool widthValid:1; + bool heightValid:1; + bool componentComplete:1; + bool keepMouse:1; + bool hoverEnabled:1; + bool smooth:1; + bool focus:1; + bool activeFocus:1; + bool notifiedFocus:1; + bool notifiedActiveFocus:1; + bool filtersChildMouseEvents:1; + bool explicitVisible:1; + bool effectiveVisible:1; + bool explicitEnable:1; + bool effectiveEnable:1; + bool polishScheduled:1; + bool inheritedLayoutMirror:1; + bool effectiveLayoutMirror:1; + bool isMirrorImplicit:1; + bool inheritMirrorFromParent:1; + bool inheritMirrorFromItem:1; + bool childrenDoNotOverlap:1; + quint32 dummy:1; + + QQuickCanvas *canvas; + QSGContext *sceneGraphContext() const { Q_ASSERT(canvas); return static_cast(QObjectPrivate::get(canvas))->context; } + + QQuickItem *parentItem; + QList childItems; + QList paintOrderChildItems() const; + void addChild(QQuickItem *); + void removeChild(QQuickItem *); + void siblingOrderChanged(); + + class InitializationState { + public: + QQuickItem *getFocusScope(QQuickItem *item); + void clear(); + void clear(QQuickItem *focusScope); + private: + QQuickItem *focusScope; + }; + void initCanvas(InitializationState *, QQuickCanvas *); + + QQuickItem *subFocusItem; + + QTransform canvasToItemTransform() const; + QTransform itemToCanvasTransform() const; + void itemToParentTransform(QTransform &) const; + + qreal x; + qreal y; + qreal width; + qreal height; + qreal implicitWidth; + qreal implicitHeight; + + qreal z; + qreal scale; + qreal rotation; + qreal opacity; + + QQuickLayoutMirroringAttached* attachedLayoutDirection; + + Qt::MouseButtons acceptedMouseButtons; + Qt::InputMethodHints imHints; + + QPointF transformOriginPoint; + + virtual qreal getImplicitWidth() const; + virtual qreal getImplicitHeight() const; + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); + + void resolveLayoutMirror(); + void setImplicitLayoutMirror(bool mirror, bool inherit); + void setLayoutMirror(bool mirror); + bool isMirrored() const { + return effectiveLayoutMirror; + } + + QPointF computeTransformOrigin() const; + QList transforms; + virtual void transformChanged(); + + QQuickItemKeyFilter *keyHandler; + void deliverKeyEvent(QKeyEvent *); + void deliverInputMethodEvent(QInputMethodEvent *); + void deliverFocusEvent(QFocusEvent *); + void deliverMouseEvent(QMouseEvent *); + void deliverWheelEvent(QWheelEvent *); + void deliverTouchEvent(QTouchEvent *); + void deliverHoverEvent(QHoverEvent *); + void deliverDragEvent(QEvent *); + + bool calcEffectiveVisible() const; + void setEffectiveVisibleRecur(bool); + bool calcEffectiveEnable() const; + void setEffectiveEnableRecur(bool); + + // XXX todo + enum DirtyType { + TransformOrigin = 0x00000001, + Transform = 0x00000002, + BasicTransform = 0x00000004, + Position = 0x00000008, + Size = 0x00000010, + + ZValue = 0x00000020, + Content = 0x00000040, + Smooth = 0x00000080, + OpacityValue = 0x00000100, + ChildrenChanged = 0x00000200, + ChildrenStackingChanged = 0x00000400, + ParentChanged = 0x00000800, + + Clip = 0x00001000, + Canvas = 0x00002000, + + EffectReference = 0x00008000, + Visible = 0x00010000, + HideReference = 0x00020000, + // When you add an attribute here, don't forget to update + // dirtyToString() + + TransformUpdateMask = TransformOrigin | Transform | BasicTransform | Position | Size | Canvas, + ComplexTransformUpdateMask = Transform | Canvas, + ContentUpdateMask = Size | Content | Smooth | Canvas, + ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Canvas, + + }; + quint32 dirtyAttributes; + QString dirtyToString() const; + void dirty(DirtyType); + void addToDirtyList(); + void removeFromDirtyList(); + QQuickItem *nextDirtyItem; + QQuickItem**prevDirtyItem; + + inline QSGTransformNode *itemNode(); + inline QSGNode *childContainerNode(); + + /* + QSGNode order is: + - itemNode + - (opacityNode) + - (clipNode) + - (effectNode) + - groupNode + */ + + QSGTransformNode *itemNodeInstance; + QSGOpacityNode *opacityNode; + QQuickDefaultClipNode *clipNode; + QSGRootNode *rootNode; + QSGNode *groupNode; + QSGNode *paintNode; + QSGNode *beforePaintNode; + + virtual QSGTransformNode *createTransformNode(); + + // A reference from an effect item means that this item is used by the effect, so + // it should insert a root node. + void refFromEffectItem(bool hide); + void derefFromEffectItem(bool unhide); + int effectRefCount; + int hideRefCount; + + void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &); + + virtual void mirrorChange() {} + + static qint64 consistentTime; + static void setConsistentTime(qint64 t); + static void start(QElapsedTimer &); + static qint64 elapsed(QElapsedTimer &); + static qint64 restart(QElapsedTimer &); +}; + +/* + Key filters can be installed on a QQuickItem, but not removed. Currently they + are only used by attached objects (which are only destroyed on Item + destruction), so this isn't a problem. If in future this becomes any form + of public API, they will have to support removal too. +*/ +class QQuickItemKeyFilter +{ +public: + QQuickItemKeyFilter(QQuickItem * = 0); + virtual ~QQuickItemKeyFilter(); + + virtual void keyPressed(QKeyEvent *event, bool post); + virtual void keyReleased(QKeyEvent *event, bool post); + virtual void inputMethodEvent(QInputMethodEvent *event, bool post); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + virtual void componentComplete(); + + bool m_processPost; + +private: + QQuickItemKeyFilter *m_next; +}; + +class QQuickKeyNavigationAttachedPrivate : public QObjectPrivate +{ +public: + QQuickKeyNavigationAttachedPrivate() + : QObjectPrivate(), + left(0), right(0), up(0), down(0), tab(0), backtab(0), + leftSet(false), rightSet(false), upSet(false), downSet(false), + tabSet(false), backtabSet(false) {} + + QQuickItem *left; + QQuickItem *right; + QQuickItem *up; + QQuickItem *down; + QQuickItem *tab; + QQuickItem *backtab; + bool leftSet : 1; + bool rightSet : 1; + bool upSet : 1; + bool downSet : 1; + bool tabSet : 1; + bool backtabSet : 1; +}; + +class QQuickKeyNavigationAttached : public QObject, public QQuickItemKeyFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickKeyNavigationAttached) + + Q_PROPERTY(QQuickItem *left READ left WRITE setLeft NOTIFY leftChanged) + Q_PROPERTY(QQuickItem *right READ right WRITE setRight NOTIFY rightChanged) + Q_PROPERTY(QQuickItem *up READ up WRITE setUp NOTIFY upChanged) + Q_PROPERTY(QQuickItem *down READ down WRITE setDown NOTIFY downChanged) + Q_PROPERTY(QQuickItem *tab READ tab WRITE setTab NOTIFY tabChanged) + Q_PROPERTY(QQuickItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged) + Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) + + Q_ENUMS(Priority) + +public: + QQuickKeyNavigationAttached(QObject * = 0); + + QQuickItem *left() const; + void setLeft(QQuickItem *); + QQuickItem *right() const; + void setRight(QQuickItem *); + QQuickItem *up() const; + void setUp(QQuickItem *); + QQuickItem *down() const; + void setDown(QQuickItem *); + QQuickItem *tab() const; + void setTab(QQuickItem *); + QQuickItem *backtab() const; + void setBacktab(QQuickItem *); + + enum Priority { BeforeItem, AfterItem }; + Priority priority() const; + void setPriority(Priority); + + static QQuickKeyNavigationAttached *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void leftChanged(); + void rightChanged(); + void upChanged(); + void downChanged(); + void tabChanged(); + void backtabChanged(); + void priorityChanged(); + +private: + virtual void keyPressed(QKeyEvent *event, bool post); + virtual void keyReleased(QKeyEvent *event, bool post); + void setFocusNavigation(QQuickItem *currentItem, const char *dir); +}; + +class QQuickLayoutMirroringAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged) + +public: + explicit QQuickLayoutMirroringAttached(QObject *parent = 0); + + bool enabled() const; + void setEnabled(bool); + void resetEnabled(); + + bool childrenInherit() const; + void setChildrenInherit(bool); + + static QQuickLayoutMirroringAttached *qmlAttachedProperties(QObject *); +Q_SIGNALS: + void enabledChanged(); + void childrenInheritChanged(); +private: + friend class QQuickItemPrivate; + QQuickItemPrivate *itemPrivate; +}; + +class QQuickKeysAttachedPrivate : public QObjectPrivate +{ +public: + QQuickKeysAttachedPrivate() + : QObjectPrivate(), inPress(false), inRelease(false) + , inIM(false), enabled(true), imeItem(0), item(0) + {} + + bool isConnected(const char *signalName); + + //loop detection + bool inPress:1; + bool inRelease:1; + bool inIM:1; + + bool enabled : 1; + + QQuickItem *imeItem; + QList targets; + QQuickItem *item; +}; + +class QQuickKeysAttached : public QObject, public QQuickItemKeyFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickKeysAttached) + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QDeclarativeListProperty forwardTo READ forwardTo) + Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) + + Q_ENUMS(Priority) + +public: + QQuickKeysAttached(QObject *parent=0); + ~QQuickKeysAttached(); + + bool enabled() const { Q_D(const QQuickKeysAttached); return d->enabled; } + void setEnabled(bool enabled) { + Q_D(QQuickKeysAttached); + if (enabled != d->enabled) { + d->enabled = enabled; + emit enabledChanged(); + } + } + + enum Priority { BeforeItem, AfterItem}; + Priority priority() const; + void setPriority(Priority); + + QDeclarativeListProperty forwardTo() { + Q_D(QQuickKeysAttached); + return QDeclarativeListProperty(this, d->targets); + } + + virtual void componentComplete(); + + static QQuickKeysAttached *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void enabledChanged(); + void priorityChanged(); + void pressed(QQuickKeyEvent *event); + void released(QQuickKeyEvent *event); + void digit0Pressed(QQuickKeyEvent *event); + void digit1Pressed(QQuickKeyEvent *event); + void digit2Pressed(QQuickKeyEvent *event); + void digit3Pressed(QQuickKeyEvent *event); + void digit4Pressed(QQuickKeyEvent *event); + void digit5Pressed(QQuickKeyEvent *event); + void digit6Pressed(QQuickKeyEvent *event); + void digit7Pressed(QQuickKeyEvent *event); + void digit8Pressed(QQuickKeyEvent *event); + void digit9Pressed(QQuickKeyEvent *event); + + void leftPressed(QQuickKeyEvent *event); + void rightPressed(QQuickKeyEvent *event); + void upPressed(QQuickKeyEvent *event); + void downPressed(QQuickKeyEvent *event); + void tabPressed(QQuickKeyEvent *event); + void backtabPressed(QQuickKeyEvent *event); + + void asteriskPressed(QQuickKeyEvent *event); + void numberSignPressed(QQuickKeyEvent *event); + void escapePressed(QQuickKeyEvent *event); + void returnPressed(QQuickKeyEvent *event); + void enterPressed(QQuickKeyEvent *event); + void deletePressed(QQuickKeyEvent *event); + void spacePressed(QQuickKeyEvent *event); + void backPressed(QQuickKeyEvent *event); + void cancelPressed(QQuickKeyEvent *event); + void selectPressed(QQuickKeyEvent *event); + void yesPressed(QQuickKeyEvent *event); + void noPressed(QQuickKeyEvent *event); + void context1Pressed(QQuickKeyEvent *event); + void context2Pressed(QQuickKeyEvent *event); + void context3Pressed(QQuickKeyEvent *event); + void context4Pressed(QQuickKeyEvent *event); + void callPressed(QQuickKeyEvent *event); + void hangupPressed(QQuickKeyEvent *event); + void flipPressed(QQuickKeyEvent *event); + void menuPressed(QQuickKeyEvent *event); + void volumeUpPressed(QQuickKeyEvent *event); + void volumeDownPressed(QQuickKeyEvent *event); + +private: + virtual void keyPressed(QKeyEvent *event, bool post); + virtual void keyReleased(QKeyEvent *event, bool post); + virtual void inputMethodEvent(QInputMethodEvent *, bool post); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + const QByteArray keyToSignal(int key) { + QByteArray keySignal; + if (key >= Qt::Key_0 && key <= Qt::Key_9) { + keySignal = "digit0Pressed"; + keySignal[5] = '0' + (key - Qt::Key_0); + } else { + int i = 0; + while (sigMap[i].key && sigMap[i].key != key) + ++i; + keySignal = sigMap[i].sig; + } + return keySignal; + } + + struct SigMap { + int key; + const char *sig; + }; + + static const SigMap sigMap[]; +}; + +QSGTransformNode *QQuickItemPrivate::itemNode() +{ + if (!itemNodeInstance) { + itemNodeInstance = createTransformNode(); + itemNodeInstance->setFlag(QSGNode::OwnedByParent, false); +#ifdef QML_RUNTIME_TESTING + Q_Q(QQuickItem); + itemNodeInstance->description = QString::fromLatin1("QQuickItem(%1)").arg(QString::fromLatin1(q->metaObject()->className())); +#endif + } + return itemNodeInstance; +} + +QSGNode *QQuickItemPrivate::childContainerNode() +{ + if (!groupNode) { + groupNode = new QSGNode(); + if (rootNode) + rootNode->appendChildNode(groupNode); + else if (clipNode) + clipNode->appendChildNode(groupNode); + else if (opacityNode) + opacityNode->appendChildNode(groupNode); + else + itemNode()->appendChildNode(groupNode); + groupNode->setFlag(QSGNode::ChildrenDoNotOverlap, childrenDoNotOverlap); +#ifdef QML_RUNTIME_TESTING + groupNode->description = QLatin1String("group"); +#endif + } + return groupNode; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItemPrivate::ChangeTypes); + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickKeysAttached) +QML_DECLARE_TYPEINFO(QQuickKeysAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQuickKeyNavigationAttached) +QML_DECLARE_TYPEINFO(QQuickKeyNavigationAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQuickLayoutMirroringAttached) +QML_DECLARE_TYPEINFO(QQuickLayoutMirroringAttached, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKITEM_P_H diff --git a/src/declarative/items/qquickitemchangelistener_p.h b/src/declarative/items/qquickitemchangelistener_p.h new file mode 100644 index 0000000000..a021658f9e --- /dev/null +++ b/src/declarative/items/qquickitemchangelistener_p.h @@ -0,0 +1,82 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEMCHANGELISTENER_P_H +#define QQUICKITEMCHANGELISTENER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QRectF; +class QQuickItem; +class QQuickAnchorsPrivate; +class QQuickItemChangeListener +{ +public: + virtual void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) {} + virtual void itemSiblingOrderChanged(QQuickItem *) {} + virtual void itemVisibilityChanged(QQuickItem *) {} + virtual void itemOpacityChanged(QQuickItem *) {} + virtual void itemDestroyed(QQuickItem *) {} + virtual void itemChildAdded(QQuickItem *, QQuickItem *) {} + virtual void itemChildRemoved(QQuickItem *, QQuickItem *) {} + virtual void itemParentChanged(QQuickItem *, QQuickItem *) {} + virtual void itemRotationChanged(QQuickItem *) {} + + virtual QQuickAnchorsPrivate *anchorPrivate() { return 0; } +}; + +QT_END_NAMESPACE + +#endif // QQUICKITEMCHANGELISTENER_P_H diff --git a/src/declarative/items/qquickitemsmodule.cpp b/src/declarative/items/qquickitemsmodule.cpp new file mode 100644 index 0000000000..38b5a91e56 --- /dev/null +++ b/src/declarative/items/qquickitemsmodule.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickitemsmodule_p.h" + +#include "qquickitem.h" +#include "qquickitem_p.h" +#include "qquickevents_p_p.h" +#include "qquickrectangle_p.h" +#include "qquickfocusscope_p.h" +#include "qquicktext_p.h" +#include "qquicktextinput_p.h" +#include "qquicktextedit_p.h" +#include "qquickimage_p.h" +#include "qquickborderimage_p.h" +#include "qquickscalegrid_p_p.h" +#include "qquickmousearea_p.h" +#include "qquickpincharea_p.h" +#include "qquickflickable_p.h" +#include "qquickflickable_p_p.h" +#include "qquicklistview_p.h" +#include "qquickvisualitemmodel_p.h" +#include "qquickvisualdatamodel_p.h" +#include "qquickgridview_p.h" +#include "qquickpathview_p.h" +#include +#include +#include "qquickpositioners_p.h" +#include "qquickrepeater_p.h" +#include "qquickloader_p.h" +#include "qquickanimatedimage_p.h" +#include "qquickflipable_p.h" +#include "qquicktranslate_p.h" +#include "qquickstateoperations_p.h" +#include "qquickanimation_p.h" +#include +#include +//#include +#include +#include +#include "qquicksprite_p.h" +#include "qquickspriteimage_p.h" +#include "qquickdrag_p.h" +#include "qquickdroparea_p.h" + +static QDeclarativePrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject *parent) +{ + QQuickItem *item = qobject_cast(obj); + if (!item) + return QDeclarativePrivate::IncompatibleObject; + + QQuickItem *parentItem = qobject_cast(parent); + if (!parentItem) + return QDeclarativePrivate::IncompatibleParent; + + item->setParentItem(parentItem); + return QDeclarativePrivate::Parented; +} + +static void qt_quickitems_defineModule(const char *uri, int major, int minor) +{ + QDeclarativePrivate::RegisterAutoParent autoparent = { 0, &qquickitem_autoParent }; + QDeclarativePrivate::qmlregister(QDeclarativePrivate::AutoParentRegistration, &autoparent); + +#ifdef QT_NO_MOVIE + qmlRegisterTypeNotAvailable(uri,major,minor,"AnimatedImage", qApp->translate("QQuickAnimatedImage","Qt was built without support for QMovie")); +#else + qmlRegisterType(uri,major,minor,"AnimatedImage"); +#endif + qmlRegisterType(uri,major,minor,"BorderImage"); + qmlRegisterType(uri,major,minor,"Column"); + qmlRegisterType(uri,major,minor,"Flickable"); + qmlRegisterType(uri,major,minor,"Flipable"); + qmlRegisterType(uri,major,minor,"Flow"); +// qmlRegisterType(uri,major,minor,"FocusPanel"); + qmlRegisterType(uri,major,minor,"FocusScope"); + qmlRegisterType(uri,major,minor,"Gradient"); + qmlRegisterType(uri,major,minor,"GradientStop"); + qmlRegisterType(uri,major,minor,"Grid"); + qmlRegisterType(uri,major,minor,"GridView"); + qmlRegisterType(uri,major,minor,"Image"); + qmlRegisterType(uri,major,minor,"Item"); + qmlRegisterType(uri,major,minor,"ListView"); + qmlRegisterType(uri,major,minor,"Loader"); + qmlRegisterType(uri,major,minor,"MouseArea"); + qmlRegisterType(uri,major,minor,"Path"); + qmlRegisterType(uri,major,minor,"PathAttribute"); + qmlRegisterType(uri,major,minor,"PathCubic"); + qmlRegisterType(uri,major,minor,"PathLine"); + qmlRegisterType(uri,major,minor,"PathPercent"); + qmlRegisterType(uri,major,minor,"PathQuad"); + qmlRegisterType("QtQuick",2,0,"PathCurve"); + qmlRegisterType("QtQuick",2,0,"PathArc"); + qmlRegisterType("QtQuick",2,0,"PathSvg"); + qmlRegisterType(uri,major,minor,"PathView"); + qmlRegisterUncreatableType(uri,major,minor,"Positioner", + QStringLiteral("Positioner is an abstract type that is only available as an attached property.")); +#ifndef QT_NO_VALIDATOR + qmlRegisterType(uri,major,minor,"IntValidator"); + qmlRegisterType(uri,major,minor,"DoubleValidator"); + qmlRegisterType(uri,major,minor,"RegExpValidator"); +#endif + qmlRegisterType(uri,major,minor,"Rectangle"); + qmlRegisterType(uri,major,minor,"Repeater"); + qmlRegisterType(uri,major,minor,"Row"); + qmlRegisterType(uri,major,minor,"Translate"); + qmlRegisterType(uri,major,minor,"Rotation"); + qmlRegisterType(uri,major,minor,"Scale"); + qmlRegisterType(uri,major,minor,"Text"); + qmlRegisterType(uri,major,minor,"TextEdit"); + qmlRegisterType(uri,major,minor,"TextInput"); + qmlRegisterType(uri,major,minor,"ViewSection"); + qmlRegisterType(uri,major,minor,"VisualDataModel"); + qmlRegisterType(uri,major,minor,"VisualDataGroup"); + qmlRegisterType(uri,major,minor,"VisualItemModel"); + + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); +#ifndef QT_NO_VALIDATOR + qmlRegisterType(); +#endif + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + qRegisterMetaType("QQuickAnchorLine"); + + qmlRegisterUncreatableType(uri,major,minor,"KeyNavigation",QQuickKeyNavigationAttached::tr("KeyNavigation is only available via attached properties")); + qmlRegisterUncreatableType(uri,major,minor,"Keys",QQuickKeysAttached::tr("Keys is only available via attached properties")); + qmlRegisterUncreatableType(uri,major,minor,"LayoutMirroring", QQuickLayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties")); + + qmlRegisterType(uri,major,minor,"PinchArea"); + qmlRegisterType(uri,major,minor,"Pinch"); + qmlRegisterType(); + + qmlRegisterType("QtQuick", 2, 0, "ShaderEffectItem"); // TODO: Remove after grace period. + qmlRegisterType("QtQuick", 2, 0, "ShaderEffect"); + qmlRegisterType("QtQuick", 2, 0, "ShaderEffectSource"); + qmlRegisterUncreatableType("QtQuick", 2, 0, "ShaderEffectMesh", QQuickShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh.")); + qmlRegisterType("QtQuick", 2, 0, "GridMesh"); + + qmlRegisterUncreatableType("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem")); + + qmlRegisterType("QtQuick", 2, 0, "Canvas"); + + qmlRegisterType("QtQuick", 2, 0, "Sprite"); + qmlRegisterType("QtQuick", 2, 0, "SpriteImage"); + + qmlRegisterType(uri, major, minor,"ParentChange"); + qmlRegisterType(uri, major, minor,"AnchorChanges"); + qmlRegisterType(); + qmlRegisterType(uri, major, minor,"AnchorAnimation"); + qmlRegisterType(uri, major, minor,"ParentAnimation"); + qmlRegisterType("QtQuick",2,0,"PathAnimation"); + qmlRegisterType("QtQuick",2,0,"PathInterpolator"); + + qmlRegisterType("QtQuick", 2, 0, "DropArea"); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterUncreatableType("QtQuick", 2, 0, "Drag", QQuickDragAttached::tr("Drag is only available via attached properties")); +} + +void QQuickItemsModule::defineModule() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; + + // XXX todo - Remove before final integration... + QByteArray mode = qgetenv("QMLSCENE_IMPORT_NAME"); + QByteArray name = "QtQuick"; + int majorVersion = 2; + int minorVersion = 0; + if (mode == "quick1") { + majorVersion = 1; + } else if (mode == "qt") { + name = "Qt"; + majorVersion = 4; + minorVersion = 7; + } + + qt_quickitems_defineModule(name, majorVersion, minorVersion); +} + diff --git a/src/declarative/items/qquickitemsmodule_p.h b/src/declarative/items/qquickitemsmodule_p.h new file mode 100644 index 0000000000..d3682007d7 --- /dev/null +++ b/src/declarative/items/qquickitemsmodule_p.h @@ -0,0 +1,65 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEMSMODULE_P_H +#define QQUICKITEMSMODULE_P_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickItemsModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKITEMSMODULE_P_H + diff --git a/src/declarative/items/qquickitemview.cpp b/src/declarative/items/qquickitemview.cpp new file mode 100644 index 0000000000..b3429ded7c --- /dev/null +++ b/src/declarative/items/qquickitemview.cpp @@ -0,0 +1,1676 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickitemview_p_p.h" + +QT_BEGIN_NAMESPACE + + +FxViewItem::FxViewItem(QQuickItem *i, bool own) + : item(i), ownItem(own), index(-1) +{ +} + +FxViewItem::~FxViewItem() +{ + if (ownItem && item) { + item->setParentItem(0); + item->deleteLater(); + item = 0; + } +} + + +QQuickItemViewChangeSet::QQuickItemViewChangeSet() + : active(false) +{ + reset(); +} + +bool QQuickItemViewChangeSet::hasPendingChanges() const +{ + return !pendingChanges.isEmpty(); +} + +void QQuickItemViewChangeSet::applyChanges(const QDeclarativeChangeSet &changeSet) +{ + pendingChanges.apply(changeSet); + + int moveId = -1; + int moveOffset; + + foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) { + itemCount -= r.count; + if (moveId == -1 && newCurrentIndex >= r.index + r.count) { + newCurrentIndex -= r.count; + currentChanged = true; + } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) { + // current item has been removed. + if (r.isMove()) { + moveId = r.moveId; + moveOffset = newCurrentIndex - r.index; + } else { + currentRemoved = true; + newCurrentIndex = -1; + if (itemCount) + newCurrentIndex = qMin(r.index, itemCount - 1); + } + currentChanged = true; + } + } + foreach (const QDeclarativeChangeSet::Insert &i, changeSet.inserts()) { + if (moveId == -1) { + if (itemCount && newCurrentIndex >= i.index) { + newCurrentIndex += i.count; + currentChanged = true; + } else if (newCurrentIndex < 0) { + newCurrentIndex = 0; + currentChanged = true; + } else if (newCurrentIndex == 0 && !itemCount) { + // this is the first item, set the initial current index + currentChanged = true; + } + } else if (moveId == i.moveId) { + newCurrentIndex = i.index + moveOffset; + } + itemCount += i.count; + } +} + +void QQuickItemViewChangeSet::prepare(int currentIndex, int count) +{ + if (active) + return; + reset(); + active = true; + itemCount = count; + newCurrentIndex = currentIndex; +} + +void QQuickItemViewChangeSet::reset() +{ + itemCount = 0; + newCurrentIndex = -1; + pendingChanges.clear(); + removedItems.clear(); + active = false; + currentChanged = false; + currentRemoved = false; +} + + +QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent) + : QQuickFlickable(dd, parent) +{ + Q_D(QQuickItemView); + d->init(); +} + +QQuickItemView::~QQuickItemView() +{ + Q_D(QQuickItemView); + d->clear(); + if (d->ownModel) + delete d->model; + delete d->header; + delete d->footer; +} + + +QQuickItem *QQuickItemView::currentItem() const +{ + Q_D(const QQuickItemView); + if (!d->currentItem) + return 0; + const_cast(d)->applyPendingChanges(); + return d->currentItem->item; +} + +QVariant QQuickItemView::model() const +{ + Q_D(const QQuickItemView); + return d->modelVariant; +} + +void QQuickItemView::setModel(const QVariant &model) +{ + Q_D(QQuickItemView); + if (d->modelVariant == model) + return; + if (d->model) { + disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + } + + QQuickVisualModel *oldModel = d->model; + + d->clear(); + d->setPosition(d->contentStartPosition()); + d->model = 0; + d->modelVariant = model; + + QObject *object = qvariant_cast(model); + QQuickVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete oldModel; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this), this); + d->ownModel = true; + if (isComponentComplete()) + static_cast(d->model.data())->componentComplete(); + } else { + d->model = oldModel; + } + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + + if (d->model) { + d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; + if (isComponentComplete()) { + updateSections(); + d->refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + } + d->updateViewport(); + } + connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + emit countChanged(); + } + emit modelChanged(); +} + +QDeclarativeComponent *QQuickItemView::delegate() const +{ + Q_D(const QQuickItemView); + if (d->model) { + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QQuickItemView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QQuickItemView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + if (isComponentComplete()) { + for (int i = 0; i < d->visibleItems.count(); ++i) + d->releaseItem(d->visibleItems.at(i)); + d->visibleItems.clear(); + d->releaseItem(d->currentItem); + d->currentItem = 0; + updateSections(); + d->refill(); + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + d->updateViewport(); + } + if (oldCount != dataModel->count()) + emit countChanged(); + } + emit delegateChanged(); +} + + +int QQuickItemView::count() const +{ + Q_D(const QQuickItemView); + if (!d->model) + return 0; + const_cast(d)->applyPendingChanges(); + return d->model->count(); +} + +int QQuickItemView::currentIndex() const +{ + Q_D(const QQuickItemView); + const_cast(d)->applyPendingChanges(); + return d->currentIndex; +} + +void QQuickItemView::setCurrentIndex(int index) +{ + Q_D(QQuickItemView); + if (d->requestedIndex >= 0) // currently creating item + return; + d->currentIndexCleared = (index == -1); + + d->applyPendingChanges(); + if (index == d->currentIndex) + return; + if (isComponentComplete() && d->isValid()) { + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->updateCurrent(index); + } else if (d->currentIndex != index) { + d->currentIndex = index; + emit currentIndexChanged(); + } +} + + +bool QQuickItemView::isWrapEnabled() const +{ + Q_D(const QQuickItemView); + return d->wrap; +} + +void QQuickItemView::setWrapEnabled(bool wrap) +{ + Q_D(QQuickItemView); + if (d->wrap == wrap) + return; + d->wrap = wrap; + emit keyNavigationWrapsChanged(); +} + +int QQuickItemView::cacheBuffer() const +{ + Q_D(const QQuickItemView); + return d->buffer; +} + +void QQuickItemView::setCacheBuffer(int b) +{ + Q_D(QQuickItemView); + if (d->buffer != b) { + d->buffer = b; + if (isComponentComplete()) { + d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; + d->refill(); + } + emit cacheBufferChanged(); + } +} + + +Qt::LayoutDirection QQuickItemView::layoutDirection() const +{ + Q_D(const QQuickItemView); + return d->layoutDirection; +} + +void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QQuickItemView); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + d->regenerate(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const +{ + Q_D(const QQuickItemView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} + + +QDeclarativeComponent *QQuickItemView::header() const +{ + Q_D(const QQuickItemView); + return d->headerComponent; +} + +QQuickItem *QQuickItemView::headerItem() const +{ + Q_D(const QQuickItemView); + const_cast(d)->applyPendingChanges(); + return d->header ? d->header->item : 0; +} + +void QQuickItemView::setHeader(QDeclarativeComponent *headerComponent) +{ + Q_D(QQuickItemView); + if (d->headerComponent != headerComponent) { + d->applyPendingChanges(); + delete d->header; + d->header = 0; + d->headerComponent = headerComponent; + + d->markExtentsDirty(); + + if (isComponentComplete()) { + d->updateHeader(); + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } else { + emit headerItemChanged(); + } + emit headerChanged(); + } +} + +QDeclarativeComponent *QQuickItemView::footer() const +{ + Q_D(const QQuickItemView); + return d->footerComponent; +} + +QQuickItem *QQuickItemView::footerItem() const +{ + Q_D(const QQuickItemView); + const_cast(d)->applyPendingChanges(); + return d->footer ? d->footer->item : 0; +} + +void QQuickItemView::setFooter(QDeclarativeComponent *footerComponent) +{ + Q_D(QQuickItemView); + if (d->footerComponent != footerComponent) { + d->applyPendingChanges(); + delete d->footer; + d->footer = 0; + d->footerComponent = footerComponent; + + if (isComponentComplete()) { + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } else { + emit footerItemChanged(); + } + emit footerChanged(); + } +} + +QDeclarativeComponent *QQuickItemView::highlight() const +{ + Q_D(const QQuickItemView); + const_cast(d)->applyPendingChanges(); + return d->highlightComponent; +} + +void QQuickItemView::setHighlight(QDeclarativeComponent *highlightComponent) +{ + Q_D(QQuickItemView); + if (highlightComponent != d->highlightComponent) { + d->applyPendingChanges(); + d->highlightComponent = highlightComponent; + d->createHighlight(); + if (d->currentItem) + d->updateHighlight(); + emit highlightChanged(); + } +} + +QQuickItem *QQuickItemView::highlightItem() const +{ + Q_D(const QQuickItemView); + const_cast(d)->applyPendingChanges(); + return d->highlight ? d->highlight->item : 0; +} + +bool QQuickItemView::highlightFollowsCurrentItem() const +{ + Q_D(const QQuickItemView); + return d->autoHighlight; +} + +void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QQuickItemView); + if (d->autoHighlight != autoHighlight) { + d->autoHighlight = autoHighlight; + if (autoHighlight) + d->updateHighlight(); + emit highlightFollowsCurrentItemChanged(); + } +} + +QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const +{ + Q_D(const QQuickItemView); + return static_cast(d->highlightRange); +} + +void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QQuickItemView); + if (d->highlightRange == mode) + return; + d->highlightRange = mode; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +//###Possibly rename these properties, since they are very useful even without a highlight? +qreal QQuickItemView::preferredHighlightBegin() const +{ + Q_D(const QQuickItemView); + return d->highlightRangeStart; +} + +void QQuickItemView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QQuickItemView); + d->highlightRangeStartValid = true; + if (d->highlightRangeStart == start) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightBeginChanged(); +} + +void QQuickItemView::resetPreferredHighlightBegin() +{ + Q_D(QQuickItemView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + +qreal QQuickItemView::preferredHighlightEnd() const +{ + Q_D(const QQuickItemView); + return d->highlightRangeEnd; +} + +void QQuickItemView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QQuickItemView); + d->highlightRangeEndValid = true; + if (d->highlightRangeEnd == end) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightEndChanged(); +} + +void QQuickItemView::resetPreferredHighlightEnd() +{ + Q_D(QQuickItemView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + +int QQuickItemView::highlightMoveDuration() const +{ + Q_D(const QQuickItemView); + return d->highlightMoveDuration; +} + +void QQuickItemView::setHighlightMoveDuration(int duration) +{ + Q_D(QQuickItemView); + if (d->highlightMoveDuration != duration) { + d->highlightMoveDuration = duration; + emit highlightMoveDurationChanged(); + } +} + +void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) +{ + Q_Q(QQuickItemView); + if (!isValid()) + return; + if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain) + return; + + applyPendingChanges(); + int idx = qMax(qMin(index, model->count()-1), 0); + + qreal pos = isContentFlowReversed() ? -position() - size() : position(); + FxViewItem *item = visibleItem(idx); + qreal maxExtent; + if (layoutOrientation() == Qt::Vertical) + maxExtent = -q->maxYExtent(); + else + maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent(); + if (!item) { + int itemPos = positionAt(idx); + changedVisibleIndex(idx); + // save the currently visible items in case any of them end up visible again + QList oldVisible = visibleItems; + visibleItems.clear(); + setPosition(qMin(qreal(itemPos), maxExtent)); + // now release the reference to all the old visible items. + for (int i = 0; i < oldVisible.count(); ++i) + releaseItem(oldVisible.at(i)); + item = visibleItem(idx); + } + if (item) { + const qreal itemPos = item->position(); + switch (mode) { + case QQuickItemView::Beginning: + pos = itemPos; + if (index < 0 && header) + pos -= headerSize(); + break; + case QQuickItemView::Center: + pos = itemPos - (size() - item->size())/2; + break; + case QQuickItemView::End: + pos = itemPos - size() + item->size(); + if (index >= model->count() && footer) + pos += footerSize(); + break; + case QQuickItemView::Visible: + if (itemPos > pos + size()) + pos = itemPos - size() + item->size(); + else if (item->endPosition() <= pos) + pos = itemPos; + break; + case QQuickItemView::Contain: + if (item->endPosition() >= pos + size()) + pos = itemPos - size() + item->size(); + if (itemPos < pos) + pos = itemPos; + } + pos = qMin(pos, maxExtent); + qreal minExtent; + if (layoutOrientation() == Qt::Vertical) + minExtent = -q->minYExtent(); + else + minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent(); + pos = qMax(pos, minExtent); + moveReason = QQuickItemViewPrivate::Other; + q->cancelFlick(); + setPosition(pos); + + if (highlight) { + if (autoHighlight) + resetHighlightPosition(); + updateHighlight(); + } + } + fixupPosition(); +} + +void QQuickItemView::positionViewAtIndex(int index, int mode) +{ + Q_D(QQuickItemView); + if (!d->isValid() || index < 0 || index >= d->model->count()) + return; + d->positionViewAtIndex(index, mode); +} + + +void QQuickItemView::positionViewAtBeginning() +{ + Q_D(QQuickItemView); + if (!d->isValid()) + return; + d->positionViewAtIndex(-1, Beginning); +} + +void QQuickItemView::positionViewAtEnd() +{ + Q_D(QQuickItemView); + if (!d->isValid()) + return; + d->positionViewAtIndex(d->model->count(), End); +} + +int QQuickItemView::indexAt(qreal x, qreal y) const +{ + Q_D(const QQuickItemView); + for (int i = 0; i < d->visibleItems.count(); ++i) { + const FxViewItem *item = d->visibleItems.at(i); + if (item->contains(x, y)) + return item->index; + } + + return -1; +} + +void QQuickItemViewPrivate::applyPendingChanges() +{ + Q_Q(QQuickItemView); + if (q->isComponentComplete() && currentChanges.hasPendingChanges()) + layout(); +} + +// for debugging only +void QQuickItemViewPrivate::checkVisible() const +{ + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == -1) { + ++skip; + } else if (item->index != visibleIndex + i - skip) { + qFatal("index %d %d %d", visibleIndex, i, item->index); + } + } +} + +void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QQuickItemView); + QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + + if (header && header->item == item) { + updateHeader(); + markExtentsDirty(); + if (!q->isMoving() && !q->isFlicking()) + fixupPosition(); + } else if (footer && footer->item == item) { + updateFooter(); + markExtentsDirty(); + if (!q->isMoving() && !q->isFlicking()) + fixupPosition(); + } + + if (currentItem && currentItem->item == item) + updateHighlight(); + if (trackedItem && trackedItem->item == item) + q->trackedPositionChanged(); +} + +void QQuickItemView::destroyRemoved() +{ + Q_D(QQuickItemView); + for (QList::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxViewItem *item = *it; + if (item->index == -1 && item->attached->delayRemove() == false) { + d->releaseItem(item); + it = d->visibleItems.erase(it); + } else { + ++it; + } + } + + // Correct the positioning of the items + d->updateSections(); + d->forceLayout = true; + d->layout(); +} + +void QQuickItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) +{ + Q_D(QQuickItemView); + if (reset) { + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->regenerate(); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + + emit countChanged(); + } else { + d->currentChanges.prepare(d->currentIndex, d->itemCount); + d->currentChanges.applyChanges(changeSet); + polish(); + } +} + +void QQuickItemView::createdItem(int index, QQuickItem *item) +{ + Q_D(QQuickItemView); + if (d->requestedIndex != index) { + item->setParentItem(contentItem()); + d->unrequestedItems.insert(item, index); + d->repositionPackageItemAt(item, index); + } +} + +void QQuickItemView::destroyingItem(QQuickItem *item) +{ + Q_D(QQuickItemView); + d->unrequestedItems.remove(item); +} + +void QQuickItemView::animStopped() +{ + Q_D(QQuickItemView); + d->bufferMode = QQuickItemViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange) + d->updateHighlight(); +} + + +void QQuickItemView::trackedPositionChanged() +{ + Q_D(QQuickItemView); + if (!d->trackedItem || !d->currentItem) + return; + if (d->moveReason == QQuickItemViewPrivate::SetIndex) { + qreal trackedPos = d->trackedItem->position(); + qreal trackedSize = d->trackedItem->size(); + if (d->trackedItem != d->currentItem) { + trackedSize += d->currentItem->sectionSize(); + } + qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position(); + qreal pos = viewPos; + if (d->haveHighlightRange) { + if (trackedPos > pos + d->highlightRangeEnd - trackedSize) + pos = trackedPos - d->highlightRangeEnd + trackedSize; + if (trackedPos < pos + d->highlightRangeStart) + pos = trackedPos - d->highlightRangeStart; + if (d->highlightRange != StrictlyEnforceRange) { + if (pos > d->endPosition() - d->size()) + pos = d->endPosition() - d->size(); + if (pos < d->startPosition()) + pos = d->startPosition(); + } + } else { + qreal trackedEndPos = d->trackedItem->endPosition(); + qreal toItemPos = d->currentItem->position(); + qreal toItemEndPos = d->currentItem->endPosition(); + + if (d->header && d->showHeaderForIndex(d->currentIndex)) { + trackedPos -= d->headerSize(); + trackedEndPos -= d->headerSize(); + toItemPos -= d->headerSize(); + toItemEndPos -= d->headerSize(); + } else if (d->footer && d->showFooterForIndex(d->currentIndex)) { + trackedPos += d->footerSize(); + trackedEndPos += d->footerSize(); + toItemPos += d->footerSize(); + toItemEndPos += d->footerSize(); + } + + if (trackedPos < viewPos && toItemPos < viewPos) { + pos = qMax(trackedPos, toItemPos); + } else if (trackedEndPos >= viewPos + d->size() + && toItemEndPos >= viewPos + d->size()) { + if (trackedEndPos <= toItemEndPos) { + pos = trackedEndPos - d->size(); + if (trackedSize > d->size()) + pos = trackedPos; + } else { + pos = toItemEndPos - d->size(); + if (d->currentItem->size() > d->size()) + pos = d->currentItem->position(); + } + } + } + if (viewPos != pos) { + cancelFlick(); + d->calcVelocity = true; + d->setPosition(pos); + d->calcVelocity = false; + } + } +} + +void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickItemView); + d->markExtentsDirty(); + QQuickFlickable::geometryChanged(newGeometry, oldGeometry); +} + + +qreal QQuickItemView::minYExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Horizontal) + return QQuickFlickable::minYExtent(); + + if (d->vData.minExtentDirty) { + d->minExtent = d->vData.startMargin-d->startPosition(); + if (d->header) + d->minExtent += d->headerSize(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += d->highlightRangeStart; + if (d->visibleItem(0)) + d->minExtent -= d->visibleItem(0)->sectionSize(); + d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd)); + } + d->vData.minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QQuickItemView::maxYExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Horizontal) + return height(); + + if (d->vData.maxExtentDirty) { + if (!d->model || !d->model->count()) { + d->maxExtent = d->header ? -d->headerSize() : 0; + d->maxExtent += height(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart); + if (d->highlightRangeEnd != d->highlightRangeStart) + d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd)); + } else { + d->maxExtent = -(d->endPosition() - height()); + } + + if (d->footer) + d->maxExtent -= d->footerSize(); + d->maxExtent -= d->vData.endMargin; + qreal minY = minYExtent(); + if (d->maxExtent > minY) + d->maxExtent = minY; + d->vData.maxExtentDirty = false; + } + return d->maxExtent; +} + +qreal QQuickItemView::minXExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Vertical) + return QQuickFlickable::minXExtent(); + + if (d->hData.minExtentDirty) { + d->minExtent = -d->startPosition(); + qreal highlightStart; + qreal highlightEnd; + qreal endPositionFirstItem = 0; + if (d->isContentFlowReversed()) { + d->minExtent += d->hData.endMargin; + if (d->model && d->model->count()) + endPositionFirstItem = d->positionAt(d->model->count()-1); + else if (d->header) + d->minExtent += d->headerSize(); + highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size(); + highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size(); + if (d->footer) + d->minExtent += d->footerSize(); + qreal maxX = maxXExtent(); + if (d->minExtent < maxX) + d->minExtent = maxX; + } else { + d->minExtent += d->hData.startMargin; + endPositionFirstItem = d->endPositionAt(0); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->header) + d->minExtent += d->headerSize(); + } + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += highlightStart; + d->minExtent = d->isContentFlowReversed() + ? qMin(d->minExtent, endPositionFirstItem + highlightEnd) + : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd)); + } + d->hData.minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QQuickItemView::maxXExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Vertical) + return width(); + + if (d->hData.maxExtentDirty) { + qreal highlightStart; + qreal highlightEnd; + qreal lastItemPosition = 0; + d->maxExtent = 0; + if (d->isContentFlowReversed()) { + highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size(); + highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size(); + lastItemPosition = d->endPosition(); + } else { + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->model && d->model->count()) + lastItemPosition = d->positionAt(d->model->count()-1); + } + if (!d->model || !d->model->count()) { + if (!d->isContentFlowReversed()) + d->maxExtent = d->header ? -d->headerSize() : 0; + d->maxExtent += width(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(lastItemPosition - highlightStart); + if (highlightEnd != highlightStart) { + d->maxExtent = d->isContentFlowReversed() + ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd)) + : qMin(d->maxExtent, -(d->endPosition() - highlightEnd)); + } + } else { + d->maxExtent = -(d->endPosition() - width()); + } + if (d->isContentFlowReversed()) { + if (d->header) + d->maxExtent -= d->headerSize(); + d->maxExtent -= d->hData.startMargin; + } else { + if (d->footer) + d->maxExtent -= d->footerSize(); + d->maxExtent -= d->hData.endMargin; + qreal minX = minXExtent(); + if (d->maxExtent > minX) + d->maxExtent = minX; + } + d->hData.maxExtentDirty = false; + } + + return d->maxExtent; +} + +void QQuickItemView::setContentX(qreal pos) +{ + Q_D(QQuickItemView); + // Positioning the view manually should override any current movement state + d->moveReason = QQuickItemViewPrivate::Other; + QQuickFlickable::setContentX(pos); +} + +void QQuickItemView::setContentY(qreal pos) +{ + Q_D(QQuickItemView); + // Positioning the view manually should override any current movement state + d->moveReason = QQuickItemViewPrivate::Other; + QQuickFlickable::setContentY(pos); +} + +qreal QQuickItemView::xOrigin() const +{ + Q_D(const QQuickItemView); + if (d->isContentFlowReversed()) + return -maxXExtent() + d->size() - d->hData.startMargin; + else + return -minXExtent() + d->hData.startMargin; +} + +void QQuickItemView::updatePolish() +{ + Q_D(QQuickItemView); + QQuickFlickable::updatePolish(); + d->layout(); +} + +void QQuickItemView::componentComplete() +{ + Q_D(QQuickItemView); + if (d->model && d->ownModel) + static_cast(d->model.data())->componentComplete(); + + QQuickFlickable::componentComplete(); + + updateSections(); + d->updateHeader(); + d->updateFooter(); + d->updateViewport(); + d->setPosition(d->contentStartPosition()); + if (d->isValid()) { + d->refill(); + d->moveReason = QQuickItemViewPrivate::SetIndex; + if (d->currentIndex < 0 && !d->currentIndexCleared) + d->updateCurrent(0); + else + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + d->fixupPosition(); + } + if (d->model && d->model->count()) + emit countChanged(); +} + + + +QQuickItemViewPrivate::QQuickItemViewPrivate() + : itemCount(0) + , buffer(0), bufferMode(BufferBefore | BufferAfter) + , layoutDirection(Qt::LeftToRight) + , moveReason(Other) + , visibleIndex(0) + , currentIndex(-1), currentItem(0) + , trackedItem(0), requestedIndex(-1) + , highlightComponent(0), highlight(0) + , highlightRange(QQuickItemView::NoHighlightRange) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightMoveDuration(150) + , headerComponent(0), header(0), footerComponent(0), footer(0) + , minExtent(0), maxExtent(0) + , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false) + , inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) + , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) +{ +} + +bool QQuickItemViewPrivate::isValid() const +{ + return model && model->count() && model->isValid(); +} + +qreal QQuickItemViewPrivate::position() const +{ + Q_Q(const QQuickItemView); + return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX(); +} + +qreal QQuickItemViewPrivate::size() const +{ + Q_Q(const QQuickItemView); + return layoutOrientation() == Qt::Vertical ? q->height() : q->width(); +} + +qreal QQuickItemViewPrivate::startPosition() const +{ + return isContentFlowReversed() ? -lastPosition() : originPosition(); +} + +qreal QQuickItemViewPrivate::endPosition() const +{ + return isContentFlowReversed() ? -originPosition() : lastPosition(); +} + +qreal QQuickItemViewPrivate::contentStartPosition() const +{ + Q_Q(const QQuickItemView); + qreal pos = -headerSize(); + if (layoutOrientation() == Qt::Vertical) + pos -= vData.startMargin; + else if (isContentFlowReversed()) + pos -= hData.endMargin; + else + pos -= hData.startMargin; + + return pos; +} + +int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const +{ + if (visibleItems.count()) { + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + if (visibleItems.at(i)->index != -1) + return visibleItems.at(i)->index; + } + return defaultValue; +} + +FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; +} + +FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const { + const qreal pos = isContentFlowReversed() ? -position()-size() : position(); + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index != -1 && item->endPosition() > pos) + return item; + } + return visibleItems.count() ? visibleItems.first() : 0; +} + +// Map a model index to visibleItems list index. +// These may differ if removed items are still present in the visible list, +// e.g. doing a removal animation +int QQuickItemViewPrivate::mapFromModel(int modelIndex) const +{ + if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) + return -1; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return i; + if (item->index > modelIndex) + return -1; + } + return -1; // Not in visibleList +} + +void QQuickItemViewPrivate::init() +{ + Q_Q(QQuickItemView); + QQuickItemPrivate::get(contentItem)->childrenDoNotOverlap = true; + q->setFlag(QQuickItem::ItemIsFocusScope); + addItemChangeListener(this, Geometry); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlickableDirection(QQuickFlickable::VerticalFlick); +} + +void QQuickItemViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QQuickItemView); + applyPendingChanges(); + + if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { + if (currentItem) { + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + currentIndex = modelIndex; + emit q->currentIndexChanged(); + updateHighlight(); + } else if (currentIndex != modelIndex) { + currentIndex = modelIndex; + emit q->currentIndexChanged(); + } + return; + } + + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + + FxViewItem *oldCurrentItem = currentItem; + currentIndex = modelIndex; + currentItem = createItem(modelIndex); + if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + oldCurrentItem->attached->setIsCurrentItem(false); + if (currentItem) { + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + initializeCurrentItem(); + } + + updateHighlight(); + emit q->currentIndexChanged(); + releaseItem(oldCurrentItem); +} + +void QQuickItemViewPrivate::clear() +{ + currentChanges.reset(); + timeline.clear(); + + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + visibleIndex = 0; + + releaseItem(currentItem); + currentItem = 0; + createHighlight(); + trackedItem = 0; + + markExtentsDirty(); + itemCount = 0; +} + + +void QQuickItemViewPrivate::mirrorChange() +{ + Q_Q(QQuickItemView); + regenerate(); + emit q->effectiveLayoutDirectionChanged(); +} + +void QQuickItemViewPrivate::refill() +{ + if (isContentFlowReversed()) + refill(-position()-size(), -position()); + else + refill(position(), position()+size()); +} + +void QQuickItemViewPrivate::refill(qreal from, qreal to, bool doBuffer) +{ + Q_Q(QQuickItemView); + if (!isValid() || !q->isComponentComplete()) + return; + + currentChanges.reset(); + + int prevCount = itemCount; + itemCount = model->count(); + qreal bufferFrom = from - buffer; + qreal bufferTo = to + buffer; + qreal fillFrom = from; + qreal fillTo = to; + if (doBuffer && (bufferMode & BufferAfter)) + fillTo = bufferTo; + if (doBuffer && (bufferMode & BufferBefore)) + fillFrom = bufferFrom; + + // Item creation and release is staggered in order to avoid + // creating/releasing multiple items in one frame + // while flicking (as much as possible). + + bool changed = addVisibleItems(fillFrom, fillTo, doBuffer); + + if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create + if (removeNonVisibleItems(bufferFrom, bufferTo)) + changed = true; + deferredRelease = false; + } else { + deferredRelease = true; + } + + if (changed) { + markExtentsDirty(); + visibleItemsChanged(); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); + } + + lazyRelease = false; + if (prevCount != itemCount) + emit q->countChanged(); +} + +void QQuickItemViewPrivate::regenerate() +{ + Q_Q(QQuickItemView); + if (q->isComponentComplete()) { + currentChanges.reset(); + delete header; + header = 0; + delete footer; + footer = 0; + updateHeader(); + updateFooter(); + clear(); + updateViewport(); + setPosition(contentStartPosition()); + refill(); + updateCurrent(currentIndex); + } +} + +void QQuickItemViewPrivate::updateViewport() +{ + Q_Q(QQuickItemView); + if (isValid()) { + if (layoutOrientation() == Qt::Vertical) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(endPosition() - startPosition()); + } +} + +void QQuickItemViewPrivate::layout() +{ + Q_Q(QQuickItemView); + if (inApplyModelChanges) + return; + + if (!isValid() && !visibleItems.count()) { + clear(); + setPosition(contentStartPosition()); + return; + } + + if (!applyModelChanges() && !forceLayout) + return; + forceLayout = false; + + layoutVisibleItems(); + refill(); + + markExtentsDirty(); + + updateHighlight(); + + if (!q->isMoving() && !q->isFlicking()) { + fixupPosition(); + refill(); + } + + updateHeader(); + updateFooter(); + updateViewport(); + updateUnrequestedPositions(); +} + +bool QQuickItemViewPrivate::applyModelChanges() +{ + Q_Q(QQuickItemView); + if (!q->isComponentComplete() || !currentChanges.hasPendingChanges() || inApplyModelChanges) + return false; + inApplyModelChanges = true; + + updateUnrequestedIndexes(); + moveReason = QQuickItemViewPrivate::Other; + + int prevCount = itemCount; + bool removedVisible = false; + bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty() + || !currentChanges.pendingChanges.inserts().isEmpty(); + + FxViewItem *firstVisible = firstVisibleItem(); + FxViewItem *origVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0; + int firstItemIndex = firstVisible ? firstVisible->index : -1; + qreal removedBeforeFirstVisibleBy = 0; + + const QVector &removals = currentChanges.pendingChanges.removes(); + for (int i=0; i::Iterator it = visibleItems.begin(); + while (it != visibleItems.end()) { + FxViewItem *item = *it; + if (item->index == -1 || item->index < removals[i].index) { + // already removed, or before removed items + if (item->index < removals[i].index && !removedVisible) + removedVisible = true; + ++it; + } else if (item->index >= removals[i].index + removals[i].count) { + // after removed items + item->index -= removals[i].count; + ++it; + } else { + // removed item + removedVisible = true; + if (!removals[i].isMove()) + item->attached->emitRemove(); + + if (item->attached->delayRemove() && !removals[i].isMove()) { + item->index = -1; + QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection); + ++it; + } else { + if (firstVisible && item->position() < firstVisible->position() && item != visibleItems.first()) + removedBeforeFirstVisibleBy += item->size(); + if (removals[i].isMove()) { + currentChanges.removedItems.insert(removals[i].moveKey(item->index), item); + } else { + if (item == firstVisible) + firstVisible = 0; + currentChanges.removedItems.insertMulti(QDeclarativeChangeSet::MoveKey(), item); + } + it = visibleItems.erase(it); + } + } + } + + } + if (!removals.isEmpty()) + updateVisibleIndex(); + + const QVector &insertions = currentChanges.pendingChanges.inserts(); + bool addedVisible = false; + InsertionsResult insertResult; + bool allInsertionsBeforeVisible = true; + + for (int i=0; i= visibleIndex) + allInsertionsBeforeVisible = false; + if (wasEmpty && !visibleItems.isEmpty()) + resetFirstItemPosition(); + itemCount += insertions[i].count; + } + for (int i=0; iattached->emitAdd(); + + // if the first visible item has moved, ensure another one takes its place + // so that we avoid shifting all content forwards + // (if an item is removed from before the first visible, the first visible should not move upwards) + if (firstVisible && firstItemIndex >= 0) { + bool found = false; + for (int i=0; iindex == firstItemIndex) { + // an item has moved backwards up to the first visible's position + resetItemPosition(insertResult.movedBackwards[i], firstVisible); + insertResult.movedBackwards.removeAt(i); + found = true; + break; + } + } + if (!found && !allInsertionsBeforeVisible) { + // first visible item has moved forward, another visible item takes its place + FxViewItem *item = visibleItem(firstItemIndex); + if (item) + resetItemPosition(item, firstVisible); + } + } + + // Ensure we don't cause an ugly list scroll + if (firstVisible && visibleItems.count() && visibleItems.first() != firstVisible) { + // ensure first item is placed at correct postion if moving backward + // since it will be used to position all subsequent items + if (insertResult.movedBackwards.count() && origVisibleItemsFirst) + resetItemPosition(visibleItems.first(), origVisibleItemsFirst); + qreal moveBackwardsBy = insertResult.sizeAddedBeforeVisible; + for (int i=0; isize(); + moveItemBy(visibleItems.first(), removedBeforeFirstVisibleBy, moveBackwardsBy); + } + + // Whatever removed/moved items remain are no longer visible items. + for (QHash::Iterator it = currentChanges.removedItems.begin(); + it != currentChanges.removedItems.end(); ++it) { + releaseItem(it.value()); + } + currentChanges.removedItems.clear(); + + if (currentChanges.currentChanged) { + if (currentChanges.currentRemoved && currentItem) { + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + } + if (!currentIndexCleared) + updateCurrent(currentChanges.newCurrentIndex); + } + currentChanges.reset(); + + updateSections(); + if (prevCount != itemCount) + emit q->countChanged(); + + bool visibleAffected = removedVisible || addedVisible || !currentChanges.pendingChanges.changes().isEmpty(); + if (!visibleAffected && viewportChanged) + updateViewport(); + + inApplyModelChanges = false; + return visibleAffected; +} + +FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex) +{ + Q_Q(QQuickItemView); + + requestedIndex = modelIndex; + FxViewItem *viewItem = 0; + + if (QQuickItem *item = model->item(modelIndex, false)) { + viewItem = newViewItem(modelIndex, item); + if (viewItem) { + viewItem->index = modelIndex; + if (model->completePending()) { + // complete + viewItem->item->setZ(1); + QDeclarative_setParent_noEvent(viewItem->item, q->contentItem()); + viewItem->item->setParentItem(q->contentItem()); + model->completeItem(); + } else { + QDeclarative_setParent_noEvent(viewItem->item, q->contentItem()); + viewItem->item->setParentItem(q->contentItem()); + } + // do other set up for the new item that should not happen + // until after bindings are evaluated + initializeViewItem(viewItem); + + unrequestedItems.remove(viewItem->item); + } + } + requestedIndex = -1; + return viewItem; +} + + +void QQuickItemViewPrivate::releaseItem(FxViewItem *item) +{ + Q_Q(QQuickItemView); + if (!item || !model) + return; + if (trackedItem == item) + trackedItem = 0; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item); + itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + if (model->release(item->item) == 0) { + // item was not destroyed, and we no longer reference it. + unrequestedItems.insert(item->item, model->indexOf(item->item, q)); + } + delete item; +} + +QQuickItem *QQuickItemViewPrivate::createHighlightItem() +{ + return createComponentItem(highlightComponent, true, true); +} + +QQuickItem *QQuickItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault) +{ + Q_Q(QQuickItemView); + + QQuickItem *item = 0; + if (component) { + QDeclarativeContext *creationContext = component->creationContext(); + QDeclarativeContext *context = new QDeclarativeContext( + creationContext ? creationContext : qmlContext(q)); + QObject *nobj = component->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + } else if (createDefault) { + item = new QQuickItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + if (receiveItemGeometryChanges) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); + } + } + return item; +} + +void QQuickItemViewPrivate::updateTrackedItem() +{ + Q_Q(QQuickItemView); + FxViewItem *item = currentItem; + if (highlight) + item = highlight; + trackedItem = item; + + if (trackedItem) + q->trackedPositionChanged(); +} + +void QQuickItemViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QQuickItemView); + for (QHash::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); +} + +void QQuickItemViewPrivate::updateUnrequestedPositions() +{ + for (QHash::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + repositionPackageItemAt(it.key(), it.value()); +} + +void QQuickItemViewPrivate::updateVisibleIndex() +{ + visibleIndex = 0; + for (QList::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) { + if ((*it)->index != -1) { + visibleIndex = (*it)->index; + break; + } + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickitemview_p.h b/src/declarative/items/qquickitemview_p.h new file mode 100644 index 0000000000..8bb63539b4 --- /dev/null +++ b/src/declarative/items/qquickitemview_p.h @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEMVIEW_P_H +#define QQUICKITEMVIEW_P_H + +#include "qquickflickable_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeChangeSet; + +class QQuickItemViewPrivate; + +class Q_AUTOTEST_EXPORT QQuickItemView : public QQuickFlickable +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentIndexChanged) + + Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) + Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) + + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) + + Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged) + Q_PROPERTY(QQuickItem *headerItem READ headerItem NOTIFY headerItemChanged) + Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged) + Q_PROPERTY(QQuickItem *footerItem READ footerItem NOTIFY footerItemChanged) + + Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) + Q_PROPERTY(QQuickItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) + Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem NOTIFY highlightFollowsCurrentItemChanged) + Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) + Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) + Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) + Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) + + Q_ENUMS(HighlightRangeMode) + Q_ENUMS(PositionMode) + +public: + QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent = 0); + ~QQuickItemView(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int count() const; + + int currentIndex() const; + void setCurrentIndex(int idx); + + QQuickItem *currentItem() const; + + bool isWrapEnabled() const; + void setWrapEnabled(bool); + + int cacheBuffer() const; + void setCacheBuffer(int); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection(Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + + QDeclarativeComponent *footer() const; + void setFooter(QDeclarativeComponent *); + QQuickItem *footerItem() const; + + QDeclarativeComponent *header() const; + void setHeader(QDeclarativeComponent *); + QQuickItem *headerItem() const; + + QDeclarativeComponent *highlight() const; + void setHighlight(QDeclarativeComponent *); + + QQuickItem *highlightItem() const; + + bool highlightFollowsCurrentItem() const; + virtual void setHighlightFollowsCurrentItem(bool); + + enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + HighlightRangeMode highlightRangeMode() const; + void setHighlightRangeMode(HighlightRangeMode mode); + + qreal preferredHighlightBegin() const; + void setPreferredHighlightBegin(qreal); + void resetPreferredHighlightBegin(); + + qreal preferredHighlightEnd() const; + void setPreferredHighlightEnd(qreal); + void resetPreferredHighlightEnd(); + + int highlightMoveDuration() const; + virtual void setHighlightMoveDuration(int); + + enum PositionMode { Beginning, Center, End, Visible, Contain }; + + Q_INVOKABLE void positionViewAtIndex(int index, int mode); + Q_INVOKABLE int indexAt(qreal x, qreal y) const; + Q_INVOKABLE void positionViewAtBeginning(); + Q_INVOKABLE void positionViewAtEnd(); + + virtual void setContentX(qreal pos); + virtual void setContentY(qreal pos); + virtual qreal xOrigin() const; + +signals: + void modelChanged(); + void delegateChanged(); + void countChanged(); + void currentIndexChanged(); + + void keyNavigationWrapsChanged(); + void cacheBufferChanged(); + + void layoutDirectionChanged(); + void effectiveLayoutDirectionChanged(); + + void headerChanged(); + void footerChanged(); + void headerItemChanged(); + void footerItemChanged(); + + void highlightChanged(); + void highlightItemChanged(); + void highlightFollowsCurrentItemChanged(); + void highlightRangeModeChanged(); + void preferredHighlightBeginChanged(); + void preferredHighlightEndChanged(); + void highlightMoveDurationChanged(); + +protected: + virtual void updatePolish(); + virtual void componentComplete(); + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual qreal minYExtent() const; + virtual qreal maxYExtent() const; + virtual qreal minXExtent() const; + virtual qreal maxXExtent() const; + +protected slots: + virtual void updateSections() {} + void destroyRemoved(); + void createdItem(int index, QQuickItem *item); + void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); + void destroyingItem(QQuickItem *item); + void animStopped(); + void trackedPositionChanged(); + + + +private: + Q_DECLARE_PRIVATE(QQuickItemView) +}; + + +class Q_AUTOTEST_EXPORT QQuickItemViewAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) + Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged) + + Q_PROPERTY(QString section READ section NOTIFY sectionChanged) + Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged) + Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged) + +public: + QQuickItemViewAttached(QObject *parent) + : QObject(parent), m_isCurrent(false), m_delayRemove(false) {} + ~QQuickItemViewAttached() {} + + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + bool delayRemove() const { return m_delayRemove; } + void setDelayRemove(bool delay) { + if (m_delayRemove != delay) { + m_delayRemove = delay; + emit delayRemoveChanged(); + } + } + + QString section() const { return m_section; } + void setSection(const QString §) { + if (m_section != sect) { + m_section = sect; + emit sectionChanged(); + } + } + + QString prevSection() const { return m_prevSection; } + void setPrevSection(const QString §) { + if (m_prevSection != sect) { + m_prevSection = sect; + emit prevSectionChanged(); + } + } + + QString nextSection() const { return m_nextSection; } + void setNextSection(const QString §) { + if (m_nextSection != sect) { + m_nextSection = sect; + emit nextSectionChanged(); + } + } + + void emitAdd() { emit add(); } + void emitRemove() { emit remove(); } + +signals: + void currentItemChanged(); + void delayRemoveChanged(); + + void add(); + void remove(); + + void sectionChanged(); + void prevSectionChanged(); + void nextSectionChanged(); + +public: + bool m_isCurrent : 1; + bool m_delayRemove : 1; + + // current only used by list view + mutable QString m_section; + QString m_prevSection; + QString m_nextSection; +}; + + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QQUICKITEMVIEW_P_H + diff --git a/src/declarative/items/qquickitemview_p_p.h b/src/declarative/items/qquickitemview_p_p.h new file mode 100644 index 0000000000..398de84c25 --- /dev/null +++ b/src/declarative/items/qquickitemview_p_p.h @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEMVIEW_P_P_H +#define QQUICKITEMVIEW_P_P_H + +#include "qquickitemview_p.h" +#include "qquickflickable_p_p.h" +#include "qquickvisualdatamodel_p.h" +#include + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class FxViewItem +{ +public: + FxViewItem(QQuickItem *, bool own); + ~FxViewItem(); + + // these are positions and sizes along the current direction of scrolling/flicking + virtual qreal position() const = 0; + virtual qreal endPosition() const = 0; + virtual qreal size() const = 0; + virtual qreal sectionSize() const = 0; + + virtual bool contains(qreal x, qreal y) const = 0; + + QQuickItem *item; + bool ownItem; + int index; + QQuickItemViewAttached *attached; +}; + +class QQuickItemViewChangeSet +{ +public: + QQuickItemViewChangeSet(); + + bool hasPendingChanges() const; + void prepare(int currentIndex, int count); + void reset(); + + void applyChanges(const QDeclarativeChangeSet &changeSet); + + int itemCount; + int newCurrentIndex; + QDeclarativeChangeSet pendingChanges; + QHash removedItems; + + bool active : 1; + bool currentChanged : 1; + bool currentRemoved : 1; +}; + +class QQuickItemViewPrivate : public QQuickFlickablePrivate +{ + Q_DECLARE_PUBLIC(QQuickItemView) +public: + QQuickItemViewPrivate(); + + struct InsertionsResult { + QList addedItems; + QList movedBackwards; + qreal sizeAddedBeforeVisible; + + InsertionsResult() : sizeAddedBeforeVisible(0) {} + }; + + enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; + enum MovementReason { Other, SetIndex, Mouse }; + + bool isValid() const; + qreal position() const; + qreal size() const; + qreal startPosition() const; + qreal endPosition() const; + qreal contentStartPosition() const; + int findLastVisibleIndex(int defaultValue = -1) const; + FxViewItem *visibleItem(int modelIndex) const; + FxViewItem *firstVisibleItem() const; + int mapFromModel(int modelIndex) const; + + virtual void init(); + virtual void clear(); + virtual void updateViewport(); + + void regenerate(); + void layout(); + void refill(); + void refill(qreal from, qreal to, bool doBuffer = false); + void mirrorChange(); + + FxViewItem *createItem(int modelIndex); + virtual void releaseItem(FxViewItem *item); + + QQuickItem *createHighlightItem(); + QQuickItem *createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault = false); + + void updateCurrent(int modelIndex); + void updateTrackedItem(); + void updateUnrequestedIndexes(); + void updateUnrequestedPositions(); + void updateVisibleIndex(); + void positionViewAtIndex(int index, int mode); + void applyPendingChanges(); + bool applyModelChanges(); + + void checkVisible() const; + + void markExtentsDirty() { + if (layoutOrientation() == Qt::Vertical) + vData.markExtentsDirty(); + else + hData.markExtentsDirty(); + } + + QDeclarativeGuard model; + QVariant modelVariant; + int itemCount; + int buffer; + int bufferMode; + Qt::LayoutDirection layoutDirection; + + MovementReason moveReason; + + QList visibleItems; + int visibleIndex; + int currentIndex; + FxViewItem *currentItem; + FxViewItem *trackedItem; + QHash unrequestedItems; + int requestedIndex; + QQuickItemViewChangeSet currentChanges; + + // XXX split into struct + QDeclarativeComponent *highlightComponent; + FxViewItem *highlight; + int highlightRange; // enum value + qreal highlightRangeStart; + qreal highlightRangeEnd; + int highlightMoveDuration; + + QDeclarativeComponent *headerComponent; + FxViewItem *header; + QDeclarativeComponent *footerComponent; + FxViewItem *footer; + + mutable qreal minExtent; + mutable qreal maxExtent; + + bool ownModel : 1; + bool wrap : 1; + bool lazyRelease : 1; + bool deferredRelease : 1; + bool inApplyModelChanges : 1; + bool inViewportMoved : 1; + bool forceLayout : 1; + bool currentIndexCleared : 1; + bool haveHighlightRange : 1; + bool autoHighlight : 1; + bool highlightRangeStartValid : 1; + bool highlightRangeEndValid : 1; + +protected: + virtual Qt::Orientation layoutOrientation() const = 0; + virtual bool isContentFlowReversed() const = 0; + + virtual qreal positionAt(int index) const = 0; + virtual qreal endPositionAt(int index) const = 0; + virtual qreal originPosition() const = 0; + virtual qreal lastPosition() const = 0; + + virtual qreal headerSize() const = 0; + virtual qreal footerSize() const = 0; + virtual bool showHeaderForIndex(int index) const = 0; + virtual bool showFooterForIndex(int index) const = 0; + virtual void updateHeader() = 0; + virtual void updateFooter() = 0; + + virtual void createHighlight() = 0; + virtual void updateHighlight() = 0; + virtual void resetHighlightPosition() = 0; + + virtual void setPosition(qreal pos) = 0; + virtual void fixupPosition() = 0; + + virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer) = 0; + virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) = 0; + virtual void visibleItemsChanged() = 0; + + virtual FxViewItem *newViewItem(int index, QQuickItem *item) = 0; + virtual void repositionPackageItemAt(QQuickItem *item, int index) = 0; + virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem) = 0; + virtual void resetFirstItemPosition() = 0; + virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards) = 0; + + virtual void layoutVisibleItems() = 0; + virtual void changedVisibleIndex(int newIndex) = 0; + virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *, InsertionsResult *) = 0; + + virtual void initializeViewItem(FxViewItem *) {} + virtual void initializeCurrentItem() {} + virtual void updateSections() {} + + virtual void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKITEMVIEW_P_P_H diff --git a/src/declarative/items/qquicklistview.cpp b/src/declarative/items/qquicklistview.cpp new file mode 100644 index 0000000000..e43a831c4c --- /dev/null +++ b/src/declarative/items/qquicklistview.cpp @@ -0,0 +1,2500 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicklistview_p.h" +#include "qquickitemview_p_p.h" +#include "qquickvisualitemmodel_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "qplatformdefs.h" + +QT_BEGIN_NAMESPACE + +#ifndef QML_FLICK_SNAPONETHRESHOLD +#define QML_FLICK_SNAPONETHRESHOLD 30 +#endif + +class FxListItemSG; + +class QQuickListViewPrivate : public QQuickItemViewPrivate +{ + Q_DECLARE_PUBLIC(QQuickListView) +public: + static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); } + + virtual Qt::Orientation layoutOrientation() const; + virtual bool isContentFlowReversed() const; + bool isRightToLeft() const; + + virtual qreal positionAt(int index) const; + virtual qreal endPositionAt(int index) const; + virtual qreal originPosition() const; + virtual qreal lastPosition() const; + + FxViewItem *itemBefore(int modelIndex) const; + QString sectionAt(int modelIndex); + qreal snapPosAt(qreal pos); + FxViewItem *snapItemAt(qreal pos); + + virtual void init(); + virtual void clear(); + + virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer); + virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo); + virtual void visibleItemsChanged(); + + virtual FxViewItem *newViewItem(int index, QQuickItem *item); + virtual void initializeViewItem(FxViewItem *item); + virtual void releaseItem(FxViewItem *item); + virtual void repositionPackageItemAt(QQuickItem *item, int index); + virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem); + virtual void resetFirstItemPosition(); + virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards); + + virtual void createHighlight(); + virtual void updateHighlight(); + virtual void resetHighlightPosition(); + + virtual void setPosition(qreal pos); + virtual void layoutVisibleItems(); + bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *firstVisible, InsertionsResult *); + + virtual void updateSections(); + QQuickItem *getSectionItem(const QString §ion); + void releaseSectionItem(QQuickItem *item); + void updateInlineSection(FxListItemSG *); + void updateCurrentSection(); + void updateStickySections(); + + virtual qreal headerSize() const; + virtual qreal footerSize() const; + virtual bool showHeaderForIndex(int index) const; + virtual bool showFooterForIndex(int index) const; + virtual void updateHeader(); + virtual void updateFooter(); + + virtual void changedVisibleIndex(int newIndex); + virtual void initializeCurrentItem(); + + void updateAverage(); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + virtual void fixupPosition(); + virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); + virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); + + QQuickListView::Orientation orient; + qreal visiblePos; + qreal averageSize; + qreal spacing; + QQuickListView::SnapMode snapMode; + + QSmoothedAnimation *highlightPosAnimator; + QSmoothedAnimation *highlightSizeAnimator; + qreal highlightMoveSpeed; + qreal highlightResizeSpeed; + int highlightResizeDuration; + + QQuickViewSection *sectionCriteria; + QString currentSection; + static const int sectionCacheSize = 5; + QQuickItem *sectionCache[sectionCacheSize]; + QQuickItem *currentSectionItem; + QString currentStickySection; + QQuickItem *nextSectionItem; + QString nextStickySection; + QString lastVisibleSection; + QString nextSection; + + qreal overshootDist; + bool correctFlick : 1; + bool inFlickCorrection : 1; + + QQuickListViewPrivate() + : orient(QQuickListView::Vertical) + , visiblePos(0) + , averageSize(100.0), spacing(0.0) + , snapMode(QQuickListView::NoSnap) + , highlightPosAnimator(0), highlightSizeAnimator(0) + , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1) + , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0) + , overshootDist(0.0), correctFlick(false), inFlickCorrection(false) + {} + + friend class QQuickViewSection; +}; + +//---------------------------------------------------------------------------- + +QQuickViewSection::QQuickViewSection(QQuickListView *parent) + : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels) + , m_view(parent ? QQuickListViewPrivate::get(parent) : 0) +{ +} + +void QQuickViewSection::setProperty(const QString &property) +{ + if (property != m_property) { + m_property = property; + emit propertyChanged(); + m_view->updateSections(); + } +} + +void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria) +{ + if (criteria != m_criteria) { + m_criteria = criteria; + emit criteriaChanged(); + m_view->updateSections(); + } +} + +void QQuickViewSection::setDelegate(QDeclarativeComponent *delegate) +{ + if (delegate != m_delegate) { + m_delegate = delegate; + emit delegateChanged(); + m_view->updateSections(); + } +} + +QString QQuickViewSection::sectionString(const QString &value) +{ + if (m_criteria == FirstCharacter) + return value.isEmpty() ? QString() : value.at(0); + else + return value; +} + +void QQuickViewSection::setLabelPositioning(int l) +{ + if (m_labelPositioning != l) { + m_labelPositioning = l; + emit labelPositioningChanged(); + m_view->updateSections(); + } +} + +//---------------------------------------------------------------------------- + +class FxListItemSG : public FxViewItem +{ +public: + FxListItemSG(QQuickItem *i, QQuickListView *v, bool own) : FxViewItem(i, own), section(0), view(v) { + attached = static_cast(qmlAttachedPropertiesObject(item)); + if (attached) + static_cast(attached)->setView(view); + } + + ~FxListItemSG() {} + + qreal position() const { + if (section) { + if (view->orientation() == QQuickListView::Vertical) + return section->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x()); + } else { + return itemPosition(); + } + } + qreal itemPosition() const { + if (view->orientation() == QQuickListView::Vertical) + return item->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x()); + } + qreal size() const { + if (section) + return (view->orientation() == QQuickListView::Vertical ? item->height()+section->height() : item->width()+section->width()); + else + return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width()); + } + qreal itemSize() const { + return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width()); + } + qreal sectionSize() const { + if (section) + return (view->orientation() == QQuickListView::Vertical ? section->height() : section->width()); + return 0.0; + } + qreal endPosition() const { + if (view->orientation() == QQuickListView::Vertical) { + return item->y() + item->height(); + } else { + return (view->effectiveLayoutDirection() == Qt::RightToLeft + ? -item->x() + : item->x() + item->width()); + } + } + void setPosition(qreal pos) { + if (view->orientation() == QQuickListView::Vertical) { + if (section) { + section->setY(pos); + pos += section->height(); + } + item->setY(pos); + } else { + if (view->effectiveLayoutDirection() == Qt::RightToLeft) { + if (section) { + section->setX(-section->width()-pos); + pos += section->width(); + } + item->setX(-item->width()-pos); + } else { + if (section) { + section->setX(pos); + pos += section->width(); + } + item->setX(pos); + } + } + } + void setSize(qreal size) { + if (view->orientation() == QQuickListView::Vertical) + item->setHeight(size); + else + item->setWidth(size); + } + bool contains(qreal x, qreal y) const { + return (x >= item->x() && x < item->x() + item->width() && + y >= item->y() && y < item->y() + item->height()); + } + + QQuickItem *section; + QQuickListView *view; +}; + +//---------------------------------------------------------------------------- + +bool QQuickListViewPrivate::isContentFlowReversed() const +{ + return isRightToLeft(); +} + +Qt::Orientation QQuickListViewPrivate::layoutOrientation() const +{ + return static_cast(orient); +} + +bool QQuickListViewPrivate::isRightToLeft() const +{ + Q_Q(const QQuickListView); + return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft; +} + +// Returns the item before modelIndex, if created. +// May return an item marked for removal. +FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const +{ + if (modelIndex < visibleIndex) + return 0; + int idx = 1; + int lastIndex = -1; + while (idx < visibleItems.count()) { + FxViewItem *item = visibleItems.at(idx); + if (item->index != -1) + lastIndex = item->index; + if (item->index == modelIndex) + return visibleItems.at(idx-1); + ++idx; + } + if (lastIndex == modelIndex-1) + return visibleItems.last(); + return 0; +} + +void QQuickListViewPrivate::setPosition(qreal pos) +{ + Q_Q(QQuickListView); + if (orient == QQuickListView::Vertical) { + q->QQuickFlickable::setContentY(pos); + } else { + if (isRightToLeft()) + q->QQuickFlickable::setContentX(-pos-size()); + else + q->QQuickFlickable::setContentX(pos); + } +} + +qreal QQuickListViewPrivate::originPosition() const +{ + qreal pos = 0; + if (!visibleItems.isEmpty()) { + pos = (*visibleItems.constBegin())->position(); + if (visibleIndex > 0) + pos -= visibleIndex * (averageSize + spacing); + } + return pos; +} + +qreal QQuickListViewPrivate::lastPosition() const +{ + qreal pos = 0; + if (!visibleItems.isEmpty()) { + int invisibleCount = visibleItems.count() - visibleIndex; + for (int i = visibleItems.count()-1; i >= 0; --i) { + if (visibleItems.at(i)->index != -1) { + invisibleCount = model->count() - visibleItems.at(i)->index - 1; + break; + } + } + pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); + } else if (model && model->count()) { + pos = (model->count() * averageSize + (model->count()-1) * spacing); + } + return pos; +} + +qreal QQuickListViewPrivate::positionAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return item->position(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = visibleIndex - modelIndex; + qreal cs = 0; + if (modelIndex == currentIndex && currentItem) { + cs = currentItem->size() + spacing; + --count; + } + return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs; + } else { + int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1; + return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing); + } + } + return 0; +} + +qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return item->endPosition(); + if (!visibleItems.isEmpty()) { + if (modelIndex < visibleIndex) { + int count = visibleIndex - modelIndex; + return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing; + } else { + int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1; + return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing); + } + } + return 0; +} + +QString QQuickListViewPrivate::sectionAt(int modelIndex) +{ + if (FxViewItem *item = visibleItem(modelIndex)) + return item->attached->section(); + + QString section; + if (sectionCriteria) { + QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); + section = sectionCriteria->sectionString(propValue); + } + + return section; +} + +qreal QQuickListViewPrivate::snapPosAt(qreal pos) +{ + if (FxViewItem *snapItem = snapItemAt(pos)) + return snapItem->position(); + if (visibleItems.count()) { + qreal firstPos = (*visibleItems.constBegin())->position(); + qreal endPos = (*(--visibleItems.constEnd()))->position(); + if (pos < firstPos) { + return firstPos - qRound((firstPos - pos) / averageSize) * averageSize; + } else if (pos > endPos) + return endPos + qRound((pos - endPos) / averageSize) * averageSize; + } + return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition(); +} + +FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos) +{ + FxViewItem *snapItem = 0; + qreal prevItemSize = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == -1) + continue; + qreal itemTop = item->position(); + if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size()) + return item; + if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos) + snapItem = item; + prevItemSize = item->size(); + } + return snapItem; +} + +void QQuickListViewPrivate::changedVisibleIndex(int newIndex) +{ + visiblePos = positionAt(newIndex); + visibleIndex = newIndex; +} + +void QQuickListViewPrivate::init() +{ + QQuickItemViewPrivate::init(); + ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize); +} + +void QQuickListViewPrivate::clear() +{ + for (int i = 0; i < sectionCacheSize; ++i) { + delete sectionCache[i]; + sectionCache[i] = 0; + } + visiblePos = 0; + currentSectionItem = 0; + nextSectionItem = 0; + lastVisibleSection = QString(); + QQuickItemViewPrivate::clear(); +} + +FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item) +{ + Q_Q(QQuickListView); + + FxListItemSG *listItem = new FxListItemSG(item, q, false); + listItem->index = modelIndex; + + // initialise attached properties + if (sectionCriteria) { + QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); + listItem->attached->m_section = sectionCriteria->sectionString(propValue); + if (modelIndex > 0) { + if (FxViewItem *item = itemBefore(modelIndex)) + listItem->attached->m_prevSection = item->attached->section(); + else + listItem->attached->m_prevSection = sectionAt(modelIndex-1); + } + if (modelIndex < model->count()-1) { + if (FxViewItem *item = visibleItem(modelIndex+1)) + listItem->attached->m_nextSection = static_cast(item->attached)->section(); + else + listItem->attached->m_nextSection = sectionAt(modelIndex+1); + } + } + + return listItem; +} + +void QQuickListViewPrivate::initializeViewItem(FxViewItem *item) +{ + QQuickItemViewPrivate::initializeViewItem(item); + + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item); + itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); + + if (sectionCriteria && sectionCriteria->delegate()) { + if (item->attached->m_prevSection != item->attached->m_section) + updateInlineSection(static_cast(item)); + } +} + +void QQuickListViewPrivate::releaseItem(FxViewItem *item) +{ + if (item) { + FxListItemSG* listItem = static_cast(item); + if (listItem->section) { + int i = 0; + do { + if (!sectionCache[i]) { + sectionCache[i] = listItem->section; + sectionCache[i]->setVisible(false); + listItem->section = 0; + break; + } + ++i; + } while (i < sectionCacheSize); + delete listItem->section; + } + } + QQuickItemViewPrivate::releaseItem(item); +} + +bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer) +{ + qreal itemEnd = visiblePos; + if (visibleItems.count()) { + visiblePos = (*visibleItems.constBegin())->position(); + itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing; + } + + int modelIndex = findLastVisibleIndex(); + bool haveValidItems = modelIndex >= 0; + modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1; + + if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing + || fillTo < visiblePos - averageSize - spacing)) { + // We've jumped more than a page. Estimate which items are now + // visible and fill from there. + int count = (fillFrom - itemEnd) / (averageSize + spacing); + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + modelIndex += count; + if (modelIndex >= model->count()) { + count -= modelIndex - model->count() + 1; + modelIndex = model->count() - 1; + } else if (modelIndex < 0) { + count -= modelIndex; + modelIndex = 0; + } + visibleIndex = modelIndex; + visiblePos = itemEnd + count * (averageSize + spacing); + itemEnd = visiblePos; + } + + bool changed = false; + FxListItemSG *item = 0; + qreal pos = itemEnd; + while (modelIndex < model->count() && pos <= fillTo) { +// qDebug() << "refill: append item" << modelIndex << "pos" << pos; + if (!(item = static_cast(createItem(modelIndex)))) + break; + item->setPosition(pos); + pos += item->size() + spacing; + visibleItems.append(item); + ++modelIndex; + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos >= fillFrom) { +// qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; + if (!(item = static_cast(createItem(visibleIndex-1)))) + break; + --visibleIndex; + visiblePos -= item->size() + spacing; + item->setPosition(visiblePos); + visibleItems.prepend(item); + changed = true; + if (doBuffer) // never buffer more than one item per frame + break; + } + + return changed; +} + +bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) +{ + FxViewItem *item = 0; + bool changed = false; + + while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() <= bufferFrom) { + if (item->attached->delayRemove()) + break; + if (item->size() == 0) + break; +// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); + if (item->index != -1) + visibleIndex++; + visibleItems.removeFirst(); + releaseItem(item); + changed = true; + } + while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { + if (item->attached->delayRemove()) + break; +// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); + visibleItems.removeLast(); + releaseItem(item); + changed = true; + } + + return changed; +} + +void QQuickListViewPrivate::visibleItemsChanged() +{ + if (visibleItems.count()) + visiblePos = (*visibleItems.constBegin())->position(); + updateAverage(); + if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) { + static_cast(currentItem)->setPosition(positionAt(currentIndex)); + updateHighlight(); + } + if (sectionCriteria) + updateCurrentSection(); + updateHeader(); + updateFooter(); + updateViewport(); + updateUnrequestedPositions(); +} + +void QQuickListViewPrivate::layoutVisibleItems() +{ + if (!visibleItems.isEmpty()) { + bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item; + qreal sum = (*visibleItems.constBegin())->size(); + qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing; + for (int i=1; i < visibleItems.count(); ++i) { + FxListItemSG *item = static_cast(visibleItems.at(i)); + item->setPosition(pos); + pos += item->size() + spacing; + sum += item->size(); + fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item); + } + averageSize = qRound(sum / visibleItems.count()); + + // move current item if it is not a visible item. + if (currentIndex >= 0 && currentItem && !fixedCurrent) { + static_cast(currentItem)->setPosition(positionAt(currentIndex)); + } + } +} + +void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index) +{ + Q_Q(QQuickListView); + qreal pos = position(); + if (orient == QQuickListView::Vertical) { + if (item->y() + item->height() > pos && item->y() < pos + q->height()) + item->setY(positionAt(index)); + } else { + if (item->x() + item->width() > pos && item->x() < pos + q->width()) { + if (isRightToLeft()) + item->setX(-positionAt(index)-item->width()); + else + item->setX(positionAt(index)); + } + } +} + +void QQuickListViewPrivate::resetItemPosition(FxViewItem *item, FxViewItem *toItem) +{ + if (item == toItem) + return; + static_cast(item)->setPosition(toItem->position()); +} + +void QQuickListViewPrivate::resetFirstItemPosition() +{ + FxListItemSG *item = static_cast(visibleItems.first()); + item->setPosition(0); +} + +void QQuickListViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards) +{ + qreal diff = forwards - backwards; + static_cast(item)->setPosition(item->position() + diff); +} + +void QQuickListViewPrivate::createHighlight() +{ + Q_Q(QQuickListView); + bool changed = false; + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + delete highlight; + highlight = 0; + + delete highlightPosAnimator; + delete highlightSizeAnimator; + highlightPosAnimator = 0; + highlightSizeAnimator = 0; + + changed = true; + } + + if (currentItem) { + QQuickItem *item = createHighlightItem(); + if (item) { + FxListItemSG *newHighlight = new FxListItemSG(item, q, true); + + if (autoHighlight) { + newHighlight->setSize(static_cast(currentItem)->itemSize()); + newHighlight->setPosition(static_cast(currentItem)->itemPosition()); + } + const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x"); + highlightPosAnimator = new QSmoothedAnimation(q); + highlightPosAnimator->target = QDeclarativeProperty(item, posProp); + highlightPosAnimator->velocity = highlightMoveSpeed; + highlightPosAnimator->userDuration = highlightMoveDuration; + + const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width"); + highlightSizeAnimator = new QSmoothedAnimation(q); + highlightSizeAnimator->velocity = highlightResizeSpeed; + highlightSizeAnimator->userDuration = highlightResizeDuration; + highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp); + + highlight = newHighlight; + changed = true; + } + } + if (changed) + emit q->highlightItemChanged(); +} + +void QQuickListViewPrivate::updateHighlight() +{ + applyPendingChanges(); + + if ((!currentItem && highlight) || (currentItem && !highlight)) + createHighlight(); + bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange; + if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) { + // auto-update highlight + FxListItemSG *listItem = static_cast(currentItem); + highlightPosAnimator->to = isRightToLeft() + ? -listItem->itemPosition()-listItem->itemSize() + : listItem->itemPosition(); + highlightSizeAnimator->to = listItem->itemSize(); + if (orient == QQuickListView::Vertical) { + if (highlight->item->width() == 0) + highlight->item->setWidth(currentItem->item->width()); + } else { + if (highlight->item->height() == 0) + highlight->item->setHeight(currentItem->item->height()); + } + + highlightPosAnimator->restart(); + highlightSizeAnimator->restart(); + } + updateTrackedItem(); +} + +void QQuickListViewPrivate::resetHighlightPosition() +{ + if (highlight && currentItem) + static_cast(highlight)->setPosition(static_cast(currentItem)->itemPosition()); +} + +QQuickItem * QQuickListViewPrivate::getSectionItem(const QString §ion) +{ + Q_Q(QQuickListView); + QQuickItem *sectionItem = 0; + int i = sectionCacheSize-1; + while (i >= 0 && !sectionCache[i]) + --i; + if (i >= 0) { + sectionItem = sectionCache[i]; + sectionCache[i] = 0; + sectionItem->setVisible(true); + QDeclarativeContext *context = QDeclarativeEngine::contextForObject(sectionItem)->parentContext(); + context->setContextProperty(QLatin1String("section"), section); + } else { + QDeclarativeContext *creationContext = sectionCriteria->delegate()->creationContext(); + QDeclarativeContext *context = new QDeclarativeContext( + creationContext ? creationContext : qmlContext(q)); + context->setContextProperty(QLatin1String("section"), section); + QObject *nobj = sectionCriteria->delegate()->beginCreate(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + sectionItem = qobject_cast(nobj); + if (!sectionItem) { + delete nobj; + } else { + sectionItem->setZ(2); + QDeclarative_setParent_noEvent(sectionItem, contentItem); + sectionItem->setParentItem(contentItem); + } + } else { + delete context; + } + sectionCriteria->delegate()->completeCreate(); + } + + return sectionItem; +} + +void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item) +{ + int i = 0; + do { + if (!sectionCache[i]) { + sectionCache[i] = item; + sectionCache[i]->setVisible(false); + return; + } + ++i; + } while (i < sectionCacheSize); + delete item; +} + +void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem) +{ + if (!sectionCriteria || !sectionCriteria->delegate()) + return; + if (listItem->attached->m_prevSection != listItem->attached->m_section + && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels + || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) { + if (!listItem->section) { + qreal pos = listItem->position(); + listItem->section = getSectionItem(listItem->attached->m_section); + listItem->setPosition(pos); + } else { + QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext(); + context->setContextProperty(QLatin1String("section"), listItem->attached->m_section); + } + } else if (listItem->section) { + qreal pos = listItem->position(); + releaseSectionItem(listItem->section); + listItem->section = 0; + listItem->setPosition(pos); + } +} + +void QQuickListViewPrivate::updateStickySections() +{ + if (!sectionCriteria || visibleItems.isEmpty() + || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem)) + return; + + bool isRtl = isRightToLeft(); + qreal viewPos = isRightToLeft() ? -position()-size() : position(); + QQuickItem *sectionItem = 0; + QQuickItem *lastSectionItem = 0; + int index = 0; + while (index < visibleItems.count()) { + if (QQuickItem *section = static_cast(visibleItems.at(index))->section) { + // Find the current section header and last visible section header + // and hide them if they will overlap a static section header. + qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x(); + qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width(); + bool visTop = true; + if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart) + visTop = isRtl ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos; + bool visBot = true; + if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) + visBot = isRtl ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size(); + section->setVisible(visBot && visTop); + if (visTop && !sectionItem) + sectionItem = section; + if (isRtl) { + if (-sectionPos <= viewPos + size()) + lastSectionItem = section; + } else { + if (sectionPos + sectionSize < viewPos + size()) + lastSectionItem = section; + } + } + ++index; + } + + // Current section header + if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart) { + if (!currentSectionItem) { + currentSectionItem = getSectionItem(currentSection); + } else if (currentStickySection != currentSection) { + QDeclarativeContext *context = QDeclarativeEngine::contextForObject(currentSectionItem)->parentContext(); + context->setContextProperty(QLatin1String("section"), currentSection); + } + currentStickySection = currentSection; + if (!currentSectionItem) + return; + + qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width(); + bool atBeginning = orient == QQuickListView::Vertical ? vData.atBeginning : (isRightToLeft() ? hData.atEnd : hData.atBeginning); + currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos)); + qreal pos = isRtl ? position() + size() - sectionSize : viewPos; + if (sectionItem) { + qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x(); + pos = isRtl ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize); + } + if (header) + pos = isRtl ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos); + if (footer) + pos = isRtl ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos); + if (orient == QQuickListView::Vertical) + currentSectionItem->setY(pos); + else + currentSectionItem->setX(pos); + } else if (currentSectionItem) { + releaseSectionItem(currentSectionItem); + currentSectionItem = 0; + } + + // Next section footer + if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) { + if (!nextSectionItem) { + nextSectionItem = getSectionItem(nextSection); + } else if (nextStickySection != nextSection) { + QDeclarativeContext *context = QDeclarativeEngine::contextForObject(nextSectionItem)->parentContext(); + context->setContextProperty(QLatin1String("section"), nextSection); + } + nextStickySection = nextSection; + if (!nextSectionItem) + return; + + qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width(); + nextSectionItem->setVisible(!nextSection.isEmpty()); + qreal pos = isRtl ? position() : viewPos + size() - sectionSize; + if (lastSectionItem) { + qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x(); + pos = isRtl ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize); + } + if (header) + pos = isRtl ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos); + if (orient == QQuickListView::Vertical) + nextSectionItem->setY(pos); + else + nextSectionItem->setX(pos); + } else if (nextSectionItem) { + releaseSectionItem(nextSectionItem); + nextSectionItem = 0; + } +} + +void QQuickListViewPrivate::updateSections() +{ + Q_Q(QQuickListView); + if (!q->isComponentComplete()) + return; + + QQuickItemViewPrivate::updateSections(); + + if (sectionCriteria && !visibleItems.isEmpty()) { + QString prevSection; + if (visibleIndex > 0) + prevSection = sectionAt(visibleIndex-1); + QQuickListViewAttached *prevAtt = 0; + int idx = -1; + for (int i = 0; i < visibleItems.count(); ++i) { + QQuickListViewAttached *attached = static_cast(visibleItems.at(i)->attached); + attached->setPrevSection(prevSection); + if (visibleItems.at(i)->index != -1) { + QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property()); + attached->setSection(sectionCriteria->sectionString(propValue)); + idx = visibleItems.at(i)->index; + } + updateInlineSection(static_cast(visibleItems.at(i))); + if (prevAtt) + prevAtt->setNextSection(attached->section()); + prevSection = attached->section(); + prevAtt = attached; + } + if (prevAtt) { + if (idx > 0 && idx < model->count()-1) + prevAtt->setNextSection(sectionAt(idx+1)); + else + prevAtt->setNextSection(QString()); + } + } + + lastVisibleSection = QString(); + updateCurrentSection(); + updateStickySections(); +} + +void QQuickListViewPrivate::updateCurrentSection() +{ + Q_Q(QQuickListView); + if (!sectionCriteria || visibleItems.isEmpty()) { + if (!currentSection.isEmpty()) { + currentSection.clear(); + emit q->currentSectionChanged(); + } + return; + } + bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels; + qreal sectionThreshold = position(); + if (currentSectionItem && !inlineSections) + sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width(); + int index = 0; + int modelIndex = visibleIndex; + while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) { + if (visibleItems.at(index)->index != -1) + modelIndex = visibleItems.at(index)->index; + ++index; + } + + QString newSection = currentSection; + if (index < visibleItems.count()) + newSection = visibleItems.at(index)->attached->section(); + else + newSection = (*visibleItems.constBegin())->attached->section(); + if (newSection != currentSection) { + currentSection = newSection; + updateStickySections(); + emit q->currentSectionChanged(); + } + + if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) { + // Don't want to scan for next section on every movement, so remember + // the last section in the visible area and only scan for the next + // section when that changes. Clearing lastVisibleSection will also + // force searching. + QString lastSection = currentSection; + qreal endPos = isRightToLeft() ? -position() : position() + size(); + if (nextSectionItem && !inlineSections) + endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width(); + while (index < visibleItems.count() && static_cast(visibleItems.at(index))->itemPosition() < endPos) { + if (visibleItems.at(index)->index != -1) + modelIndex = visibleItems.at(index)->index; + lastSection = visibleItems.at(index)->attached->section(); + ++index; + } + + if (lastVisibleSection != lastSection) { + nextSection = QString(); + lastVisibleSection = lastSection; + for (int i = modelIndex; i < itemCount; ++i) { + QString section = sectionAt(i); + if (section != lastSection) { + nextSection = section; + updateStickySections(); + break; + } + } + } + } +} + +void QQuickListViewPrivate::initializeCurrentItem() +{ + QQuickItemViewPrivate::initializeCurrentItem(); + + if (currentItem) { + FxListItemSG *listItem = static_cast(currentItem); + + if (currentIndex == visibleIndex - 1 && visibleItems.count()) { + // We can calculate exact postion in this case + listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); + } else { + // Create current item now and position as best we can. + // Its position will be corrected when it becomes visible. + listItem->setPosition(positionAt(currentIndex)); + } + + // Avoid showing section delegate twice. We still need the section heading so that + // currentItem positioning works correctly. + // This is slightly sub-optimal, but section heading caching minimizes the impact. + if (listItem->section) + listItem->section->setVisible(false); + + if (visibleItems.isEmpty()) + averageSize = listItem->size(); + } +} + +void QQuickListViewPrivate::updateAverage() +{ + if (!visibleItems.count()) + return; + qreal sum = 0.0; + for (int i = 0; i < visibleItems.count(); ++i) + sum += visibleItems.at(i)->size(); + averageSize = qRound(sum / visibleItems.count()); +} + +qreal QQuickListViewPrivate::headerSize() const +{ + return header ? header->size() : 0.0; +} + +qreal QQuickListViewPrivate::footerSize() const +{ + return footer ? footer->size() : 0.0; +} + +bool QQuickListViewPrivate::showHeaderForIndex(int index) const +{ + return index == 0; +} + +bool QQuickListViewPrivate::showFooterForIndex(int index) const +{ + return index == model->count()-1; +} + +void QQuickListViewPrivate::updateFooter() +{ + Q_Q(QQuickListView); + bool created = false; + if (!footer) { + QQuickItem *item = createComponentItem(footerComponent, true); + if (!item) + return; + item->setZ(1); + footer = new FxListItemSG(item, q, true); + created = true; + } + + FxListItemSG *listItem = static_cast(footer); + if (visibleItems.count()) { + qreal endPos = lastPosition(); + if (findLastVisibleIndex() == model->count()-1) { + listItem->setPosition(endPos); + } else { + qreal visiblePos = position() + q->height(); + if (endPos <= visiblePos || listItem->position() < endPos) + listItem->setPosition(endPos); + } + } else { + listItem->setPosition(visiblePos); + } + + if (created) + emit q->footerItemChanged(); +} + +void QQuickListViewPrivate::updateHeader() +{ + Q_Q(QQuickListView); + bool created = false; + if (!header) { + QQuickItem *item = createComponentItem(headerComponent, true); + if (!item) + return; + item->setZ(1); + header = new FxListItemSG(item, q, true); + created = true; + } + + FxListItemSG *listItem = static_cast(header); + if (listItem) { + if (visibleItems.count()) { + qreal startPos = originPosition(); + if (visibleIndex == 0) { + listItem->setPosition(startPos - headerSize()); + } else { + if (position() <= startPos || listItem->position() > startPos - headerSize()) + listItem->setPosition(startPos - headerSize()); + } + } else { + listItem->setPosition(-headerSize()); + } + } + + if (created) + emit q->headerItemChanged(); +} + +void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QQuickListView); + QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + if (item != contentItem && (!highlight || item != highlight->item)) { + if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height()) + || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) { + forceLayout = true; + q->polish(); + } + } +} + +void QQuickListViewPrivate::fixupPosition() +{ + if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange) + || snapMode != QQuickListView::NoSnap) + moveReason = Other; + if (orient == QQuickListView::Vertical) + fixupY(); + else + fixupX(); +} + +void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((orient == QQuickListView::Horizontal && &data == &vData) + || (orient == QQuickListView::Vertical && &data == &hData)) + return; + + correctFlick = false; + fixupMode = moveReason == Mouse ? fixupMode : Immediate; + bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange; + + qreal viewPos = isRightToLeft() ? -position()-size() : position(); + + if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) { + qreal tempPosition = isRightToLeft() ? -position()-size() : position(); + if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = 0; + if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2) + bias = averageSize/2; + else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2) + bias = -averageSize/2; + if (isRightToLeft()) + bias = -bias; + tempPosition -= bias; + } + FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); + if (!topItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + topItem = currentItem; + } + FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); + if (!bottomItem && strictHighlightRange && currentItem) { + // StrictlyEnforceRange always keeps an item in range + updateHighlight(); + bottomItem = currentItem; + } + qreal pos; + bool isInBounds = -position() > maxExtent && -position() <= minExtent; + if (topItem && (isInBounds || strictHighlightRange)) { + if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) { + pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart; + } else { + if (isRightToLeft()) + pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); + } + } else if (bottomItem && isInBounds) { + if (isRightToLeft()) + pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent); + } else { + QQuickItemViewPrivate::fixup(data, minExtent, maxExtent); + return; + } + + qreal dist = qAbs(data.move + pos); + if (dist > 0) { + timeline.reset(data.move); + if (fixupMode != Immediate) { + timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -pos); + } + vTime = timeline.time(); + } + } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) { + updateHighlight(); + qreal pos = static_cast(currentItem)->itemPosition(); + if (viewPos < pos + static_cast(currentItem)->itemSize() - highlightRangeEnd) + viewPos = pos + static_cast(currentItem)->itemSize() - highlightRangeEnd; + if (viewPos > pos - highlightRangeStart) + viewPos = pos - highlightRangeStart; + if (isRightToLeft()) + viewPos = -viewPos-size(); + + timeline.reset(data.move); + if (viewPos != position()) { + if (fixupMode != Immediate) { + timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); + data.fixingUp = true; + } else { + timeline.set(data.move, -viewPos); + } + } + vTime = timeline.time(); + } else { + QQuickItemViewPrivate::fixup(data, minExtent, maxExtent); + } + data.inOvershoot = false; + fixupMode = Normal; +} + +void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QQuickListView); + + data.fixingUp = false; + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) { + correctFlick = true; + QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); + return; + } + qreal maxDistance = 0; + qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value(); + + // -ve velocity means list is moving up/left + if (velocity > 0) { + if (data.move.value() < minExtent) { + if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = dist < averageSize/2 ? averageSize/2 : 0; + if (isRightToLeft()) + bias = -bias; + data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart; + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = maxVelocity; + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) { + // if we've been dragged < averageSize/2 then bias towards the next item + qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); + qreal bias = -dist < averageSize/2 ? averageSize/2 : 0; + if (isRightToLeft()) + bias = -bias; + data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart; + maxDistance = qAbs(data.flickTarget - data.move.value()); + velocity = -maxVelocity; + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds; + if (maxDistance > 0 || overShoot) { + // These modes require the list to stop exactly on an item boundary. + // The initial flick will estimate the boundary to stop on. + // Since list items can have variable sizes, the boundary will be + // reevaluated and adjusted as we approach the boundary. + qreal v = velocity; + if (maxVelocity != -1 && maxVelocity < qAbs(v)) { + if (v < 0) + v = -maxVelocity; + else + v = maxVelocity; + } + if (!hData.flicking && !vData.flicking) { + // the initial flick - estimate boundary + qreal accel = deceleration; + qreal v2 = v * v; + overshootDist = 0.0; + // + averageSize/4 to encourage moving at least one item in the flick direction + qreal dist = v2 / (accel * 2.0) + averageSize/4; + if (maxDistance > 0) + dist = qMin(dist, maxDistance); + if (v > 0) + dist = -dist; + if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) { + if (snapMode != QQuickListView::SnapOneItem) { + qreal distTemp = isRightToLeft() ? -dist : dist; + data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart; + } + data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; + if (overShoot) { + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget -= overshootDist; + } + } + qreal adjDist = -data.flickTarget + data.move.value(); + if (qAbs(adjDist) > qAbs(dist)) { + // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration + qreal adjv2 = accel * 2.0f * qAbs(adjDist); + if (adjv2 > v2) { + v2 = adjv2; + v = qSqrt(v2); + if (dist > 0) + v = -v; + } + } + dist = adjDist; + accel = v2 / (2.0f * qAbs(dist)); + } else if (overShoot) { + data.flickTarget = data.move.value() - dist; + if (data.flickTarget >= minExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget += overshootDist; + } else if (data.flickTarget <= maxExtent) { + overshootDist = overShootDistance(vSize); + data.flickTarget -= overshootDist; + } + } + timeline.reset(data.move); + timeline.accel(data.move, v, accel, maxDistance + overshootDist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + if (!hData.flicking && q->xflick()) { + hData.flicking = true; + emit q->flickingChanged(); + emit q->flickingHorizontallyChanged(); + emit q->flickStarted(); + } + if (!vData.flicking && q->yflick()) { + vData.flicking = true; + emit q->flickingChanged(); + emit q->flickingVerticallyChanged(); + emit q->flickStarted(); + } + correctFlick = true; + } else { + // reevaluate the target boundary. + qreal newtarget = data.flickTarget; + if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) { + qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; + newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart; + newtarget = isRightToLeft() ? -newtarget+size() : newtarget; + } + if (velocity < 0 && newtarget <= maxExtent) + newtarget = maxExtent - overshootDist; + else if (velocity > 0 && newtarget >= minExtent) + newtarget = minExtent + overshootDist; + if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do + if (qAbs(velocity) < MinimumFlickVelocity) + correctFlick = false; + return; + } + data.flickTarget = newtarget; + qreal dist = -newtarget + data.move.value(); + if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) { + correctFlick = false; + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + return; + } + timeline.reset(data.move); + timeline.accelDistance(data.move, v, -dist); + timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); + } + } else { + correctFlick = false; + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + +//---------------------------------------------------------------------------- + +/*! + \qmlclass ListView QQuickListView + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \inherits Flickable + \brief The ListView item provides a list view of items provided by a model. + + A ListView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + A ListView has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. Items in a + ListView are laid out horizontally or vertically. List views are inherently + flickable because ListView inherits from \l Flickable. + + \section1 Example Usage + + The following example shows the definition of a simple list model defined + in a file called \c ContactModel.qml: + + \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0 + + Another component can display this model data in a ListView, like this: + + \snippet doc/src/snippets/declarative/listview/listview.qml import + \codeline + \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple + + \image listview-simple.png + + Here, the ListView creates a \c ContactModel component for its model, and a \l Text element + for its delegate. The view will create a new \l Text component for each item in the model. Notice + the delegate is able to access the model's \c name and \c number data directly. + + An improved list view is shown below. The delegate is visually improved and is moved + into a separate \c contactDelegate component. + + \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced + \image listview-highlight.png + + The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, + and \c focus is set to \c true to enable keyboard navigation for the list view. + The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + ListView attaches a number of properties to the root item of the delegate, for example + \c {ListView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c ListView.isCurrentItem, while the child + \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem. + + \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem + + \note Views do not enable \e clip automatically. If the view + is not clipped by another item or the screen, it will be necessary + to set \e {clip: true} in order to have the out of view items clipped + nicely. + + \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples} +*/ +QQuickListView::QQuickListView(QQuickItem *parent) + : QQuickItemView(*(new QQuickListViewPrivate), parent) +{ +} + +QQuickListView::~QQuickListView() +{ +} + +/*! + \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current item, for example: + + \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem +*/ + +/*! + \qmlattachedproperty ListView QtQuick2::ListView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty string QtQuick2::ListView::previousSection + This attached property holds the section of the previous element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty string QtQuick2::ListView::nextSection + This attached property holds the section of the next element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty string QtQuick2::ListView::section + This attached property holds the section of this element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty bool QtQuick2::ListView::delayRemove + This attached property holds whether the delegate may be destroyed. + + It is attached to each instance of the delegate. + + It is sometimes necessary to delay the destruction of an item + until an animation completes. + + The example delegate below ensures that the animation completes before + the item is removed from the list. + + \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove +*/ + +/*! + \qmlattachedsignal QtQuick2::ListView::onAdd() + This attached handler is called immediately after an item is added to the view. +*/ + +/*! + \qmlattachedsignal QtQuick2::ListView::onRemove() + This attached handler is called immediately before an item is removed from the view. +*/ + +/*! + \qmlproperty model QtQuick2::ListView::model + This property holds the model providing data for the list. + + The model provides the set of data that is used to create the items + in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel + or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is + used, it must be a subclass of \l QAbstractItemModel or a simple list. + + \sa {qmlmodels}{Data Models} +*/ + +/*! + \qmlproperty Component QtQuick2::ListView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + The ListView will lay out the items based on the size of the root item + in the delegate. + + It is recommended that the delagate's size be a whole number to avoid sub-pixel + alignment of items. + + \note Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. +*/ +/*! + \qmlproperty int QtQuick2::ListView::currentIndex + \qmlproperty Item QtQuick2::ListView::currentItem + + The \c currentIndex property holds the index of the current item, and + \c currentItem holds the current item. Setting the currentIndex to -1 + will clear the highlight and set currentItem to null. + + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the ListView so that the current + item becomes visible. + + Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ + +/*! + \qmlproperty Item QtQuick2::ListView::highlightItem + + This holds the highlight item created from the \l highlight component. + + The \c highlightItem is managed by the view unless + \l highlightFollowsCurrentItem is set to false. + + \sa highlight, highlightFollowsCurrentItem +*/ + +/*! + \qmlproperty int QtQuick2::ListView::count + This property holds the number of items in the view. +*/ + +/*! + \qmlproperty Component QtQuick2::ListView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component is created for each list. + The geometry of the resulting component instance is managed by the list + so as to stay with the current item, unless the highlightFollowsCurrentItem + property is false. + + \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples} +*/ + +/*! + \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem + This property holds whether the highlight is managed by the view. + + If this property is true (the default value), the highlight is moved smoothly + to follow the current item. Otherwise, the + highlight is not moved by the view, and any movement must be implemented + by the highlight. + + Here is a highlight with its motion defined by a \l {SpringAnimation} item: + + \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem + + Note that the highlight animation also affects the way that the view + is scrolled. This is because the view moves to maintain the + highlight within the preferred highlight range (or visible viewport). + + \sa highlight, highlightMoveSpeed +*/ +//###Possibly rename these properties, since they are very useful even without a highlight? +/*! + \qmlproperty real QtQuick2::ListView::preferredHighlightBegin + \qmlproperty real QtQuick2::ListView::preferredHighlightEnd + \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode + + These properties define the preferred range of the highlight (for the current item) + within the view. The \c preferredHighlightBegin value must be less than the + \c preferredHighlightEnd value. + + These properties affect the position of the current item when the list is scrolled. + For example, if the currently selected item should stay in the middle of the + list when the view is scrolled, set the \c preferredHighlightBegin and + \c preferredHighlightEnd values to the top and bottom coordinates of where the middle + item would be. If the \c currentItem is changed programmatically, the list will + automatically scroll so that the current item is in the middle of the view. + Furthermore, the behavior of the current item index will occur whether or not a + highlight exists. + + Valid values for \c highlightRangeMode are: + + \list + \o ListView.ApplyRange - the view attempts to maintain the highlight within the range. + However, the highlight can move outside of the range at the ends of the list or due + to mouse interaction. + \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range. + The current item changes if a keyboard or mouse action would cause the highlight to move + outside of the range. + \o ListView.NoHighlightRange - this is the default value. + \endlist +*/ +void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QQuickListView); + if (d->autoHighlight != autoHighlight) { + if (!autoHighlight) { + if (d->highlightPosAnimator) + d->highlightPosAnimator->stop(); + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->stop(); + } + QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight); + } +} + +/*! + \qmlproperty real QtQuick2::ListView::spacing + + This property holds the spacing between items. + + The default value is 0. +*/ +qreal QQuickListView::spacing() const +{ + Q_D(const QQuickListView); + return d->spacing; +} + +void QQuickListView::setSpacing(qreal spacing) +{ + Q_D(QQuickListView); + if (spacing != d->spacing) { + d->spacing = spacing; + d->forceLayout = true; + d->layout(); + emit spacingChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick2::ListView::orientation + This property holds the orientation of the list. + + Possible values: + + \list + \o ListView.Horizontal - Items are laid out horizontally + \o ListView.Vertical (default) - Items are laid out vertically + \endlist + + \table + \row + \o Horizontal orientation: + \image ListViewHorizontal.png + + \row + \o Vertical orientation: + \image listview-highlight.png + \endtable +*/ +QQuickListView::Orientation QQuickListView::orientation() const +{ + Q_D(const QQuickListView); + return d->orient; +} + +void QQuickListView::setOrientation(QQuickListView::Orientation orientation) +{ + Q_D(QQuickListView); + if (d->orient != orientation) { + d->orient = orientation; + if (d->orient == Vertical) { + setContentWidth(-1); + setFlickableDirection(VerticalFlick); + setContentX(0); + } else { + setContentHeight(-1); + setFlickableDirection(HorizontalFlick); + setContentY(0); + } + d->regenerate(); + emit orientationChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick2::ListView::layoutDirection + This property holds the layout direction of the horizontal list. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items will be laid out from left to right. + \o Qt.RightToLeft - Items will be laid out from right to let. + \endlist + + \sa ListView::effectiveLayoutDirection +*/ + + +/*! + \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection + This property holds the effective layout direction of the horizontal list. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the horizontal list will be mirrored. However, the + property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged. + + \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +/*! + \qmlproperty bool QtQuick2::ListView::keyNavigationWraps + This property holds whether the list wraps key navigation. + + If this is true, key navigation that would move the current item selection + past the end of the list instead wraps around and moves the selection to + the start of the list, and vice-versa. + + By default, key navigation is not wrapped. +*/ + + +/*! + \qmlproperty int QtQuick2::ListView::cacheBuffer + This property determines whether delegates are retained outside the + visible area of the view. + + If this value is non-zero, the view keeps as many delegates + instantiated as it can fit within the buffer specified. For example, + if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is + set to 40, then up to 2 delegates above and 2 delegates below the visible + area may be retained. + + Note that cacheBuffer is not a pixel buffer - it only maintains additional + instantiated delegates. + + Setting this value can improve the smoothness of scrolling behavior at the expense + of additional memory usage. It is not a substitute for creating efficient + delegates; the fewer elements in a delegate, the faster a view can be + scrolled. +*/ + + +/*! + \qmlproperty string QtQuick2::ListView::section.property + \qmlproperty enumeration QtQuick2::ListView::section.criteria + \qmlproperty Component QtQuick2::ListView::section.delegate + \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning + + These properties determine the expression to be evaluated and appearance + of the section labels. + + \c section.property holds the name of the property that is the basis + of each section. + + \c section.criteria holds the criteria for forming each section based on + \c section.property. This value can be one of: + + \list + \o ViewSection.FullString (default) - sections are created based on the + \c section.property value. + \o ViewSection.FirstCharacter - sections are created based on the first + character of the \c section.property value (for example, 'A', 'B', 'C' + sections, etc. for an address book) + \endlist + + \c section.delegate holds the delegate component for each section. + + \c section.labelPositioning determines whether the current and/or + next section labels stick to the start/end of the view, and whether + the labels are shown inline. This value can be a combination of: + + \list + \o ViewSection.InlineLabels - section labels are shown inline between + the item delegates separating sections (default). + \o ViewSection.CurrentLabelAtStart - the current section label sticks to the + start of the view as it is moved. + \o ViewSection.NextLabelAtEnd - the next section label (beyond all visible + sections) sticks to the end of the view as it is moved. \note Enabling + \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next + section, which has performance implications, especially for slower models. + \endlist + + Each item in the list has attached properties named \c ListView.section, + \c ListView.previousSection and \c ListView.nextSection. + + For example, here is a ListView that displays a list of animals, separated + into sections. Each item in the ListView is placed in a different section + depending on the "size" property of the model item. The \c sectionHeading + delegate component provides the light blue bar that marks the beginning of + each section. + + + \snippet examples/declarative/modelviews/listview/sections.qml 0 + + \image qml-listview-sections-example.png + + \note Adding sections to a ListView does not automatically re-order the + list items by the section criteria. + If the model is not ordered by section, then it is possible that + the sections created will not be unique; each boundary between + differing sections will result in a section header being created + even if that section exists elsewhere. + + \sa {declarative/modelviews/listview}{ListView examples} +*/ +QQuickViewSection *QQuickListView::sectionCriteria() +{ + Q_D(QQuickListView); + if (!d->sectionCriteria) { + d->sectionCriteria = new QQuickViewSection(this); + connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections())); + } + return d->sectionCriteria; +} + +/*! + \qmlproperty string QtQuick2::ListView::currentSection + This property holds the section that is currently at the beginning of the view. +*/ +QString QQuickListView::currentSection() const +{ + Q_D(const QQuickListView); + return d->currentSection; +} + +/*! + \qmlproperty real QtQuick2::ListView::highlightMoveSpeed + \qmlproperty int QtQuick2::ListView::highlightMoveDuration + \qmlproperty real QtQuick2::ListView::highlightResizeSpeed + \qmlproperty int QtQuick2::ListView::highlightResizeDuration + + These properties hold the move and resize animation speed of the highlight delegate. + + \l highlightFollowsCurrentItem must be true for these properties + to have effect. + + The default value for the speed properties is 400 pixels/second. + The default value for the duration properties is -1, i.e. the + highlight will take as much time as necessary to move at the set speed. + + These properties have the same characteristics as a SmoothedAnimation. + + \sa highlightFollowsCurrentItem +*/ +qreal QQuickListView::highlightMoveSpeed() const +{ + Q_D(const QQuickListView); + return d->highlightMoveSpeed; +} + +void QQuickListView::setHighlightMoveSpeed(qreal speed) +{ + Q_D(QQuickListView); + if (d->highlightMoveSpeed != speed) { + d->highlightMoveSpeed = speed; + if (d->highlightPosAnimator) + d->highlightPosAnimator->velocity = d->highlightMoveSpeed; + emit highlightMoveSpeedChanged(); + } +} + +void QQuickListView::setHighlightMoveDuration(int duration) +{ + Q_D(QQuickListView); + if (d->highlightMoveDuration != duration) { + if (d->highlightPosAnimator) + d->highlightPosAnimator->userDuration = duration; + QQuickItemView::setHighlightMoveDuration(duration); + } +} + +qreal QQuickListView::highlightResizeSpeed() const +{ + Q_D(const QQuickListView); + return d->highlightResizeSpeed; +} + +void QQuickListView::setHighlightResizeSpeed(qreal speed) +{ + Q_D(QQuickListView); + if (d->highlightResizeSpeed != speed) { + d->highlightResizeSpeed = speed; + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->velocity = d->highlightResizeSpeed; + emit highlightResizeSpeedChanged(); + } +} + +int QQuickListView::highlightResizeDuration() const +{ + Q_D(const QQuickListView); + return d->highlightResizeDuration; +} + +void QQuickListView::setHighlightResizeDuration(int duration) +{ + Q_D(QQuickListView); + if (d->highlightResizeDuration != duration) { + d->highlightResizeDuration = duration; + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->userDuration = d->highlightResizeDuration; + emit highlightResizeDurationChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick2::ListView::snapMode + + This property determines how the view scrolling will settle following a drag or flick. + The possible values are: + + \list + \o ListView.NoSnap (default) - the view stops anywhere within the visible area. + \o ListView.SnapToItem - the view settles with an item aligned with the start of + the view. + \o ListView.SnapOneItem - the view settles no more than one item away from the first + visible item at the time the mouse button is released. This mode is particularly + useful for moving one page at a time. + \endlist + + \c snapMode does not affect the \l currentIndex. To update the + \l currentIndex as the list is moved, set \l highlightRangeMode + to \c ListView.StrictlyEnforceRange. + + \sa highlightRangeMode +*/ +QQuickListView::SnapMode QQuickListView::snapMode() const +{ + Q_D(const QQuickListView); + return d->snapMode; +} + +void QQuickListView::setSnapMode(SnapMode mode) +{ + Q_D(QQuickListView); + if (d->snapMode != mode) { + d->snapMode = mode; + emit snapModeChanged(); + } +} + + +/*! + \qmlproperty Component QtQuick2::ListView::footer + This property holds the component to use as the footer. + + An instance of the footer component is created for each view. The + footer is positioned at the end of the view, after any items. + + \sa header +*/ + + +/*! + \qmlproperty Component QtQuick2::ListView::header + This property holds the component to use as the header. + + An instance of the header component is created for each view. The + header is positioned at the beginning of the view, before any items. + + \sa footer +*/ + +void QQuickListView::viewportMoved() +{ + Q_D(QQuickListView); + QQuickItemView::viewportMoved(); + if (!d->itemCount) + return; + // Recursion can occur due to refill changing the content size. + if (d->inViewportMoved) + return; + d->inViewportMoved = true; + d->lazyRelease = true; + d->refill(); + if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) + d->moveReason = QQuickListViewPrivate::Mouse; + if (d->moveReason != QQuickListViewPrivate::SetIndex) { + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { + // reposition highlight + qreal pos = d->highlight->position(); + qreal viewPos = d->isRightToLeft() ? -d->position()-d->size() : d->position(); + if (pos > viewPos + d->highlightRangeEnd - d->highlight->size()) + pos = viewPos + d->highlightRangeEnd - d->highlight->size(); + if (pos < viewPos + d->highlightRangeStart) + pos = viewPos + d->highlightRangeStart; + if (pos != d->highlight->position()) { + d->highlightPosAnimator->stop(); + static_cast(d->highlight)->setPosition(pos); + } else { + d->updateHighlight(); + } + + // update current index + if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) { + if (snapItem->index >= 0 && snapItem->index != d->currentIndex) + d->updateCurrent(snapItem->index); + } + } + } + + if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) { + d->inFlickCorrection = true; + // Near an end and it seems that the extent has changed? + // Recalculate the flick so that we don't end up in an odd position. + if (yflick() && !d->vData.inOvershoot) { + if (d->vData.velocity > 0) { + const qreal minY = minYExtent(); + if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2) + && minY != d->vData.flickTarget) + d->flickY(-d->vData.smoothVelocity.value()); + d->bufferMode = QQuickListViewPrivate::BufferBefore; + } else if (d->vData.velocity < 0) { + const qreal maxY = maxYExtent(); + if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2) + && maxY != d->vData.flickTarget) + d->flickY(-d->vData.smoothVelocity.value()); + d->bufferMode = QQuickListViewPrivate::BufferAfter; + } + } + + if (xflick() && !d->hData.inOvershoot) { + if (d->hData.velocity > 0) { + const qreal minX = minXExtent(); + if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2) + && minX != d->hData.flickTarget) + d->flickX(-d->hData.smoothVelocity.value()); + d->bufferMode = d->isRightToLeft() ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore; + } else if (d->hData.velocity < 0) { + const qreal maxX = maxXExtent(); + if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2) + && maxX != d->hData.flickTarget) + d->flickX(-d->hData.smoothVelocity.value()); + d->bufferMode = d->isRightToLeft() ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter; + } + } + d->inFlickCorrection = false; + } + if (d->sectionCriteria) { + d->updateCurrentSection(); + d->updateStickySections(); + } + d->inViewportMoved = false; +} + +void QQuickListView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickListView); + if (d->model && d->model->count() && d->interactive) { + if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left) + || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right) + || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Up)) { + if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) { + decrementCurrentIndex(); + event->accept(); + return; + } else if (d->wrap) { + event->accept(); + return; + } + } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right) + || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left) + || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Down)) { + if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) { + incrementCurrentIndex(); + event->accept(); + return; + } else if (d->wrap) { + event->accept(); + return; + } + } + } + event->ignore(); + QQuickItemView::keyPressEvent(event); +} + +void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickListView); + if (d->isRightToLeft() && d->orient == QQuickListView::Horizontal) { + // maintain position relative to the right edge + int dx = newGeometry.width() - oldGeometry.width(); + setContentX(contentX() - dx); + } + QQuickItemView::geometryChanged(newGeometry, oldGeometry); +} + + +/*! + \qmlmethod QtQuick2::ListView::incrementCurrentIndex() + + Increments the current index. The current index will wrap + if keyNavigationWraps is true and it is currently at the end. + This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QQuickListView::incrementCurrentIndex() +{ + Q_D(QQuickListView); + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() < count - 1 || d->wrap)) { + d->moveReason = QQuickListViewPrivate::SetIndex; + int index = currentIndex()+1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } +} + +/*! + \qmlmethod QtQuick2::ListView::decrementCurrentIndex() + + Decrements the current index. The current index will wrap + if keyNavigationWraps is true and it is currently at the beginning. + This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QQuickListView::decrementCurrentIndex() +{ + Q_D(QQuickListView); + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() > 0 || d->wrap)) { + d->moveReason = QQuickListViewPrivate::SetIndex; + int index = currentIndex()-1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } +} + +void QQuickListView::updateSections() +{ + Q_D(QQuickListView); + if (isComponentComplete() && d->model) { + QList roles; + if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty()) + roles << d->sectionCriteria->property().toUtf8(); + d->model->setWatchedRoles(roles); + d->updateSections(); + if (d->itemCount) { + d->forceLayout = true; + d->layout(); + } + } +} + +bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult) +{ + Q_Q(QQuickListView); + + int modelIndex = change.index; + int count = change.count; + + + qreal tempPos = isRightToLeft() ? -position()-size() : position(); + int index = visibleItems.count() ? mapFromModel(modelIndex) : 0; + + if (index < 0) { + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + if (i == 0 && visibleItems.first()->index == -1) { + // there are no visible items except items marked for removal + index = visibleItems.count(); + } else if (visibleItems.at(i)->index + 1 == modelIndex + && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) { + // Special case of appending an item to the model. + index = visibleItems.count(); + } else { + if (modelIndex < visibleIndex) { + // Insert before visible items + visibleIndex += count; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index != -1 && item->index >= modelIndex) + item->index += count; + } + } + return true; + } + } + + // index can be the next item past the end of the visible items list (i.e. appended) + int pos = 0; + if (visibleItems.count()) { + pos = index < visibleItems.count() ? visibleItems.at(index)->position() + : visibleItems.last()->endPosition()+spacing; + } + + int prevAddedCount = insertResult->addedItems.count(); + if (firstVisible && pos < firstVisible->position()) { + // Insert items before the visible item. + int insertionIdx = index; + int i = 0; + int from = tempPos - buffer; + + for (i = count-1; i >= 0; --i) { + if (pos > from) { + insertResult->sizeAddedBeforeVisible += averageSize; + pos -= averageSize; + } else { + FxViewItem *item = 0; + if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { + if (item->index > modelIndex + i) + insertResult->movedBackwards.append(item); + item->index = modelIndex + i; + } + if (!item) + item = createItem(modelIndex + i); + + visibleItems.insert(insertionIdx, item); + if (!change.isMove()) { + insertResult->addedItems.append(item); + insertResult->sizeAddedBeforeVisible += item->size(); + } + pos -= item->size() + spacing; + } + index++; + } + } else { + int i = 0; + int to = buffer+tempPos+size(); + for (i = 0; i < count && pos <= to; ++i) { + FxViewItem *item = 0; + if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { + if (item->index > modelIndex + i) + insertResult->movedBackwards.append(item); + item->index = modelIndex + i; + } + if (!item) + item = createItem(modelIndex + i); + + visibleItems.insert(index, item); + if (!change.isMove()) + insertResult->addedItems.append(item); + pos += item->size() + spacing; + ++index; + } + } + + for (; index < visibleItems.count(); ++index) { + FxViewItem *item = visibleItems.at(index); + if (item->index != -1) + item->index += count; + } + + updateVisibleIndex(); + + return insertResult->addedItems.count() > prevAddedCount; +} + + +/*! + \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode) + + Positions the view such that the \a index is at the position specified by + \a mode: + + \list + \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view. + \o ListView.Center - position item in the center of the view. + \o ListView.End - position item at bottom (or right for horizontal orientation) of the view. + \o ListView.Visible - if any part of the item is visible then take no action, otherwise + bring the item into view. + \o ListView.Contain - ensure the entire item is visible. If the item is larger than + the view the item is positioned at the top (or left for horizontal orientation) of the view. + \endlist + + If positioning the view at \a index would cause empty space to be displayed at + the beginning or end of the view, the view will be positioned at the boundary. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + The correct way to bring an item into view is with \c positionViewAtIndex. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end: + + \code + Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning) + \endcode +*/ + +/*! + \qmlmethod QtQuick2::ListView::positionViewAtBeginning() + \qmlmethod QtQuick2::ListView::positionViewAtEnd() + + Positions the view at the beginning or end, taking into account any header or footer. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end on startup: + + \code + Component.onCompleted: positionViewAtEnd() + \endcode +*/ + +/*! + \qmlmethod int QtQuick2::ListView::indexAt(int x, int y) + + Returns the index of the visible item containing the point \a x, \a y in content + coordinates. If there is no item at the point specified, or the item is + not visible -1 is returned. + + If the item is outside the visible area, -1 is returned, regardless of + whether an item will exist at that point when scrolled into view. + + \bold Note: methods should only be called after the Component has completed. +*/ + +QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj) +{ + return new QQuickListViewAttached(obj); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquicklistview_p.h b/src/declarative/items/qquicklistview_p.h new file mode 100644 index 0000000000..0266e23cd5 --- /dev/null +++ b/src/declarative/items/qquicklistview_p.h @@ -0,0 +1,215 @@ +// Commit: 95814418f9d6adeba365c795462e8afb00138211 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLISTVIEW_P_H +#define QQUICKLISTVIEW_P_H + +#include "qquickitemview_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickListView; +class QQuickListViewPrivate; +class Q_AUTOTEST_EXPORT QQuickViewSection : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(SectionCriteria criteria READ criteria WRITE setCriteria NOTIFY criteriaChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int labelPositioning READ labelPositioning WRITE setLabelPositioning NOTIFY labelPositioningChanged) + Q_ENUMS(SectionCriteria) + Q_ENUMS(LabelPositioning) +public: + QQuickViewSection(QQuickListView *parent=0); + + QString property() const { return m_property; } + void setProperty(const QString &); + + enum SectionCriteria { FullString, FirstCharacter }; + SectionCriteria criteria() const { return m_criteria; } + void setCriteria(SectionCriteria); + + QDeclarativeComponent *delegate() const { return m_delegate; } + void setDelegate(QDeclarativeComponent *delegate); + + QString sectionString(const QString &value); + + enum LabelPositioning { InlineLabels = 0x01, CurrentLabelAtStart = 0x02, NextLabelAtEnd = 0x04 }; + int labelPositioning() { return m_labelPositioning; } + void setLabelPositioning(int pos); + +Q_SIGNALS: + void propertyChanged(); + void criteriaChanged(); + void delegateChanged(); + void labelPositioningChanged(); + +private: + QString m_property; + SectionCriteria m_criteria; + QDeclarativeComponent *m_delegate; + int m_labelPositioning; + QQuickListViewPrivate *m_view; +}; + + +class QQuickVisualModel; +class QQuickListViewAttached; +class Q_AUTOTEST_EXPORT QQuickListView : public QQuickItemView +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickListView) + + // XXX deprecate these two properties (only duration should be necessary) + Q_PROPERTY(qreal highlightMoveSpeed READ highlightMoveSpeed WRITE setHighlightMoveSpeed NOTIFY highlightMoveSpeedChanged) + Q_PROPERTY(qreal highlightResizeSpeed READ highlightResizeSpeed WRITE setHighlightResizeSpeed NOTIFY highlightResizeSpeedChanged) + + Q_PROPERTY(int highlightResizeDuration READ highlightResizeDuration WRITE setHighlightResizeDuration NOTIFY highlightResizeDurationChanged) + + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) + Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) + + Q_PROPERTY(QQuickViewSection *section READ sectionCriteria CONSTANT) + Q_PROPERTY(QString currentSection READ currentSection NOTIFY currentSectionChanged) + + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) + + Q_ENUMS(Orientation) + Q_ENUMS(SnapMode) + Q_CLASSINFO("DefaultProperty", "data") + +public: + QQuickListView(QQuickItem *parent=0); + ~QQuickListView(); + + qreal spacing() const; + void setSpacing(qreal spacing); + + enum Orientation { Horizontal = Qt::Horizontal, Vertical = Qt::Vertical }; + Orientation orientation() const; + void setOrientation(Orientation); + + QQuickViewSection *sectionCriteria(); + QString currentSection() const; + + virtual void setHighlightFollowsCurrentItem(bool); + + qreal highlightMoveSpeed() const; + void setHighlightMoveSpeed(qreal); + + qreal highlightResizeSpeed() const; + void setHighlightResizeSpeed(qreal); + + int highlightResizeDuration() const; + void setHighlightResizeDuration(int); + + virtual void setHighlightMoveDuration(int); + + enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + + static QQuickListViewAttached *qmlAttachedProperties(QObject *); + +public Q_SLOTS: + void incrementCurrentIndex(); + void decrementCurrentIndex(); + +Q_SIGNALS: + void spacingChanged(); + void orientationChanged(); + void currentSectionChanged(); + void highlightMoveSpeedChanged(); + void highlightResizeSpeedChanged(); + void highlightResizeDurationChanged(); + void snapModeChanged(); + +protected: + virtual void viewportMoved(); + virtual void keyPressEvent(QKeyEvent *); + virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry); + +protected Q_SLOTS: + void updateSections(); +}; + +class QQuickListViewAttached : public QQuickItemViewAttached +{ + Q_OBJECT + +public: + QQuickListViewAttached(QObject *parent) + : QQuickItemViewAttached(parent), m_view(0) {} + ~QQuickListViewAttached() {} + + Q_PROPERTY(QQuickListView *view READ view NOTIFY viewChanged) + QQuickListView *view() { return m_view; } + void setView(QQuickListView *view) { + if (view != m_view) { + m_view = view; + emit viewChanged(); + } + } + +Q_SIGNALS: + void viewChanged(); + +public: + QDeclarativeGuard m_view; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPEINFO(QQuickListView, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQuickListView) +QML_DECLARE_TYPE(QQuickViewSection) + +QT_END_HEADER + +#endif // QQUICKLISTVIEW_P_H diff --git a/src/declarative/items/qquickloader.cpp b/src/declarative/items/qquickloader.cpp new file mode 100644 index 0000000000..cef98a6f7c --- /dev/null +++ b/src/declarative/items/qquickloader.cpp @@ -0,0 +1,853 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickloader_p_p.h" + +#include + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +QQuickLoaderPrivate::QQuickLoaderPrivate() + : item(0), component(0), itemContext(0), incubator(0), updatingSize(false), + itemWidthValid(false), itemHeightValid(false), + active(true), loadingFromSource(false), asynchronous(false) +{ +} + +QQuickLoaderPrivate::~QQuickLoaderPrivate() +{ + delete incubator; + disposeInitialPropertyValues(); +} + +void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (resizeItem == item) { + if (!updatingSize && newGeometry.width() != oldGeometry.width()) + itemWidthValid = true; + if (!updatingSize && newGeometry.height() != oldGeometry.height()) + itemHeightValid = true; + _q_updateSize(false); + } + QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); +} + +void QQuickLoaderPrivate::clear() +{ + disposeInitialPropertyValues(); + + if (incubator) + incubator->clear(); + + if (loadingFromSource && component) { + component->deleteLater(); + component = 0; + } + source = QUrl(); + + if (item) { + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + p->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + + // We can't delete immediately because our item may have triggered + // the Loader to load a different item. + item->setParentItem(0); + item->setVisible(false); + item->deleteLater(); + item = 0; + } +} + +void QQuickLoaderPrivate::initResize() +{ + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + p->addItemChangeListener(this, QQuickItemPrivate::Geometry); + // We may override the item's size, so we need to remember + // whether the item provided its own valid size. + itemWidthValid = p->widthValid; + itemHeightValid = p->heightValid; + _q_updateSize(); +} + +/*! + \qmlclass Loader QQuickLoader + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \inherits Item + + \brief The Loader item allows dynamically loading an Item-based + subtree from a URL or Component. + + Loader is used to dynamically load visual QML components. It can load a + QML file (using the \l source property) or a \l Component object (using + the \l sourceComponent property). It is useful for delaying the creation + of a component until it is required: for example, when a component should + be created on demand, or when a component should not be created + unnecessarily for performance reasons. + + Here is a Loader that loads "Page1.qml" as a component when the + \l MouseArea is clicked: + + \snippet doc/src/snippets/declarative/loader/simple.qml 0 + + The loaded item can be accessed using the \l item property. + + If the \l source or \l sourceComponent changes, any previously instantiated + items are destroyed. Setting \l source to an empty string or setting + \l sourceComponent to \c undefined destroys the currently loaded item, + freeing resources and leaving the Loader empty. + + \section2 Loader sizing behavior + + Loader is like any other visual item and must be positioned and sized + accordingly to become visible. + + \list + \o If an explicit size is not specified for the Loader, the Loader + is automatically resized to the size of the loaded item once the + component is loaded. + \o If the size of the Loader is specified explicitly by setting + the width, height or by anchoring, the loaded item will be resized + to the size of the Loader. + \endlist + + In both scenarios the size of the item and the Loader are identical. + This ensures that anchoring to the Loader is equivalent to anchoring + to the loaded item. + + \table + \row + \o sizeloader.qml + \o sizeitem.qml + \row + \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0 + \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0 + \row + \o The red rectangle will be sized to the size of the root item. + \o The red rectangle will be 50x50, centered in the root item. + \endtable + + + \section2 Receiving signals from loaded items + + Any signals emitted from the loaded item can be received using the + \l Connections element. For example, the following \c application.qml + loads \c MyItem.qml, and is able to receive the \c message signal from + the loaded item through a \l Connections object: + + \table + \row + \o application.qml + \o MyItem.qml + \row + \o \snippet doc/src/snippets/declarative/loader/connections.qml 0 + \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0 + \endtable + + Alternatively, since \c MyItem.qml is loaded within the scope of the + Loader, it could also directly call any function defined in the Loader or + its parent \l Item. + + + \section2 Focus and key events + + Loader is a focus scope. Its \l {Item::}{focus} property must be set to + \c true for any of its children to get the \e {active focus}. (See + \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} + for more details.) Any key events received in the loaded item should likely + also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader. + + For example, the following \c application.qml loads \c KeyReader.qml when + the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is + set to \c true for the Loader as well as the \l Item in the dynamically + loaded object: + + \table + \row + \o application.qml + \o KeyReader.qml + \row + \o \snippet doc/src/snippets/declarative/loader/focus.qml 0 + \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0 + \endtable + + Once \c KeyReader.qml is loaded, it accepts key events and sets + \c event.accepted to \c true so that the event is not propagated to the + parent \l Rectangle. + + \sa {dynamic-object-creation}{Dynamic Object Creation} +*/ + +QQuickLoader::QQuickLoader(QQuickItem *parent) + : QQuickImplicitSizeItem(*(new QQuickLoaderPrivate), parent) +{ + setFlag(ItemIsFocusScope); +} + +QQuickLoader::~QQuickLoader() +{ + Q_D(QQuickLoader); + if (d->item) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->item); + p->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + } +} + +/*! + \qmlproperty bool QtQuick2::Loader::active + This property is \c true if the Loader is currently active. + The default value for the \l active property is \c true. + + If the Loader is inactive, changing the \l source or \l sourceComponent + will not cause the item to be instantiated until the Loader is made active. + + Setting the value to inactive will cause any \l item loaded by the loader + to be released, but will not affect the \l source or \l sourceComponent. + + The \l status of an inactive loader is always \c Null. + + \sa source, sourceComponent + */ +bool QQuickLoader::active() const +{ + Q_D(const QQuickLoader); + return d->active; +} + +void QQuickLoader::setActive(bool newVal) +{ + Q_D(QQuickLoader); + if (d->active != newVal) { + d->active = newVal; + if (newVal == true) { + if (d->loadingFromSource) { + loadFromSource(); + } else { + loadFromSourceComponent(); + } + } else { + if (d->item) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->item); + p->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + + // We can't delete immediately because our item may have triggered + // the Loader to load a different item. + d->item->setParentItem(0); + d->item->setVisible(false); + d->item->deleteLater(); + d->item = 0; + emit itemChanged(); + } + emit statusChanged(); + } + emit activeChanged(); + } +} + + +/*! + \qmlproperty url QtQuick2::Loader::source + This property holds the URL of the QML component to instantiate. + + Note the QML component must be an \l{Item}-based component. The loader + cannot load non-visual components. + + To unload the currently loaded item, set this property to an empty string, + or set \l sourceComponent to \c undefined. Setting \c source to a + new URL will also cause the item created by the previous URL to be unloaded. + + \sa sourceComponent, status, progress +*/ +QUrl QQuickLoader::source() const +{ + Q_D(const QQuickLoader); + return d->source; +} + +void QQuickLoader::setSource(const QUrl &url) +{ + setSource(url, true); // clear previous values +} + +void QQuickLoader::setSource(const QUrl &url, bool needsClear) +{ + Q_D(QQuickLoader); + if (d->source == url) + return; + + if (needsClear) + d->clear(); + + d->source = url; + d->loadingFromSource = true; + + if (d->active) + loadFromSource(); + else + emit sourceChanged(); +} + +void QQuickLoader::loadFromSource() +{ + Q_D(QQuickLoader); + if (d->source.isEmpty()) { + emit sourceChanged(); + emit statusChanged(); + emit progressChanged(); + emit itemChanged(); + return; + } + + if (isComponentComplete()) { + d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this); + d->load(); + } +} + +/*! + \qmlproperty Component QtQuick2::Loader::sourceComponent + This property holds the \l{Component} to instantiate. + + \qml + Item { + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { sourceComponent: redSquare } + Loader { sourceComponent: redSquare; x: 10 } + } + \endqml + + To unload the currently loaded item, set this property to an empty string + or \c undefined. + + \sa source, progress +*/ + +QDeclarativeComponent *QQuickLoader::sourceComponent() const +{ + Q_D(const QQuickLoader); + return d->component; +} + +void QQuickLoader::setSourceComponent(QDeclarativeComponent *comp) +{ + Q_D(QQuickLoader); + if (comp == d->component) + return; + + d->clear(); + + d->component = comp; + d->loadingFromSource = false; + + if (d->active) + loadFromSourceComponent(); + else + emit sourceComponentChanged(); +} + +void QQuickLoader::resetSourceComponent() +{ + setSourceComponent(0); +} + +void QQuickLoader::loadFromSourceComponent() +{ + Q_D(QQuickLoader); + if (!d->component) { + emit sourceComponentChanged(); + emit statusChanged(); + emit progressChanged(); + emit itemChanged(); + return; + } + + if (isComponentComplete()) + d->load(); +} + +/*! + \qmlmethod object QtQuick2::Loader::setSource(url source, object properties) + + Creates an object instance of the given \a source component that will have + the given \a properties. The \a properties argument is optional. The instance + will be accessible via the \l item property once loading and instantiation + is complete. + + If the \l active property is \c false at the time when this function is called, + the given \a source component will not be loaded but the \a source and initial + \a properties will be cached. When the loader is made \l active, an instance of + the \a source component will be created with the initial \a properties set. + + Setting the initial property values of an instance of a component in this manner + will \e not trigger any associated \l{Behavior}s. + + Note that the cached \a properties will be cleared if the \l source or \l sourceComponent + is changed after calling this function but prior to setting the loader \l active. + + Example: + \table + \row + \o + \qml + // ExampleComponent.qml + import QtQuick 2.0 + Rectangle { + id: rect + color: "red" + width: 10 + height: 10 + + Behavior on color { + NumberAnimation { + target: rect + property: "width" + to: (rect.width + 20) + duration: 0 + } + } + } + \endqml + \o + \qml + // example.qml + import QtQuick 2.0 + Item { + Loader { + id: squareLoader + onLoaded: console.log(squareLoader.item.width); // prints [10], not [30] + } + + Component.onCompleted: { + squareLoader.setSource("ExampleComponent.qml", { "color": "blue" }); + // will trigger the onLoaded code when complete. + } + } + \endqml + \endtable + + \sa source, active +*/ +void QQuickLoader::setSource(QDeclarativeV8Function *args) +{ + Q_ASSERT(args); + Q_D(QQuickLoader); + + bool ipvError = false; + args->returnValue(v8::Undefined()); + v8::Handle ipv = d->extractInitialPropertyValues(args, this, &ipvError); + if (ipvError) + return; + + d->clear(); + QUrl sourceUrl = d->resolveSourceUrl(args); + if (!ipv.IsEmpty()) { + d->disposeInitialPropertyValues(); + d->initialPropertyValues = qPersistentNew(ipv); + d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal()); + } + + setSource(sourceUrl, false); // already cleared and set ipv above. +} + +void QQuickLoaderPrivate::disposeInitialPropertyValues() +{ + if (!initialPropertyValues.IsEmpty()) + qPersistentDispose(initialPropertyValues); + if (!qmlGlobalForIpv.IsEmpty()) + qPersistentDispose(qmlGlobalForIpv); +} + +void QQuickLoaderPrivate::load() +{ + Q_Q(QQuickLoader); + + if (!q->isComponentComplete() || !component) + return; + + if (!component->isLoading()) { + _q_sourceLoaded(); + } else { + QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), + q, SLOT(_q_sourceLoaded())); + QObject::connect(component, SIGNAL(progressChanged(qreal)), + q, SIGNAL(progressChanged())); + emit q->statusChanged(); + emit q->progressChanged(); + if (loadingFromSource) + emit q->sourceChanged(); + else + emit q->sourceComponentChanged(); + emit q->itemChanged(); + } +} + +void QQuickLoaderIncubator::setInitialState(QObject *o) +{ + loader->setInitialState(o); +} + +void QQuickLoaderPrivate::setInitialState(QObject *obj) +{ + Q_Q(QQuickLoader); + + QQuickItem *item = qobject_cast(obj); + if (item) { + QDeclarative_setParent_noEvent(itemContext, obj); + QDeclarative_setParent_noEvent(item, q); + item->setParentItem(q); + } + + if (initialPropertyValues.IsEmpty()) + return; + + QDeclarativeComponentPrivate *d = QDeclarativeComponentPrivate::get(component); + Q_ASSERT(d && d->engine); + d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj); +} + +void QQuickLoaderIncubator::statusChanged(Status status) +{ + loader->incubatorStateChanged(status); +} + +void QQuickLoaderPrivate::incubatorStateChanged(QDeclarativeIncubator::Status status) +{ + Q_Q(QQuickLoader); + if (status == QDeclarativeIncubator::Loading || status == QDeclarativeIncubator::Null) + return; + + if (status == QDeclarativeIncubator::Ready) { + QObject *obj = incubator->object(); + item = qobject_cast(obj); + if (item) { + initResize(); + } else { + qmlInfo(q) << QQuickLoader::tr("Loader does not support loading non-visual elements."); + delete itemContext; + itemContext = 0; + delete obj; + } + } else if (status == QDeclarativeIncubator::Error) { + if (!incubator->errors().isEmpty()) + QDeclarativeEnginePrivate::warning(qmlEngine(q), incubator->errors()); + delete itemContext; + itemContext = 0; + delete incubator->object(); + source = QUrl(); + } + if (loadingFromSource) + emit q->sourceChanged(); + else + emit q->sourceComponentChanged(); + emit q->statusChanged(); + emit q->progressChanged(); + emit q->itemChanged(); + emit q->loaded(); + disposeInitialPropertyValues(); // cleanup +} + +void QQuickLoaderPrivate::_q_sourceLoaded() +{ + Q_Q(QQuickLoader); + if (!component || !component->errors().isEmpty()) { + if (component) + QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors()); + if (loadingFromSource) + emit q->sourceChanged(); + else + emit q->sourceComponentChanged(); + emit q->statusChanged(); + emit q->progressChanged(); + disposeInitialPropertyValues(); // cleanup + return; + } + + QDeclarativeContext *creationContext = component->creationContext(); + if (!creationContext) creationContext = qmlContext(q); + itemContext = new QDeclarativeContext(creationContext); + itemContext->setContextObject(q); + + if (incubator) { + bool async = incubator->incubationMode() == QDeclarativeIncubator::Asynchronous; + if (asynchronous != async) { + delete incubator; + incubator = 0; + } + } + if (!incubator) + incubator = new QQuickLoaderIncubator(this, asynchronous ? QDeclarativeIncubator::Asynchronous : QDeclarativeIncubator::AsynchronousIfNested); + + component->create(*incubator, itemContext); + + if (incubator->status() == QDeclarativeIncubator::Loading) + emit q->statusChanged(); +} + +/*! + \qmlproperty enumeration QtQuick2::Loader::status + + This property holds the status of QML loading. It can be one of: + \list + \o Loader.Null - the loader is inactive or no QML source has been set + \o Loader.Ready - the QML source has been loaded + \o Loader.Loading - the QML source is currently being loaded + \o Loader.Error - an error occurred while loading the QML source + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: loader.status == Loader.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + Loader { + id: loader + onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist + + Note that if the source is a local file, the status will initially be Ready (or Error). While + there will be no onStatusChanged signal in that case, the onLoaded will still be invoked. + + \sa progress +*/ + +QQuickLoader::Status QQuickLoader::status() const +{ + Q_D(const QQuickLoader); + + if (!d->active) + return Null; + + if (d->component) { + switch (d->component->status()) { + case QDeclarativeComponent::Loading: + return Loading; + case QDeclarativeComponent::Error: + return Error; + case QDeclarativeComponent::Null: + return Null; + default: + break; + } + } + + if (d->incubator) { + switch (d->incubator->status()) { + case QDeclarativeIncubator::Loading: + return Loading; + case QDeclarativeIncubator::Error: + return Error; + default: + break; + } + } + + if (d->item) + return Ready; + + return d->source.isEmpty() ? Null : Error; +} + +void QQuickLoader::componentComplete() +{ + Q_D(QQuickLoader); + QQuickItem::componentComplete(); + if (active()) { + if (d->loadingFromSource) { + d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this); + } + d->load(); + } +} + +/*! + \qmlsignal QtQuick2::Loader::onLoaded() + + This handler is called when the \l status becomes \c Loader.Ready, or on successful + initial load. +*/ + + +/*! +\qmlproperty real QtQuick2::Loader::progress + +This property holds the progress of loading QML data from the network, from +0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so +this value will rapidly change from 0 to 1. + +\sa status +*/ +qreal QQuickLoader::progress() const +{ + Q_D(const QQuickLoader); + + if (d->item) + return 1.0; + + if (d->component) + return d->component->progress(); + + return 0.0; +} + +/*! +\qmlproperty bool QtQuick2::Loader::asynchronous + +This property holds whether the component will be instantiated asynchronously. + +Note that this property affects object instantiation only; it is unrelated to +loading a component asynchronously via a network. +*/ +bool QQuickLoader::asynchronous() const +{ + Q_D(const QQuickLoader); + return d->asynchronous; +} + +void QQuickLoader::setAsynchronous(bool a) +{ + Q_D(QQuickLoader); + if (d->asynchronous == a) + return; + + d->asynchronous = a; + emit asynchronousChanged(); +} + +void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged) +{ + Q_Q(QQuickLoader); + if (!item || updatingSize) + return; + + updatingSize = true; + + if (!itemWidthValid) + q->setImplicitWidth(item->implicitWidth()); + else + q->setImplicitWidth(item->width()); + if (loaderGeometryChanged && q->widthValid()) + item->setWidth(q->width()); + + if (!itemHeightValid) + q->setImplicitHeight(item->implicitHeight()); + else + q->setImplicitHeight(item->height()); + if (loaderGeometryChanged && q->heightValid()) + item->setHeight(q->height()); + + updatingSize = false; +} + +/*! + \qmlproperty Item QtQuick2::Loader::item + This property holds the top-level item that is currently loaded. +*/ +QQuickItem *QQuickLoader::item() const +{ + Q_D(const QQuickLoader); + return d->item; +} + +void QQuickLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickLoader); + if (newGeometry != oldGeometry) { + d->_q_updateSize(); + } + QQuickItem::geometryChanged(newGeometry, oldGeometry); +} + +QUrl QQuickLoaderPrivate::resolveSourceUrl(QDeclarativeV8Function *args) +{ + QV8Engine *v8engine = args->engine(); + QString arg = v8engine->toString((*args)[0]->ToString()); + if (arg.isEmpty()) + return QUrl(); + + QDeclarativeContextData *context = args->context(); + Q_ASSERT(context); + return context->resolvedUrl(QUrl(arg)); +} + +v8::Handle QQuickLoaderPrivate::extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error) +{ + v8::Local valuemap; + if (args->Length() >= 2) { + v8::Local v = (*args)[1]; + if (!v->IsObject() || v->IsArray()) { + *error = true; + qmlInfo(loader) << loader->tr("setSource: value is not an object"); + } else { + *error = false; + valuemap = v8::Local::Cast(v); + } + } + + return valuemap; +} + +#include + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickloader_p.h b/src/declarative/items/qquickloader_p.h new file mode 100644 index 0000000000..3dcab4c654 --- /dev/null +++ b/src/declarative/items/qquickloader_p.h @@ -0,0 +1,123 @@ +// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLOADER_P_H +#define QQUICKLOADER_P_H + +#include "qquickimplicitsizeitem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickLoaderPrivate; +class Q_AUTOTEST_EXPORT QQuickLoader : public QQuickImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(Status) + + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QDeclarativeComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged) + Q_PROPERTY(QQuickItem *item READ item NOTIFY itemChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + +public: + QQuickLoader(QQuickItem *parent = 0); + virtual ~QQuickLoader(); + + bool active() const; + void setActive(bool newVal); + + Q_INVOKABLE void setSource(QDeclarativeV8Function *); + + QUrl source() const; + void setSource(const QUrl &); + + QDeclarativeComponent *sourceComponent() const; + void setSourceComponent(QDeclarativeComponent *); + void resetSourceComponent(); + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + bool asynchronous() const; + void setAsynchronous(bool a); + + QQuickItem *item() const; + +Q_SIGNALS: + void itemChanged(); + void activeChanged(); + void sourceChanged(); + void sourceComponentChanged(); + void statusChanged(); + void progressChanged(); + void loaded(); + void asynchronousChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + void componentComplete(); + +private: + void setSource(const QUrl &sourceUrl, bool needsClear); + void loadFromSource(); + void loadFromSourceComponent(); + Q_DISABLE_COPY(QQuickLoader) + Q_DECLARE_PRIVATE(QQuickLoader) + Q_PRIVATE_SLOT(d_func(), void _q_sourceLoaded()) + Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickLoader) + +QT_END_HEADER + +#endif // QQUICKLOADER_P_H diff --git a/src/declarative/items/qquickloader_p_p.h b/src/declarative/items/qquickloader_p_p.h new file mode 100644 index 0000000000..63d73e8da2 --- /dev/null +++ b/src/declarative/items/qquickloader_p_p.h @@ -0,0 +1,121 @@ +// Commit: 5d2817cd668a705729df1727de49adf00713ac97 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLOADER_P_P_H +#define QQUICKLOADER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickloader_p.h" +#include "qquickimplicitsizeitem_p_p.h" +#include "qquickitemchangelistener_p.h" +#include + +#include + +QT_BEGIN_NAMESPACE + + +class QQuickLoaderPrivate; +class QQuickLoaderIncubator : public QDeclarativeIncubator +{ +public: + QQuickLoaderIncubator(QQuickLoaderPrivate *l, IncubationMode mode) : QDeclarativeIncubator(mode), loader(l) {} + +protected: + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); + +private: + QQuickLoaderPrivate *loader; +}; + +class QDeclarativeContext; +class QQuickLoaderPrivate : public QQuickImplicitSizeItemPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickLoader) + +public: + QQuickLoaderPrivate(); + ~QQuickLoaderPrivate(); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void clear(); + void initResize(); + void load(); + + void incubatorStateChanged(QDeclarativeIncubator::Status status); + void setInitialState(QObject *o); + void disposeInitialPropertyValues(); + QUrl resolveSourceUrl(QDeclarativeV8Function *args); + v8::Handle extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error); + + QUrl source; + QQuickItem *item; + QDeclarativeComponent *component; + QDeclarativeContext *itemContext; + QQuickLoaderIncubator *incubator; + v8::Persistent initialPropertyValues; + v8::Persistent qmlGlobalForIpv; + bool updatingSize: 1; + bool itemWidthValid : 1; + bool itemHeightValid : 1; + bool active : 1; + bool loadingFromSource : 1; + bool asynchronous : 1; + + void _q_sourceLoaded(); + void _q_updateSize(bool loaderGeometryChanged = true); +}; + +QT_END_NAMESPACE + +#endif // QQUICKLOADER_P_P_H diff --git a/src/declarative/items/qquickmousearea.cpp b/src/declarative/items/qquickmousearea.cpp new file mode 100644 index 0000000000..19e7fd457b --- /dev/null +++ b/src/declarative/items/qquickmousearea.cpp @@ -0,0 +1,1131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmousearea_p.h" +#include "qquickmousearea_p_p.h" +#include "qquickcanvas.h" +#include "qquickevents_p_p.h" +#include "qquickdrag_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +static const int PressAndHoldDelay = 800; + +QQuickDrag::QQuickDrag(QObject *parent) +: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), +_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false) +{ +} + +QQuickDrag::~QQuickDrag() +{ +} + +QQuickItem *QQuickDrag::target() const +{ + return _target; +} + +void QQuickDrag::setTarget(QQuickItem *t) +{ + if (_target == t) + return; + _target = t; + emit targetChanged(); +} + +void QQuickDrag::resetTarget() +{ + if (_target == 0) + return; + _target = 0; + emit targetChanged(); +} + +QQuickDrag::Axis QQuickDrag::axis() const +{ + return _axis; +} + +void QQuickDrag::setAxis(QQuickDrag::Axis a) +{ + if (_axis == a) + return; + _axis = a; + emit axisChanged(); +} + +qreal QQuickDrag::xmin() const +{ + return _xmin; +} + +void QQuickDrag::setXmin(qreal m) +{ + if (_xmin == m) + return; + _xmin = m; + emit minimumXChanged(); +} + +qreal QQuickDrag::xmax() const +{ + return _xmax; +} + +void QQuickDrag::setXmax(qreal m) +{ + if (_xmax == m) + return; + _xmax = m; + emit maximumXChanged(); +} + +qreal QQuickDrag::ymin() const +{ + return _ymin; +} + +void QQuickDrag::setYmin(qreal m) +{ + if (_ymin == m) + return; + _ymin = m; + emit minimumYChanged(); +} + +qreal QQuickDrag::ymax() const +{ + return _ymax; +} + +void QQuickDrag::setYmax(qreal m) +{ + if (_ymax == m) + return; + _ymax = m; + emit maximumYChanged(); +} + +bool QQuickDrag::active() const +{ + return _active; +} + +void QQuickDrag::setActive(bool drag) +{ + if (_active == drag) + return; + _active = drag; + emit activeChanged(); +} + +bool QQuickDrag::filterChildren() const +{ + return _filterChildren; +} + +void QQuickDrag::setFilterChildren(bool filter) +{ + if (_filterChildren == filter) + return; + _filterChildren = filter; + emit filterChildrenChanged(); +} + +QQuickDragAttached *QQuickDrag::qmlAttachedProperties(QObject *obj) +{ + return new QQuickDragAttached(obj); +} + +QQuickMouseAreaPrivate::QQuickMouseAreaPrivate() +: absorb(true), hovered(false), pressed(false), longPress(false), + moved(false), stealMouse(false), doubleClick(false), preventStealing(false), + drag(0) +{ +} + +QQuickMouseAreaPrivate::~QQuickMouseAreaPrivate() +{ + delete drag; +} + +void QQuickMouseAreaPrivate::init() +{ + Q_Q(QQuickMouseArea); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildMouseEvents(true); +} + +void QQuickMouseAreaPrivate::saveEvent(QMouseEvent *event) +{ + lastPos = event->localPos(); + lastScenePos = event->windowPos(); + lastButton = event->button(); + lastButtons = event->buttons(); + lastModifiers = event->modifiers(); +} + +bool QQuickMouseAreaPrivate::isPressAndHoldConnected() +{ + Q_Q(QQuickMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QQuickMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); +} + +bool QQuickMouseAreaPrivate::isDoubleClickConnected() +{ + Q_Q(QQuickMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QQuickMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); +} + +bool QQuickMouseAreaPrivate::isClickConnected() +{ + Q_Q(QQuickMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("clicked(QQuickMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); +} + +void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t) +{ + Q_Q(QQuickMouseArea); + QPointF scenePos = q->mapToScene(QPointF(event->x(), event->y())); + propagateHelper(event, canvas->rootItem(), scenePos, t); +} + +bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *item,const QPointF &sp, PropagateType sig) +{ + //Based off of QQuickCanvas::deliverInitialMousePressEvent + //But specific to MouseArea, so doesn't belong in canvas + Q_Q(const QQuickMouseArea); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(sp); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled()) + continue; + if (propagateHelper(ev, child, sp, sig)) + return true; + } + + QQuickMouseArea* ma = qobject_cast(item); + if (ma && ma != q && itemPrivate->acceptedMouseButtons & ev->button()) { + switch (sig) { + case Click: + if (!ma->d_func()->isClickConnected()) + return false; + break; + case DoubleClick: + if (!ma->d_func()->isDoubleClickConnected()) + return false; + break; + case PressAndHold: + if (!ma->d_func()->isPressAndHoldConnected()) + return false; + break; + } + QPointF p = item->mapFromScene(sp); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + ev->setX(p.x()); + ev->setY(p.y()); + ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide + switch (sig) { + case Click: emit ma->clicked(ev); break; + case DoubleClick: emit ma->doubleClicked(ev); break; + case PressAndHold: emit ma->pressAndHold(ev); break; + } + if (ev->isAccepted()) + return true; + } + } + return false; + +} + +/*! + \qmlclass MouseArea QQuickMouseArea + \inqmlmodule QtQuick 2 + \ingroup qml-basic-interaction-elements + \brief The MouseArea item enables simple mouse handling. + \inherits Item + + A MouseArea is an invisible item that is typically used in conjunction with + a visible item in order to provide mouse handling for that item. + By effectively acting as a proxy, the logic for mouse handling can be + contained within a MouseArea item. + + For basic key handling, see the \l{Keys}{Keys attached property}. + + The \l enabled property is used to enable and disable mouse handling for + the proxied item. When disabled, the mouse area becomes transparent to + mouse events. + + The \l pressed read-only property indicates whether or not the user is + holding down a mouse button over the mouse area. This property is often + used in bindings between properties in a user interface. The containsMouse + read-only property indicates the presence of the mouse cursor over the + mouse area but, by default, only when a mouse button is held down; see below + for further details. + + Information about the mouse position and button clicks are provided via + signals for which event handler properties are defined. The most commonly + used involved handling mouse presses and clicks: onClicked, onDoubleClicked, + onPressed, onReleased and onPressAndHold. + + By default, MouseArea items only report mouse clicks and not changes to the + position of the mouse cursor. Setting the hoverEnabled property ensures that + handlers defined for onPositionChanged, onEntered and onExited are used and + that the containsMouse property is updated even when no mouse buttons are + pressed. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage qml-mousearea-snippet.png + \enddiv + + The following example uses a MouseArea in a \l Rectangle that changes + the \l Rectangle color to red when clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import + \codeline + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro + + \clearfloat + Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains + additional information about the mouse event, such as the position, button, + and any key modifiers. + + Here is an extension of the previous example that produces a different + color when the area is right clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended + + Behavioral Change in QtQuick 2.0 + + From QtQuick 2.0, the signals clicked, doubleClicked and pressAndHold have a different interaction + model with regards to the delivery of events to multiple overlapping MouseAreas. These signals will now propagate + to all MouseAreas in the area, in painting order, until accepted by one of them. A signal is accepted by + default if there is a signal handler for it, use mouse.accepted = false; to ignore. This propagation + can send the signal to MouseAreas other than the one which accepted the press event, although that MouseArea + will receive the signal first. + + Note that to get the same behavior as a QtQuick 1.0 MouseArea{} with regard to absorbing all mouse events, you will + now need to add empty signal handlers for these three signals. + + \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example} +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onEntered() + + This handler is called when the mouse enters the mouse area. + + By default the onEntered handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onEntered when no mouse button is pressed. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onExited() + + This handler is called when the mouse exits the mouse area. + + By default the onExited handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onExited when no mouse button is pressed. + + The example below shows a fairly typical relationship between + two MouseAreas, with \c mouseArea2 on top of \c mouseArea1. Moving the + mouse into \c mouseArea2 from \c mouseArea1 will cause \c onExited + to be called for \c mouseArea1. + \qml + Rectangle { + width: 400; height: 400 + MouseArea { + id: mouseArea1 + anchors.fill: parent + hoverEnabled: true + } + MouseArea { + id: mouseArea2 + width: 100; height: 100 + anchors.centerIn: parent + hoverEnabled: true + } + } + \endqml + + If instead you give the two mouseAreas a parent-child relationship, + moving the mouse into \c mouseArea2 from \c mouseArea1 will \b not + cause \c onExited to be called for \c mouseArea1. Instead, they will + both be considered to be simultaneously hovered. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onPositionChanged(MouseEvent mouse) + + This handler is called when the mouse position changes. + + The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y + position, and any buttons currently pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + By default the onPositionChanged handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onPositionChanged when no mouse button is pressed. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onClicked(MouseEvent mouse) + + This handler is called when there is a click. A click is defined as a press followed by a release, + both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and + releasing is also considered a click). + + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onPressed(MouseEvent mouse) + + This handler is called when there is a press. + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position and which button was pressed. + + The \e accepted property of the MouseEvent parameter determines whether this MouseArea + will handle the press and all future mouse events until release. The default is to accept + the event and not allow other MouseArea beneath this one to handle the event. If \e accepted + is set to false, no further events will be sent to this MouseArea until the button is next + pressed. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onReleased(MouseEvent mouse) + + This handler is called when there is a release. + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + \sa onCanceled +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onPressAndHold(MouseEvent mouse) + + This handler is called when there is a long press (currently 800ms). + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position of the press, and which button is pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onDoubleClicked(MouseEvent mouse) + + This handler is called when there is a double-click (a press followed by a release followed by a press). + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false + in the handler, the onPressed/onReleased/onClicked handlers will be called for the second + click; otherwise they are suppressed. The accepted property defaults to true. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onCanceled() + + This handler is called when mouse events have been canceled, either because an event was not accepted, or + because another element stole the mouse event handling. + + This signal is for advanced use: it is useful when there is more than one MouseArea + that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter + case, if you execute some logic on the pressed signal and then start dragging, the + \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset + the logic when the MouseArea has lost the mouse handling to the \l Flickable, + \c onCanceled should be used in addition to onReleased. +*/ +QQuickMouseArea::QQuickMouseArea(QQuickItem *parent) + : QQuickItem(*(new QQuickMouseAreaPrivate), parent) +{ + Q_D(QQuickMouseArea); + d->init(); +} + +QQuickMouseArea::~QQuickMouseArea() +{ +} + +/*! + \qmlproperty real QtQuick2::MouseArea::mouseX + \qmlproperty real QtQuick2::MouseArea::mouseY + These properties hold the coordinates of the mouse cursor. + + If the hoverEnabled property is false then these properties will only be valid + while a button is pressed, and will remain valid as long as the button is held + down even if the mouse is moved outside the area. + + By default, this property is false. + + If hoverEnabled is true then these properties will be valid when: + \list + \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true). + \i a button is pressed and held, even if it has since moved out of the area. + \endlist + + The coordinates are relative to the MouseArea. +*/ +qreal QQuickMouseArea::mouseX() const +{ + Q_D(const QQuickMouseArea); + return d->lastPos.x(); +} + +qreal QQuickMouseArea::mouseY() const +{ + Q_D(const QQuickMouseArea); + return d->lastPos.y(); +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::enabled + This property holds whether the item accepts mouse events. + + By default, this property is true. +*/ +bool QQuickMouseArea::isEnabled() const +{ + Q_D(const QQuickMouseArea); + return d->absorb; +} + +void QQuickMouseArea::setEnabled(bool a) +{ + Q_D(QQuickMouseArea); + if (a != d->absorb) { + d->absorb = a; + emit enabledChanged(); + } +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::preventStealing + This property holds whether the mouse events may be stolen from this + MouseArea. + + If a MouseArea is placed within an item that filters child mouse + events, such as Flickable, the mouse + events may be stolen from the MouseArea if a gesture is recognized + by the parent element, e.g. a flick gesture. If preventStealing is + set to true, no element will steal the mouse events. + + Note that setting preventStealing to true once an element has started + stealing events will have no effect until the next press event. + + By default this property is false. +*/ +bool QQuickMouseArea::preventStealing() const +{ + Q_D(const QQuickMouseArea); + return d->preventStealing; +} + +void QQuickMouseArea::setPreventStealing(bool prevent) +{ + Q_D(QQuickMouseArea); + if (prevent != d->preventStealing) { + d->preventStealing = prevent; + setKeepMouseGrab(d->preventStealing && d->absorb); + emit preventStealingChanged(); + } +} + +/*! + \qmlproperty MouseButtons QtQuick2::MouseArea::pressedButtons + This property holds the mouse buttons currently pressed. + + It contains a bitwise combination of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + The code below displays "right" when the right mouse buttons is pressed: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons + + \sa acceptedButtons +*/ +Qt::MouseButtons QQuickMouseArea::pressedButtons() const +{ + Q_D(const QQuickMouseArea); + return d->lastButtons; +} + +void QQuickMouseArea::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + d->moved = false; + d->stealMouse = d->preventStealing; + if (!d->absorb) + QQuickItem::mousePressEvent(event); + else { + d->longPress = false; + d->saveEvent(event); + if (d->drag) { + d->dragX = drag()->axis() & QQuickDrag::XAxis; + d->dragY = drag()->axis() & QQuickDrag::YAxis; + } + if (d->drag) + d->drag->setActive(false); + setHovered(true); + d->startScene = event->windowPos(); + d->pressAndHoldTimer.start(PressAndHoldDelay, this); + setKeepMouseGrab(d->stealMouse); + event->setAccepted(setPressed(true)); + + } +} + +void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) { + QQuickItem::mouseMoveEvent(event); + return; + } + + d->saveEvent(event); + + // ### we should skip this if these signals aren't used + // ### can GV handle this for us? + bool contains = boundingRect().contains(d->lastPos); + if (d->hovered && !contains) + setHovered(false); + else if (!d->hovered && contains) + setHovered(true); + + if (d->drag && d->drag->target()) { + if (!d->moved) { + d->targetStartPos = d->drag->target()->parentItem() + ? d->drag->target()->parentItem()->mapToScene(d->drag->target()->pos()) + : d->drag->target()->pos(); + } + + QPointF startLocalPos; + QPointF curLocalPos; + if (drag()->target()->parentItem()) { + startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene); + curLocalPos = drag()->target()->parentItem()->mapFromScene(event->windowPos()); + } else { + startLocalPos = d->startScene; + curLocalPos = event->windowPos(); + } + + const int dragThreshold = qApp->styleHints()->startDragDistance(); + qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); + qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); + + if (keepMouseGrab() && d->stealMouse && !d->drag->active()) + d->drag->setActive(true); + + QPointF startPos = d->drag->target()->parentItem() + ? d->drag->target()->parentItem()->mapFromScene(d->targetStartPos) + : d->targetStartPos; + + QPointF dragPos = d->drag->target()->pos(); + + if (d->dragX && d->drag->active()) { + qreal x = (curLocalPos.x() - startLocalPos.x()) + startPos.x(); + if (x < drag()->xmin()) + x = drag()->xmin(); + else if (x > drag()->xmax()) + x = drag()->xmax(); + dragPos.setX(x); + } + if (d->dragY && d->drag->active()) { + qreal y = (curLocalPos.y() - startLocalPos.y()) + startPos.y(); + if (y < drag()->ymin()) + y = drag()->ymin(); + else if (y > drag()->ymax()) + y = drag()->ymax(); + dragPos.setY(y); + } + d->drag->target()->setPos(dragPos); + + if (!keepMouseGrab()) { + if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold) + || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold) + || (d->dragX && d->dragY && (dx > dragThreshold || dy > dragThreshold))) { + setKeepMouseGrab(true); + d->stealMouse = true; + } + } + + d->moved = true; + } + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + me.setPosition(d->lastPos); + emit positionChanged(&me); +} + +void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + d->stealMouse = false; + if (!d->absorb) { + QQuickItem::mouseReleaseEvent(event); + } else { + d->saveEvent(event); + setPressed(false); + if (d->drag) + d->drag->setActive(false); + // If we don't accept hover, we need to reset containsMouse. + if (!acceptHoverEvents()) + setHovered(false); + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + + } + d->doubleClick = false; +} + +void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + if (d->absorb) { + d->saveEvent(event); + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); + me.setAccepted(d->isDoubleClickConnected()); + emit this->doubleClicked(&me); + if (!me.isAccepted()) + d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick); + d->doubleClick = d->isDoubleClickConnected() || me.isAccepted(); + } + QQuickItem::mouseDoubleClickEvent(event); +} + +void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) { + QQuickItem::hoverEnterEvent(event); + } else { + d->lastPos = event->posF(); + d->lastModifiers = event->modifiers(); + setHovered(true); + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + me.setPosition(d->lastPos); + } +} + +void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) { + QQuickItem::hoverMoveEvent(event); + } else { + d->lastPos = event->posF(); + d->lastModifiers = event->modifiers(); + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + me.setPosition(d->lastPos); + emit positionChanged(&me); + } +} + +void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) + QQuickItem::hoverLeaveEvent(event); + else + setHovered(false); +} + +void QQuickMouseArea::ungrabMouse() +{ + Q_D(QQuickMouseArea); + if (d->pressed) { + // if our mouse grab has been removed (probably by Flickable), fix our + // state + d->pressed = false; + d->stealMouse = false; + setKeepMouseGrab(false); + emit canceled(); + emit pressedChanged(); + if (d->hovered) { + d->hovered = false; + emit hoveredChanged(); + } + } +} + +void QQuickMouseArea::mouseUngrabEvent() +{ + ungrabMouse(); +} + +bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { + QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + mouseEvent.setAccepted(false); + + switch (event->type()) { + case QEvent::MouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::MouseButtonPress: + mousePressEvent(&mouseEvent); + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = c->mouseGrabberItem(); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return stealThisEvent; + } + if (event->type() == QEvent::MouseButtonRelease) { + if (d->pressed) { + d->pressed = false; + d->stealMouse = false; + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + emit canceled(); + emit pressedChanged(); + if (d->hovered) { + d->hovered = false; + emit hoveredChanged(); + } + } + } + return false; +} + +bool QQuickMouseArea::childMouseEventFilter(QQuickItem *i, QEvent *e) +{ + Q_D(QQuickMouseArea); + if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren()) + return QQuickItem::childMouseEventFilter(i, e); + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast(e)); + default: + break; + } + + return QQuickItem::childMouseEventFilter(i, e); +} + +void QQuickMouseArea::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickMouseArea); + if (event->timerId() == d->pressAndHoldTimer.timerId()) { + d->pressAndHoldTimer.stop(); + bool dragged = d->drag && d->drag->active(); + if (d->pressed && dragged == false && d->hovered == true) { + d->longPress = true; + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + me.setAccepted(d->isPressAndHoldConnected()); + emit pressAndHold(&me); + if (!me.isAccepted()) + d->propagate(&me, QQuickMouseAreaPrivate::PressAndHold); + if (!me.isAccepted()) // no one handled the long press - allow click + d->longPress = false; + } + } +} + +void QQuickMouseArea::windowDeactivateEvent() +{ + ungrabMouse(); + QQuickItem::windowDeactivateEvent(); +} + +void QQuickMouseArea::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QQuickMouseArea); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + if (d->lastScenePos.isNull) + d->lastScenePos = mapToScene(d->lastPos); + else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y()) + d->lastPos = mapFromScene(d->lastScenePos); +} + +void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickMouseArea); + switch (change) { + case ItemVisibleHasChanged: + if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) + setHovered(!d->hovered); + break; + default: + break; + } + + QQuickItem::itemChange(change, value); +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::hoverEnabled + This property holds whether hover events are handled. + + By default, mouse events are only handled in response to a button event, or when a button is + pressed. Hover enables handling of all mouse events even when no mouse button is + pressed. + + This property affects the containsMouse property and the onEntered, onExited and + onPositionChanged signals. +*/ +bool QQuickMouseArea::hoverEnabled() const +{ + return acceptHoverEvents(); +} + +void QQuickMouseArea::setHoverEnabled(bool h) +{ + if (h == acceptHoverEvents()) + return; + + setAcceptHoverEvents(h); + emit hoverEnabledChanged(); +} + + +/*! + \qmlproperty bool QtQuick2::MouseArea::containsMouse + This property holds whether the mouse is currently inside the mouse area. + + \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change. + In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed. +*/ +bool QQuickMouseArea::hovered() const +{ + Q_D(const QQuickMouseArea); + return d->hovered; +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::pressed + This property holds whether the mouse area is currently pressed. +*/ +bool QQuickMouseArea::pressed() const +{ + Q_D(const QQuickMouseArea); + return d->pressed; +} + +void QQuickMouseArea::setHovered(bool h) +{ + Q_D(QQuickMouseArea); + if (d->hovered != h) { + d->hovered = h; + emit hoveredChanged(); + d->hovered ? emit entered() : emit exited(); + } +} +/*! + \qmlproperty QtQuick2::Qt::MouseButtons MouseArea::acceptedButtons + This property holds the mouse buttons that the mouse area reacts to. + + The available buttons are: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + To accept more than one button the flags can be combined with the + "|" (or) operator: + + \code + MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton } + \endcode + + The default value is \c Qt.LeftButton. +*/ +Qt::MouseButtons QQuickMouseArea::acceptedButtons() const +{ + return acceptedMouseButtons(); +} + +void QQuickMouseArea::setAcceptedButtons(Qt::MouseButtons buttons) +{ + if (buttons != acceptedMouseButtons()) { + setAcceptedMouseButtons(buttons); + emit acceptedButtonsChanged(); + } +} + +bool QQuickMouseArea::setPressed(bool p) +{ + Q_D(QQuickMouseArea); + bool dragged = d->drag && d->drag->active(); + bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true; + + if (d->pressed != p) { + d->pressed = p; + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress); + if (d->pressed) { + if (!d->doubleClick) + emit pressed(&me); + me.setPosition(d->lastPos); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + emit pressedChanged(); + } else { + emit released(&me); + me.setPosition(d->lastPos); + emit pressedChanged(); + if (isclick && !d->longPress && !d->doubleClick){ + me.setAccepted(d->isClickConnected()); + emit clicked(&me); + if (!me.isAccepted()) + d->propagate(&me, QQuickMouseAreaPrivate::Click); + } + } + + return me.isAccepted(); + } + return false; +} + +/*! + \qmlproperty Item QtQuick2::MouseArea::drag.target + \qmlproperty bool QtQuick2::MouseArea::drag.active + \qmlproperty enumeration QtQuick2::MouseArea::drag.axis + \qmlproperty real QtQuick2::MouseArea::drag.minimumX + \qmlproperty real QtQuick2::MouseArea::drag.maximumX + \qmlproperty real QtQuick2::MouseArea::drag.minimumY + \qmlproperty real QtQuick2::MouseArea::drag.maximumY + \qmlproperty bool QtQuick2::MouseArea::drag.filterChildren + + \c drag provides a convenient way to make an item draggable. + + \list + \i \c drag.target specifies the id of the item to drag. + \i \c drag.active specifies if the target item is currently being dragged. + \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis) + \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes. + \endlist + + The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity + of the rectangle is reduced when it is dragged to the right. + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag + + \note Items cannot be dragged if they are anchored for the requested + \c drag.axis. For example, if \c anchors.left or \c anchors.right was set + for \c rect in the above example, it cannot be dragged along the X-axis. + This can be avoided by settng the anchor value to \c undefined in + an \l onPressed handler. + + If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This + enables a parent MouseArea to handle drags, for example, while descendants handle clicks: + + \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter + +*/ + +QQuickDrag *QQuickMouseArea::drag() +{ + Q_D(QQuickMouseArea); + if (!d->drag) + d->drag = new QQuickDrag; + return d->drag; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickmousearea_p.h b/src/declarative/items/qquickmousearea_p.h new file mode 100644 index 0000000000..6f15ff6eb4 --- /dev/null +++ b/src/declarative/items/qquickmousearea_p.h @@ -0,0 +1,226 @@ +// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMOUSEAREA_P_H +#define QQUICKMOUSEAREA_P_H + +#include "qquickitem.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickDragAttached; +class QQuickMouseEvent; +class Q_AUTOTEST_EXPORT QQuickDrag : public QObject +{ + Q_OBJECT + + Q_ENUMS(Axis) + Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget) + Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged) + Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) + Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged) + Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged) + Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged) + //### consider drag and drop + +public: + QQuickDrag(QObject *parent=0); + ~QQuickDrag(); + + QQuickItem *target() const; + void setTarget(QQuickItem *target); + void resetTarget(); + + enum Axis { XAxis=0x01, YAxis=0x02, XandYAxis=0x03 }; + Axis axis() const; + void setAxis(Axis); + + qreal xmin() const; + void setXmin(qreal); + qreal xmax() const; + void setXmax(qreal); + qreal ymin() const; + void setYmin(qreal); + qreal ymax() const; + void setYmax(qreal); + + bool active() const; + void setActive(bool); + + bool filterChildren() const; + void setFilterChildren(bool); + + static QQuickDragAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void targetChanged(); + void axisChanged(); + void minimumXChanged(); + void maximumXChanged(); + void minimumYChanged(); + void maximumYChanged(); + void activeChanged(); + void filterChildrenChanged(); + +private: + QQuickItem *_target; + Axis _axis; + qreal _xmin; + qreal _xmax; + qreal _ymin; + qreal _ymax; + bool _active : 1; + bool _filterChildren: 1; + Q_DISABLE_COPY(QQuickDrag) +}; + +class QQuickMouseAreaPrivate; +// used in QtLocation +class Q_DECLARATIVE_EXPORT QQuickMouseArea : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(qreal mouseX READ mouseX NOTIFY mouseXChanged) + Q_PROPERTY(qreal mouseY READ mouseY NOTIFY mouseYChanged) + Q_PROPERTY(bool containsMouse READ hovered NOTIFY hoveredChanged) + Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedChanged) + Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) + Q_PROPERTY(bool hoverEnabled READ hoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged) + Q_PROPERTY(QQuickDrag *drag READ drag CONSTANT) //### add flicking to QQuickDrag or add a QDeclarativeFlick ??? + Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged) + +public: + QQuickMouseArea(QQuickItem *parent=0); + ~QQuickMouseArea(); + + qreal mouseX() const; + qreal mouseY() const; + + bool isEnabled() const; + void setEnabled(bool); + + bool hovered() const; + bool pressed() const; + + Qt::MouseButtons pressedButtons() const; + + Qt::MouseButtons acceptedButtons() const; + void setAcceptedButtons(Qt::MouseButtons buttons); + + bool hoverEnabled() const; + void setHoverEnabled(bool h); + + QQuickDrag *drag(); + + bool preventStealing() const; + void setPreventStealing(bool prevent); + +Q_SIGNALS: + void hoveredChanged(); + void pressedChanged(); + void enabledChanged(); + void acceptedButtonsChanged(); + void hoverEnabledChanged(); + void positionChanged(QQuickMouseEvent *mouse); + void mouseXChanged(QQuickMouseEvent *mouse); + void mouseYChanged(QQuickMouseEvent *mouse); + void preventStealingChanged(); + + void pressed(QQuickMouseEvent *mouse); + void pressAndHold(QQuickMouseEvent *mouse); + void released(QQuickMouseEvent *mouse); + void clicked(QQuickMouseEvent *mouse); + void doubleClicked(QQuickMouseEvent *mouse); + void entered(); + void exited(); + void canceled(); + +protected: + void setHovered(bool); + bool setPressed(bool); + bool sendMouseEvent(QMouseEvent *event); + + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseUngrabEvent(); + virtual void hoverEnterEvent(QHoverEvent *event); + virtual void hoverMoveEvent(QHoverEvent *event); + virtual void hoverLeaveEvent(QHoverEvent *event); + virtual bool childMouseEventFilter(QQuickItem *i, QEvent *e); + virtual void timerEvent(QTimerEvent *event); + virtual void windowDeactivateEvent(); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual void itemChange(ItemChange change, const ItemChangeData& value); + +private: + void handlePress(); + void handleRelease(); + void ungrabMouse(); + +private: + Q_DISABLE_COPY(QQuickMouseArea) + Q_DECLARE_PRIVATE(QQuickMouseArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickDrag) +QML_DECLARE_TYPEINFO(QQuickDrag, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQuickMouseArea) + +QT_END_HEADER + +#endif // QQUICKMOUSEAREA_P_H diff --git a/src/declarative/items/qquickmousearea_p_p.h b/src/declarative/items/qquickmousearea_p_p.h new file mode 100644 index 0000000000..9f81bf976f --- /dev/null +++ b/src/declarative/items/qquickmousearea_p_p.h @@ -0,0 +1,111 @@ +// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMOUSEAREA_P_P_H +#define QQUICKMOUSEAREA_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickMouseEvent; +class QQuickMouseArea; +class QQuickMouseAreaPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickMouseArea) + +public: + QQuickMouseAreaPrivate(); + ~QQuickMouseAreaPrivate(); + void init(); + + void saveEvent(QMouseEvent *event); + enum PropagateType{ + Click, + DoubleClick, + PressAndHold + }; + void propagate(QQuickMouseEvent* event, PropagateType); + bool propagateHelper(QQuickMouseEvent*, QQuickItem*,const QPointF &, PropagateType); + + bool isPressAndHoldConnected(); + bool isDoubleClickConnected(); + bool isClickConnected(); + + bool absorb : 1; + bool hovered : 1; + bool pressed : 1; + bool longPress : 1; + bool moved : 1; + bool dragX : 1; + bool dragY : 1; + bool stealMouse : 1; + bool doubleClick : 1; + bool preventStealing : 1; + QQuickDrag *drag; + QPointF startScene; + QPointF targetStartPos; + QPointF lastPos; + QDeclarativeNullableValue lastScenePos; + Qt::MouseButton lastButton; + Qt::MouseButtons lastButtons; + Qt::KeyboardModifiers lastModifiers; + QBasicTimer pressAndHoldTimer; +}; + +QT_END_NAMESPACE + +#endif // QQUICKMOUSEAREA_P_P_H diff --git a/src/declarative/items/qquickninepatchnode.cpp b/src/declarative/items/qquickninepatchnode.cpp new file mode 100644 index 0000000000..2974725348 --- /dev/null +++ b/src/declarative/items/qquickninepatchnode.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickninepatchnode_p.h" +#include +#include + +QQuickNinePatchNode::QQuickNinePatchNode() + : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) + , m_horizontalTileMode(QQuickBorderImage::Stretch) + , m_verticalTileMode(QQuickBorderImage::Stretch) + , m_dirtyGeometry(false) + , m_mirror(false) +{ + setOpaqueMaterial(&m_material); + setMaterial(&m_materialO); + setGeometry(&m_geometry); + m_geometry.setDrawingMode(GL_TRIANGLES); +#ifdef QML_RUNTIME_TESTING + description = QLatin1String("borderimage"); +#endif +} + +void QQuickNinePatchNode::setInnerRect(const QRectF &rect) +{ + if (m_innerRect == rect) + return; + m_innerRect = rect; + m_dirtyGeometry = true; +} + +void QQuickNinePatchNode::setRect(const QRectF &rect) +{ + if (m_targetRect == rect) + return; + m_targetRect = rect; + m_dirtyGeometry = true; +} + +void QQuickNinePatchNode::setHorzontalTileMode(QQuickBorderImage::TileMode mode) +{ + if (mode == QQuickBorderImage::TileMode(m_horizontalTileMode)) + return; + m_horizontalTileMode = mode; + m_dirtyGeometry = true; +} + + +void QQuickNinePatchNode::setVerticalTileMode(QQuickBorderImage::TileMode mode) +{ + if (mode == QQuickBorderImage::TileMode(m_verticalTileMode)) + return; + m_verticalTileMode = mode; + m_dirtyGeometry = true; +} + + +void QQuickNinePatchNode::setFiltering(QSGTexture::Filtering filtering) +{ + if (m_material.filtering() == filtering) + return; + + m_material.setFiltering(filtering); + m_materialO.setFiltering(filtering); + markDirty(DirtyMaterial); +} + +QSGTexture::Filtering QQuickNinePatchNode::filtering() const +{ + return m_material.filtering(); +} + +void QQuickNinePatchNode::setTexture(QSGTexture *texture) +{ + if (texture == m_material.texture()) + return; + m_material.setTexture(texture); + m_materialO.setTexture(texture); + markDirty(DirtyMaterial); +} + +QSGTexture *QQuickNinePatchNode::texture() const +{ + return m_material.texture(); +} + +void QQuickNinePatchNode::setMirror(bool m) +{ + if (m_mirror == m) + return; + m_mirror = m; + m_dirtyGeometry = true; +} + + +void QQuickNinePatchNode::update() +{ + if (!m_dirtyGeometry) + return; + + // For stretch this algorithm could be simplified to use less vertices + // as more vertices could be reused then, but I doubt its where our main + // problem will lie. This way, we at least share the algorithm between all + + Q_ASSERT(m_material.texture()); + + float tw = m_material.texture()->textureSize().width(); + float th = m_material.texture()->textureSize().height(); + + QRectF textureSubRect = m_material.texture()->textureSubRect(); + QSize textureSize = m_material.texture()->textureSize(); + + float rightBorder = tw - m_innerRect.right(); + float bottomBorder = th - m_innerRect.bottom(); + +// qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode; + + int xChunkCount = 0; // Number of chunks + float xChunkSize = 0; // Size of chunk in pixels + float xTexSize = m_innerRect.width(); // Size of the texture to stretch/tile + float xSize = m_targetRect.width() - m_innerRect.left() - rightBorder; // Size of area to fill with chunks + + if (m_horizontalTileMode == QQuickBorderImage::Repeat) { + xChunkCount = qCeil(xSize / xTexSize); + xChunkSize = xTexSize; + } else if (m_horizontalTileMode == QQuickBorderImage::Round) { + xChunkCount = qCeil(xSize / xTexSize); + qreal fullWidth = xChunkCount * xTexSize; + xChunkSize = xTexSize * xSize / fullWidth; + } else { + xChunkCount = 1; + xChunkSize = xSize; + } + + int yChunkCount = 0; + float yChunkSize = 0; // Relative to target rect. + float yTexSize = m_innerRect.height(); // Size of the texture to stretch/tile + float ySize = m_targetRect.height() - m_innerRect.top() - bottomBorder; + + if (m_verticalTileMode == QQuickBorderImage::Repeat) { + yChunkCount = qCeil(ySize / yTexSize); + yChunkSize = yTexSize; + } else if (m_verticalTileMode == QQuickBorderImage::Round) { + yChunkCount = qCeil(ySize / yTexSize); + qreal fullHeight = yChunkCount * yTexSize; + yChunkSize = yTexSize * ySize / fullHeight; + } else { + yChunkCount = 1; + yChunkSize = ySize; + } + + int xTotalChunkCount = xChunkCount + 2; + int yTotalChunkCount = yChunkCount + 2; + + int totalChunkCount = xTotalChunkCount * yTotalChunkCount; + int vertexCount = totalChunkCount * 4; + int indexCount = totalChunkCount * 6; + + if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount()) + m_geometry.allocate(vertexCount, indexCount); + + QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D(); + + + // Fill in the vertices.. The loop below is pretty much an exact replica + // of the one inside fillRow. + float yTexChunk1 = m_innerRect.top() / th; + float yTexChunk2 = m_innerRect.bottom() / th; + + fillRow(v, 0, 0, xChunkCount, xChunkSize, textureSubRect, textureSize); + fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize, textureSubRect, textureSize); + + for (int yc=0; ycx = m_targetRect.width() - v->x; + ++v; + } + } + +// v = m_geometry.vertexDataAsTexturedPoint2D(); +// for (int i=0; ix, v->y, v->tx, v->ty); +// ++v; +// } + + quint16 *i = m_geometry.indexDataAsUShort(); + int row = xTotalChunkCount * 2; + for (int r=0; rset(0, y, tsr.left(), ty); + v++->set(m_innerRect.x(), y, xTexChunk1, ty); + + for (int xc=0; xcset(xx, y, xTexChunk1, ty); + + // Special case the last one + if (xc == xChunkCount - 1) { + float t = m_horizontalTileMode == QQuickBorderImage::Repeat + ? xTexChunk1 + (xTexChunk2 - xTexChunk1) * (m_targetRect.width() - rightBorder - xx) / xChunkSize + : xTexChunk2; + v->set(m_targetRect.width() - rightBorder, y, t, ty); + } else { + v->set(xx + xChunkSize, y, xTexChunk2, ty); + } + ++v; + } + + v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty); + v++->set(m_targetRect.width(), y, tsr.right(), ty); +} diff --git a/src/declarative/items/qquickninepatchnode_p.h b/src/declarative/items/qquickninepatchnode_p.h new file mode 100644 index 0000000000..cdb0fd8e70 --- /dev/null +++ b/src/declarative/items/qquickninepatchnode_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNINEPATCHNODE_H +#define QQUICKNINEPATCHNODE_H + +#include "qsgnode.h" +#include "qsgtexturematerial.h" +#include "qquickborderimage_p.h" + +class TextureReference; + +class QQuickNinePatchNode : public QSGGeometryNode +{ +public: + QQuickNinePatchNode(); + + void setTexture(QSGTexture *texture); + QSGTexture *texture() const; + + void setRect(const QRectF &rect); + QRectF rect() const { return m_targetRect; } + + void setInnerRect(const QRectF &rect); + QRectF innerRect() const { return m_innerRect; } + + void setFiltering(QSGTexture::Filtering filtering); + QSGTexture::Filtering filtering() const; + + void setHorzontalTileMode(QQuickBorderImage::TileMode mode); + QQuickBorderImage::TileMode horizontalTileMode() const { + return (QQuickBorderImage::TileMode) m_horizontalTileMode; + } + + void setVerticalTileMode(QQuickBorderImage::TileMode mode); + QQuickBorderImage::TileMode verticalTileMode() const { + return (QQuickBorderImage::TileMode) m_verticalTileMode; + } + + void setMirror(bool m); + bool mirror() const { return m_mirror; } + + void update(); + +private: + void fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize, const QRectF &tsr, const QSize &ts); + QRectF m_targetRect; + QRectF m_innerRect; + QSGOpaqueTextureMaterial m_material; + QSGTextureMaterial m_materialO; + QSGGeometry m_geometry; + + uint m_horizontalTileMode : 2; + uint m_verticalTileMode : 2; + + uint m_dirtyGeometry : 1; + uint m_mirror : 1; +}; + +#endif // QQUICKNINEPATCHNODE_H diff --git a/src/declarative/items/qquickpainteditem.cpp b/src/declarative/items/qquickpainteditem.cpp new file mode 100644 index 0000000000..5e5eb79f36 --- /dev/null +++ b/src/declarative/items/qquickpainteditem.cpp @@ -0,0 +1,537 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpainteditem.h" +#include + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickPaintedItem + \brief The QQuickPaintedItem class provides a way to use the QPainter API in the + QML Scene Graph. + + \inmodule QtDeclarative + + The QQuickPaintedItem makes it possible to use the QPainter API with the QML Scene Graph. + It sets up a textured rectangle in the Scene Graph and uses a QPainter to paint + onto the texture. The render target can be either a QImage or a QOpenGLFramebufferObject. + When the render target is a QImage, QPainter first renders into the image then + the content is uploaded to the texture. + When a QOpenGLFramebufferObject is used, QPainter paints directly onto the texture. + Call update() to trigger a repaint. + + To enable QPainter to do anti-aliased rendering, use setAntialiasing(). + + QQuickPaintedItem is meant to make it easier to port old code that is using the + QPainter API to the QML Scene Graph API and it should be used only for that purpose. + + To write your own painted item, you first create a subclass of QQuickPaintedItem, and then + start by implementing its only pure virtual public function: paint(), which implements + the actual painting. To get the size of the area painted by the item, use + contentsBoundingRect(). +*/ + +/*! + \enum QQuickPaintedItem::RenderTarget + + This enum describes QQuickPaintedItem's render targets. The render target is the + surface QPainter paints onto before the item is rendered on screen. + + \value Image The default; QPainter paints into a QImage using the raster paint engine. + The image's content needs to be uploaded to graphics memory afterward, this operation + can potentially be slow if the item is large. This render target allows high quality + anti-aliasing and fast item resizing. + + \value FramebufferObject QPainter paints into a QOpenGLFramebufferObject using the GL + paint engine. Painting can be faster as no texture upload is required, but anti-aliasing + quality is not as good as if using an image. This render target allows faster rendering + in some cases, but you should avoid using it if the item is resized often. + + \sa setRenderTarget() +*/ + +/*! + \enum QQuickPaintedItem::PerformanceHint + + This enum describes flags that you can enable to improve rendering + performance in QQuickPaintedItem. By default, none of these flags are set. + + \value FastFBOResizing If your item gets resized often and you are using the + QQuickPaintedItem::FramebufferObject render target, set this flag to true to reduce the + item resizing time at the cost of using more graphics memory. Resizing a Framebuffer object + is a costly operation, by enabling this property the Framebuffer Object will use a texture + larger than the actual size of the item to avoid as much as possible resizing it. +*/ + +/*! + \internal +*/ +QQuickPaintedItemPrivate::QQuickPaintedItemPrivate() + : QQuickItemPrivate() + , contentsScale(1.0) + , fillColor(Qt::transparent) + , renderTarget(QQuickPaintedItem::Image) + , performanceHints(0) + , geometryDirty(false) + , contentsDirty(false) + , opaquePainting(false) + , antialiasing(false) + , mipmap(false) +{ +} + +/*! + Constructs a QQuickPaintedItem with the given \a parent item. + */ +QQuickPaintedItem::QQuickPaintedItem(QQuickItem *parent) + : QQuickItem(*(new QQuickPaintedItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +/*! + \internal +*/ +QQuickPaintedItem::QQuickPaintedItem(QQuickPaintedItemPrivate &dd, QQuickItem *parent) + : QQuickItem(dd, parent) +{ + setFlag(ItemHasContents); +} + +/*! + Destroys the QQuickPaintedItem. +*/ +QQuickPaintedItem::~QQuickPaintedItem() +{ +} + +/*! + Schedules a redraw of the area covered by \a rect in this item. You can call this function + whenever your item needs to be redrawn, such as if it changes appearance or size. + + This function does not cause an immediate paint; instead it schedules a paint request that + is processed by the QML Scene Graph when the next frame is rendered. The item will only be + redrawn if it is visible. + + Note that calling this function will trigger a repaint of the whole scene. + + \sa paint() +*/ +void QQuickPaintedItem::update(const QRect &rect) +{ + Q_D(QQuickPaintedItem); + d->contentsDirty = true; + + if (rect.isNull() && !d->dirtyRect.isNull()) + d->dirtyRect = contentsBoundingRect().toAlignedRect(); + else + d->dirtyRect |= (contentsBoundingRect() & rect).toAlignedRect(); + QQuickItem::update(); +} + +/*! + Returns true if this item is opaque; otherwise, false is returned. + + By default, painted items are not opaque. + + \sa setOpaquePainting() +*/ +bool QQuickPaintedItem::opaquePainting() const +{ + Q_D(const QQuickPaintedItem); + return d->opaquePainting; +} + +/*! + If \a opaque is true, the item is opaque; otherwise, it is considered as translucent. + + Opaque items are not blended with the rest of the scene, you should set this to true + if the content of the item is opaque to speed up rendering. + + By default, painted items are not opaque. + + \sa opaquePainting() +*/ +void QQuickPaintedItem::setOpaquePainting(bool opaque) +{ + Q_D(QQuickPaintedItem); + + if (d->opaquePainting == opaque) + return; + + d->opaquePainting = opaque; + QQuickItem::update(); +} + +/*! + Returns true if antialiased painting is enabled; otherwise, false is returned. + + By default, antialiasing is not enabled. + + \sa setAntialiasing() +*/ +bool QQuickPaintedItem::antialiasing() const +{ + Q_D(const QQuickPaintedItem); + return d->antialiasing; +} + +/*! + If \a enable is true, antialiased painting is enabled. + + By default, antialiasing is not enabled. + + \sa antialiasing() +*/ +void QQuickPaintedItem::setAntialiasing(bool enable) +{ + Q_D(QQuickPaintedItem); + + if (d->antialiasing == enable) + return; + + d->antialiasing = enable; + update(); +} + +/*! + Returns true if mipmaps are enabled; otherwise, false is returned. + + By default, mipmapping is not enabled. + + \sa setMipmap() +*/ +bool QQuickPaintedItem::mipmap() const +{ + Q_D(const QQuickPaintedItem); + return d->mipmap; +} + +/*! + If \a enable is true, mipmapping is enabled on the associated texture. + + Mipmapping increases rendering speed and reduces aliasing artifacts when the item is + scaled down. + + By default, mipmapping is not enabled. + + \sa mipmap() +*/ +void QQuickPaintedItem::setMipmap(bool enable) +{ + Q_D(QQuickPaintedItem); + + if (d->mipmap == enable) + return; + + d->mipmap = enable; + update(); +} + +/*! + Returns the performance hints. + + By default, no performance hint is enabled/ + + \sa setPerformanceHint(), setPerformanceHints() +*/ +QQuickPaintedItem::PerformanceHints QQuickPaintedItem::performanceHints() const +{ + Q_D(const QQuickPaintedItem); + return d->performanceHints; +} + +/*! + Sets the given performance \a hint on the item if \a enabled is true; + otherwise clears the performance hint. + + By default, no performance hint is enabled/ + + \sa setPerformanceHints(), performanceHints() +*/ +void QQuickPaintedItem::setPerformanceHint(QQuickPaintedItem::PerformanceHint hint, bool enabled) +{ + Q_D(QQuickPaintedItem); + PerformanceHints oldHints = d->performanceHints; + if (enabled) + d->performanceHints |= hint; + else + d->performanceHints &= ~hint; + if (oldHints != d->performanceHints) + update(); +} + +/*! + Sets the performance hints to \a hints + + By default, no performance hint is enabled/ + + \sa setPerformanceHint(), performanceHints() +*/ +void QQuickPaintedItem::setPerformanceHints(QQuickPaintedItem::PerformanceHints hints) +{ + Q_D(QQuickPaintedItem); + if (d->performanceHints == hints) + return; + d->performanceHints = hints; + update(); +} + +/*! + This function returns the outer bounds of the item as a rectangle; all painting must be + restricted to inside an item's bounding rect. + + If the contents size has not been set it reflects the size of the item; otherwise + it reflects the contents size scaled by the contents scale. + + Use this function to know the area painted by the item. + + \sa QQuickItem::width(), QQuickItem::height(), contentsSize(), contentsScale() +*/ +QRectF QQuickPaintedItem::contentsBoundingRect() const +{ + Q_D(const QQuickPaintedItem); + + qreal w = d->width; + QSizeF sz = d->contentsSize * d->contentsScale; + if (w < sz.width()) + w = sz.width(); + qreal h = d->height; + if (h < sz.height()) + h = sz.height(); + + return QRectF(0, 0, w, h); +} + +/*! + \property QQuickPaintedItem::contentsSize + \brief The size of the contents + + The contents size is the size of the item in regards to how it is painted + using the paint() function. This is distinct from the size of the + item in regards to height() and width(). +*/ +QSize QQuickPaintedItem::contentsSize() const +{ + Q_D(const QQuickPaintedItem); + return d->contentsSize; +} + +void QQuickPaintedItem::setContentsSize(const QSize &size) +{ + Q_D(QQuickPaintedItem); + + if (d->contentsSize == size) + return; + + d->contentsSize = size; + update(); +} + +/*! + This convenience function is equivalent to calling setContentsSize(QSize()). +*/ +void QQuickPaintedItem::resetContentsSize() +{ + setContentsSize(QSize()); +} + +/*! + \property QQuickPaintedItem::contentsScale + \brief The scale of the contents + + All painting happening in paint() is scaled by the contents scale. This is distinct + from the scale of the item in regards to scale(). + + The default value is 1. +*/ +qreal QQuickPaintedItem::contentsScale() const +{ + Q_D(const QQuickPaintedItem); + return d->contentsScale; +} + +void QQuickPaintedItem::setContentsScale(qreal scale) +{ + Q_D(QQuickPaintedItem); + + if (d->contentsScale == scale) + return; + + d->contentsScale = scale; + update(); +} + +/*! + \property QQuickPaintedItem::fillColor + \brief The item's background fill color. + + By default, the fill color is set to Qt::transparent. +*/ +QColor QQuickPaintedItem::fillColor() const +{ + Q_D(const QQuickPaintedItem); + return d->fillColor; +} + +void QQuickPaintedItem::setFillColor(const QColor &c) +{ + Q_D(QQuickPaintedItem); + + if (d->fillColor == c) + return; + + d->fillColor = c; + update(); + + emit fillColorChanged(); +} + +/*! + \property QQuickPaintedItem::renderTarget + \brief The item's render target. + + This property defines which render target the QPainter renders into, it can be either + QQuickPaintedItem::Image or QQuickPaintedItem::FramebufferObject. Both have certains benefits, + typically performance versus quality. Using a framebuffer object avoids a costly upload + of the image contents to the texture in graphics memory, while using an image enables + high quality anti-aliasing. + + \warning Resizing a framebuffer object is a costly operation, avoid using + the QQuickPaintedItem::FramebufferObject render target if the item gets resized often. + + By default, the render target is QQuickPaintedItem::Image. +*/ +QQuickPaintedItem::RenderTarget QQuickPaintedItem::renderTarget() const +{ + Q_D(const QQuickPaintedItem); + return d->renderTarget; +} + +void QQuickPaintedItem::setRenderTarget(RenderTarget target) +{ + Q_D(QQuickPaintedItem); + + if (d->renderTarget == target) + return; + + d->renderTarget = target; + update(); + + emit renderTargetChanged(); +} + +/*! + \fn virtual void QQuickPaintedItem::paint(QPainter *painter) = 0 + + This function, which is usually called by the QML Scene Graph, paints the + contents of an item in local coordinates. + + The function is called after the item has been filled with the fillColor. + + Reimplement this function in a QQuickPaintedItem subclass to provide the + item's painting implementation, using \a painter. + + \note The QML Scene Graph uses two separate threads, the main thread does things such as + processing events or updating animations while a second thread does the actual OpenGL rendering. + As a consequence, paint() is not called from the main GUI thread but from the GL enabled + renderer thread. At the moment paint() is called, the GUI thread is blocked and this is + therefore thread-safe. +*/ + +/*! + This function is called after the item's geometry has changed. +*/ +void QQuickPaintedItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickPaintedItem); + d->geometryDirty = true; + QQuickItem::geometryChanged(newGeometry, oldGeometry); +} + + +/*! + This function is called when the Scene Graph node associated to the item needs to + be updated. +*/ +QSGNode *QQuickPaintedItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + Q_D(QQuickPaintedItem); + + if (width() <= 0 || height() <= 0) { + delete oldNode; + return 0; + } + + QSGPainterNode *node = static_cast(oldNode); + if (!node) + node = new QSGPainterNode(this); + + QRectF br = contentsBoundingRect(); + + node->setPreferredRenderTarget(d->renderTarget); + node->setFastFBOResizing(d->performanceHints & FastFBOResizing); + node->setSize(QSize(qRound(br.width()), qRound(br.height()))); + node->setSmoothPainting(d->antialiasing); + node->setLinearFiltering(d->smooth); + node->setMipmapping(d->mipmap); + node->setOpaquePainting(d->opaquePainting); + node->setFillColor(d->fillColor); + node->setContentsScale(d->contentsScale); + node->setDirty(d->contentsDirty || d->geometryDirty, d->dirtyRect); + node->update(); + + d->contentsDirty = false; + d->geometryDirty = false; + d->dirtyRect = QRect(); + + return node; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickpainteditem.h b/src/declarative/items/qquickpainteditem.h new file mode 100644 index 0000000000..5f461fd711 --- /dev/null +++ b/src/declarative/items/qquickpainteditem.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPAINTEDITEM_P_H +#define QQUICKPAINTEDITEM_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickPaintedItemPrivate; +class Q_DECLARATIVE_EXPORT QQuickPaintedItem : public QQuickItem +{ + Q_OBJECT + Q_ENUMS(RenderTarget) + + Q_PROPERTY(QSize contentsSize READ contentsSize WRITE setContentsSize NOTIFY contentsSizeChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(qreal contentsScale READ contentsScale WRITE setContentsScale NOTIFY contentsScaleChanged) + Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged) +public: + QQuickPaintedItem(QQuickItem *parent = 0); + virtual ~QQuickPaintedItem(); + + enum RenderTarget { + Image, + FramebufferObject + }; + + enum PerformanceHint { + FastFBOResizing = 0x1 + }; + Q_DECLARE_FLAGS(PerformanceHints, PerformanceHint) + + void update(const QRect &rect = QRect()); + + bool opaquePainting() const; + void setOpaquePainting(bool opaque); + + bool antialiasing() const; + void setAntialiasing(bool enable); + + bool mipmap() const; + void setMipmap(bool enable); + + PerformanceHints performanceHints() const; + void setPerformanceHint(PerformanceHint hint, bool enabled = true); + void setPerformanceHints(PerformanceHints hints); + + QRectF contentsBoundingRect() const; + + QSize contentsSize() const; + void setContentsSize(const QSize &); + void resetContentsSize(); + + qreal contentsScale() const; + void setContentsScale(qreal); + + QColor fillColor() const; + void setFillColor(const QColor&); + + RenderTarget renderTarget() const; + void setRenderTarget(RenderTarget target); + + virtual void paint(QPainter *painter) = 0; + +Q_SIGNALS: + void fillColorChanged(); + void contentsSizeChanged(); + void contentsScaleChanged(); + void renderTargetChanged(); + +protected: + QQuickPaintedItem(QQuickPaintedItemPrivate &dd, QQuickItem *parent = 0); + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private: + Q_DISABLE_COPY(QQuickPaintedItem) + Q_DECLARE_PRIVATE(QQuickPaintedItem) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPaintedItem::PerformanceHints) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKPAINTEDITEM_P_H diff --git a/src/declarative/items/qquickpainteditem_p.h b/src/declarative/items/qquickpainteditem_p.h new file mode 100644 index 0000000000..00061d9977 --- /dev/null +++ b/src/declarative/items/qquickpainteditem_p.h @@ -0,0 +1,73 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPAINTEDITEM_P_P_H +#define QQUICKPAINTEDITEM_P_P_H + +#include "qquickitem_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QQuickPaintedItemPrivate : public QQuickItemPrivate +{ +public: + QQuickPaintedItemPrivate(); + + QSize contentsSize; + qreal contentsScale; + QColor fillColor; + QQuickPaintedItem::RenderTarget renderTarget; + QQuickPaintedItem::PerformanceHints performanceHints; + + QRect dirtyRect; + + bool geometryDirty : 1; + bool contentsDirty : 1; + bool opaquePainting: 1; + bool antialiasing: 1; + bool mipmap: 1; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPAINTEDITEM_P_P_H diff --git a/src/declarative/items/qquickpathview.cpp b/src/declarative/items/qquickpathview.cpp new file mode 100644 index 0000000000..dc73bf2dd3 --- /dev/null +++ b/src/declarative/items/qquickpathview.cpp @@ -0,0 +1,1738 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpathview_p.h" +#include "qquickpathview_p_p.h" +#include "qquickcanvas.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +inline qreal qmlMod(qreal x, qreal y) +{ +#ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return fmodf(float(x), float(y)); + else +#endif + return fmod(x, y); +} + +static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0; + +QQuickPathViewAttached::QQuickPathViewAttached(QObject *parent) +: QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false) +{ + if (qPathViewAttachedType) { + m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType); + m_metaobject->setCached(true); + } else { + m_metaobject = new QDeclarativeOpenMetaObject(this); + } +} + +QQuickPathViewAttached::~QQuickPathViewAttached() +{ +} + +QVariant QQuickPathViewAttached::value(const QByteArray &name) const +{ + return m_metaobject->value(name); +} +void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &val) +{ + m_metaobject->setValue(name, val); +} + + +void QQuickPathViewPrivate::init() +{ + Q_Q(QQuickPathView); + offset = 0; + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QQuickItem::ItemIsFocusScope); + q->setFiltersChildMouseEvents(true); + FAST_CONNECT(&tl, SIGNAL(updated()), q, SLOT(ticked())) + lastPosTime.invalidate(); + FAST_CONNECT(&tl, SIGNAL(completed()), q, SLOT(movementEnding())) +} + +QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, bool onPath) +{ + Q_Q(QQuickPathView); + requestedIndex = modelIndex; + QQuickItem *item = model->item(modelIndex, false); + if (item) { + if (!attType) { + // pre-create one metatype to share with all attached objects + attType = new QDeclarativeOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q)); + foreach (const QString &attr, path->attributes()) + attType->createProperty(attr.toUtf8()); + } + qPathViewAttachedType = attType; + QQuickPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); + qPathViewAttachedType = 0; + if (att) { + att->m_view = q; + att->setOnPath(onPath); + } + item->setParentItem(q); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); + } + requestedIndex = -1; + return item; +} + +void QQuickPathViewPrivate::releaseItem(QQuickItem *item) +{ + if (!item || !model) + return; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + if (model->release(item) == 0) { + // item was not destroyed, and we no longer reference it. + if (QQuickPathViewAttached *att = attached(item)) + att->setOnPath(false); + } +} + +QQuickPathViewAttached *QQuickPathViewPrivate::attached(QQuickItem *item) +{ + return static_cast(qmlAttachedPropertiesObject(item, false)); +} + +void QQuickPathViewPrivate::clear() +{ + if (currentItem) { + releaseItem(currentItem); + currentItem = 0; + } + for (int i=0; i= 0 && index < modelCount) { + qreal start = 0.0; + if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) + start = highlightRangeStart; + qreal globalPos = index + offset; + globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount; + if (pathItems != -1 && pathItems < modelCount) { + globalPos += start * mappedRange; + globalPos = qmlMod(globalPos, 1.0); + if (globalPos < mappedRange) + pos = globalPos / mappedRange; + } else { + pos = qmlMod(globalPos + start, 1.0); + } + } + + return pos; +} + +void QQuickPathViewPrivate::createHighlight() +{ + Q_Q(QQuickPathView); + if (!q->isComponentComplete()) + return; + + bool changed = false; + if (highlightItem) { + highlightItem->setParentItem(0); + highlightItem->deleteLater(); + highlightItem = 0; + changed = true; + } + + QQuickItem *item = 0; + if (highlightComponent) { + QDeclarativeContext *creationContext = highlightComponent->creationContext(); + QDeclarativeContext *highlightContext = new QDeclarativeContext( + creationContext ? creationContext : qmlContext(q)); + QObject *nobj = highlightComponent->create(highlightContext); + if (nobj) { + QDeclarative_setParent_noEvent(highlightContext, nobj); + item = qobject_cast(nobj); + if (!item) + delete nobj; + } else { + delete highlightContext; + } + } else { + item = new QQuickItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q); + item->setParentItem(q); + highlightItem = item; + changed = true; + } + if (changed) + emit q->highlightItemChanged(); +} + +void QQuickPathViewPrivate::updateHighlight() +{ + Q_Q(QQuickPathView); + if (!q->isComponentComplete() || !isValid()) + return; + if (highlightItem) { + if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) { + updateItem(highlightItem, highlightRangeStart); + } else { + qreal target = currentIndex; + + offsetAdj = 0.0; + tl.reset(moveHighlight); + moveHighlight.setValue(highlightPosition); + + const int duration = highlightMoveDuration; + + if (target - highlightPosition > modelCount/2) { + highlightUp = false; + qreal distance = modelCount - target + highlightPosition; + tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance)); + tl.set(moveHighlight, modelCount-0.01); + tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance)); + } else if (target - highlightPosition <= -modelCount/2) { + highlightUp = true; + qreal distance = modelCount - highlightPosition + target; + tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance)); + tl.set(moveHighlight, 0.0); + tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance)); + } else { + highlightUp = highlightPosition - target < 0; + tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + } + } +} + +void QQuickPathViewPrivate::setHighlightPosition(qreal pos) +{ + if (pos != highlightPosition) { + qreal start = 0.0; + qreal end = 1.0; + if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) { + start = highlightRangeStart; + end = highlightRangeEnd; + } + + qreal range = qreal(modelCount); + // calc normalized position of highlight relative to offset + qreal relativeHighlight = qmlMod(pos + offset, range) / range; + + if (!highlightUp && relativeHighlight > end * mappedRange) { + qreal diff = 1.0 - relativeHighlight; + setOffset(offset + diff * range); + } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) { + qreal diff = relativeHighlight - (end - start) * mappedRange; + setOffset(offset - diff * range - 0.00001); + } + + highlightPosition = pos; + qreal pathPos = positionOfIndex(pos); + updateItem(highlightItem, pathPos); + if (QQuickPathViewAttached *att = attached(highlightItem)) + att->setOnPath(pathPos != -1.0); + } +} + +void QQuickPathView::pathUpdated() +{ + Q_D(QQuickPathView); + QList::iterator it = d->items.begin(); + while (it != d->items.end()) { + QQuickItem *item = *it; + if (QQuickPathViewAttached *att = d->attached(item)) + att->m_percent = -1; + ++it; + } + refill(); +} + +void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent) +{ + if (QQuickPathViewAttached *att = attached(item)) { + if (qFuzzyCompare(att->m_percent, percent)) + return; + att->m_percent = percent; + foreach (const QString &attr, path->attributes()) + att->setValue(attr.toUtf8(), path->attributeAt(attr, percent)); + } + QPointF pf = path->pointAt(percent); + item->setX(qRound(pf.x() - item->width()/2)); + item->setY(qRound(pf.y() - item->height()/2)); +} + +void QQuickPathViewPrivate::regenerate() +{ + Q_Q(QQuickPathView); + if (!q->isComponentComplete()) + return; + + clear(); + + if (!isValid()) + return; + + firstIndex = -1; + updateMappedRange(); + q->refill(); +} + +/*! + \qmlclass PathView QQuickPathView + \inqmlmodule QtQuick 2 + \ingroup qml-view-elements + \brief The PathView element lays out model-provided items on a path. + \inherits Item + + A PathView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + The view has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. + The \l delegate is instantiated for each item on the \l path. + The items may be flicked to move them along the path. + + For example, if there is a simple list model defined in a file \c ContactModel.qml like this: + + \snippet doc/src/snippets/declarative/pathview/ContactModel.qml 0 + + This data can be represented as a PathView, like this: + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 0 + + \image pathview.gif + + (Note the above example uses PathAttribute to scale and modify the + opacity of the items as they rotate. This additional code can be seen in the + PathAttribute documentation.) + + PathView does not automatically handle keyboard navigation. This is because + the keys to use for navigation will depend upon the shape of the path. Navigation + can be added quite simply by setting \c focus to \c true and calling + \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate + using the left and right arrow keys: + + \qml + PathView { + // ... + focus: true + Keys.onLeftPressed: decrementCurrentIndex() + Keys.onRightPressed: incrementCurrentIndex() + } + \endqml + + The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + PathView attaches a number of properties to the root item of the delegate, for example + \c {PathView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c PathView.isCurrentItem, while the child + \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem. + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 + + \bold Note that views do not enable \e clip automatically. If the view + is not clipped by another item or the screen, it will be necessary + to set \e {clip: true} in order to have the out of view items clipped + nicely. + + \sa Path, {declarative/modelviews/pathview}{PathView example} +*/ + +QQuickPathView::QQuickPathView(QQuickItem *parent) + : QQuickItem(*(new QQuickPathViewPrivate), parent) +{ + Q_D(QQuickPathView); + d->init(); +} + +QQuickPathView::~QQuickPathView() +{ + Q_D(QQuickPathView); + d->clear(); + if (d->attType) + d->attType->release(); + if (d->ownModel) + delete d->model; +} + +/*! + \qmlattachedproperty PathView QtQuick2::PathView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty bool QtQuick2::PathView::onPath + This attached property holds whether the item is currently on the path. + + If a pathItemCount has been set, it is possible that some items may + be instantiated, but not considered to be currently on the path. + Usually, these items would be set invisible, for example: + + \qml + Component { + Rectangle { + visible: PathView.onPath + // ... + } + } + \endqml + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty bool QtQuick2::PathView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current item. + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 +*/ + +/*! + \qmlproperty model QtQuick2::PathView::model + This property holds the model providing data for the view. + + The model provides a set of data that is used to create the items for the view. + For large or dynamic datasets the model is usually provided by a C++ model object. + Models can also be created directly in QML, using the ListModel element. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QQuickPathView::model() const +{ + Q_D(const QQuickPathView); + return d->modelVariant; +} + +void QQuickPathView::setModel(const QVariant &model) +{ + Q_D(QQuickPathView); + if (d->modelVariant == model) + return; + + if (d->model) { + disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + for (int i=0; iitems.count(); i++){ + QQuickItem *p = d->items[i]; + d->model->release(p); + } + d->items.clear(); + } + + d->modelVariant = model; + QObject *object = qvariant_cast(model); + QQuickVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this)); + d->ownModel = true; + if (isComponentComplete()) + static_cast(d->model.data())->componentComplete(); + } + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + d->modelCount = 0; + if (d->model) { + connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + d->modelCount = d->model->count(); + if (d->model->count()) + d->offset = qmlMod(d->offset, qreal(d->model->count())); + if (d->offset < 0) + d->offset = d->model->count() + d->offset; +} + d->regenerate(); + if (d->currentIndex < d->modelCount) + setOffset(qmlMod(d->modelCount - d->currentIndex, d->modelCount)); + else + d->fixOffset(); + emit countChanged(); + emit modelChanged(); +} + +/*! + \qmlproperty int QtQuick2::PathView::count + This property holds the number of items in the model. +*/ +int QQuickPathView::count() const +{ + Q_D(const QQuickPathView); + return d->model ? d->modelCount : 0; +} + +/*! + \qmlproperty Path QtQuick2::PathView::path + This property holds the path used to lay out the items. + For more information see the \l Path documentation. +*/ +QDeclarativePath *QQuickPathView::path() const +{ + Q_D(const QQuickPathView); + return d->path; +} + +void QQuickPathView::setPath(QDeclarativePath *path) +{ + Q_D(QQuickPathView); + if (d->path == path) + return; + if (d->path) + disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated())); + d->path = path; + connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated())); + if (d->isValid() && isComponentComplete()) { + d->clear(); + if (d->attType) { + d->attType->release(); + d->attType = 0; + } + d->regenerate(); + } + emit pathChanged(); +} + +/*! + \qmlproperty int QtQuick2::PathView::currentIndex + This property holds the index of the current item. +*/ +int QQuickPathView::currentIndex() const +{ + Q_D(const QQuickPathView); + return d->currentIndex; +} + +void QQuickPathView::setCurrentIndex(int idx) +{ + Q_D(QQuickPathView); + if (d->model && d->modelCount) + idx = qAbs(idx % d->modelCount); + if (d->model && idx != d->currentIndex) { + if (d->currentItem) { + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(false); + d->releaseItem(d->currentItem); + } + d->currentItem = 0; + d->moveReason = QQuickPathViewPrivate::SetIndex; + d->currentIndex = idx; + if (d->modelCount) { + int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount; + if (itemIndex < d->items.count()) { + d->currentItem = d->model->item(d->currentIndex, true); + d->currentItem->setFocus(true); + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + } else { + d->currentItem = d->getItem(d->currentIndex, false); + d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0); + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + if (d->model->completePending()) + d->model->completeItem(); + } + if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) + d->snapToCurrent(); + d->currentItemOffset = d->positionOfIndex(d->currentIndex); + d->updateHighlight(); + } + emit currentIndexChanged(); + } +} + +QQuickItem *QQuickPathView::currentItem() const +{ + Q_D(const QQuickPathView); + return d->currentItem; +} + +/*! + \qmlmethod QtQuick2::PathView::incrementCurrentIndex() + + Increments the current index. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QQuickPathView::incrementCurrentIndex() +{ + Q_D(QQuickPathView); + d->moveDirection = QQuickPathViewPrivate::Positive; + setCurrentIndex(currentIndex()+1); +} + +/*! + \qmlmethod QtQuick2::PathView::decrementCurrentIndex() + + Decrements the current index. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QQuickPathView::decrementCurrentIndex() +{ + Q_D(QQuickPathView); + if (d->model && d->modelCount) { + int idx = currentIndex()-1; + if (idx < 0) + idx = d->modelCount - 1; + d->moveDirection = QQuickPathViewPrivate::Negative; + setCurrentIndex(idx); + } +} + +/*! + \qmlproperty real QtQuick2::PathView::offset + + The offset specifies how far along the path the items are from their initial positions. + This is a real number that ranges from 0.0 to the count of items in the model. +*/ +qreal QQuickPathView::offset() const +{ + Q_D(const QQuickPathView); + return d->offset; +} + +void QQuickPathView::setOffset(qreal offset) +{ + Q_D(QQuickPathView); + d->setOffset(offset); + d->updateCurrent(); +} + +void QQuickPathViewPrivate::setOffset(qreal o) +{ + Q_Q(QQuickPathView); + if (offset != o) { + if (isValid() && q->isComponentComplete()) { + offset = qmlMod(o, qreal(modelCount)); + if (offset < 0) + offset += qreal(modelCount); + q->refill(); + } else { + offset = o; + } + emit q->offsetChanged(); + } +} + +void QQuickPathViewPrivate::setAdjustedOffset(qreal o) +{ + setOffset(o+offsetAdj); +} + +/*! + \qmlproperty Component QtQuick2::PathView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component will be created for each view. + The geometry of the resultant component instance will be managed by the view + so as to stay with the current item. + + The below example demonstrates how to make a simple highlight. Note the use + of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that + the highlight is hidden when flicked away from the path. + + \qml + Component { + Rectangle { + visible: PathView.onPath + // ... + } + } + \endqml + + \sa highlightItem, highlightRangeMode +*/ + +QDeclarativeComponent *QQuickPathView::highlight() const +{ + Q_D(const QQuickPathView); + return d->highlightComponent; +} + +void QQuickPathView::setHighlight(QDeclarativeComponent *highlight) +{ + Q_D(QQuickPathView); + if (highlight != d->highlightComponent) { + d->highlightComponent = highlight; + d->createHighlight(); + d->updateHighlight(); + emit highlightChanged(); + } +} + +/*! + \qmlproperty Item QtQuick2::PathView::highlightItem + + \c highlightItem holds the highlight item, which was created + from the \l highlight component. + + \sa highlight +*/ +QQuickItem *QQuickPathView::highlightItem() +{ + Q_D(const QQuickPathView); + return d->highlightItem; +} +/*! + \qmlproperty real QtQuick2::PathView::preferredHighlightBegin + \qmlproperty real QtQuick2::PathView::preferredHighlightEnd + \qmlproperty enumeration QtQuick2::PathView::highlightRangeMode + + These properties set the preferred range of the highlight (current item) + within the view. The preferred values must be in the range 0.0-1.0. + + If highlightRangeMode is set to \e PathView.NoHighlightRange + + If highlightRangeMode is set to \e PathView.ApplyRange the view will + attempt to maintain the highlight within the range, however + the highlight can move outside of the range at the ends of the path + or due to a mouse interaction. + + If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never + move outside of the range. This means that the current item will change + if a keyboard or mouse action would cause the highlight to move + outside of the range. + + Note that this is the correct way to influence where the + current item ends up when the view moves. For example, if you want the + currently selected item to be in the middle of the path, then set the + highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange. + Then, when the path scrolls, + the currently selected item will be the item at that position. This also applies to + when the currently selected item changes - it will scroll to within the preferred + highlight range. Furthermore, the behaviour of the current item index will occur + whether or not a highlight exists. + + The default value is \e PathView.StrictlyEnforceRange. + + Note that a valid range requires preferredHighlightEnd to be greater + than or equal to preferredHighlightBegin. +*/ +qreal QQuickPathView::preferredHighlightBegin() const +{ + Q_D(const QQuickPathView); + return d->highlightRangeStart; +} + +void QQuickPathView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QQuickPathView); + if (d->highlightRangeStart == start || start < 0 || start > 1.0) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + refill(); + emit preferredHighlightBeginChanged(); +} + +qreal QQuickPathView::preferredHighlightEnd() const +{ + Q_D(const QQuickPathView); + return d->highlightRangeEnd; +} + +void QQuickPathView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QQuickPathView); + if (d->highlightRangeEnd == end || end < 0 || end > 1.0) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + refill(); + emit preferredHighlightEndChanged(); +} + +QQuickPathView::HighlightRangeMode QQuickPathView::highlightRangeMode() const +{ + Q_D(const QQuickPathView); + return d->highlightRangeMode; +} + +void QQuickPathView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QQuickPathView); + if (d->highlightRangeMode == mode) + return; + d->highlightRangeMode = mode; + d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +/*! + \qmlproperty int QtQuick2::PathView::highlightMoveDuration + This property holds the move animation duration of the highlight delegate. + + If the highlightRangeMode is StrictlyEnforceRange then this property + determines the speed that the items move along the path. + + The default value for the duration is 300ms. +*/ +int QQuickPathView::highlightMoveDuration() const +{ + Q_D(const QQuickPathView); + return d->highlightMoveDuration; +} + +void QQuickPathView::setHighlightMoveDuration(int duration) +{ + Q_D(QQuickPathView); + if (d->highlightMoveDuration == duration) + return; + d->highlightMoveDuration = duration; + emit highlightMoveDurationChanged(); +} + +/*! + \qmlproperty real QtQuick2::PathView::dragMargin + This property holds the maximum distance from the path that initiate mouse dragging. + + By default the path can only be dragged by clicking on an item. If + dragMargin is greater than zero, a drag can be initiated by clicking + within dragMargin pixels of the path. +*/ +qreal QQuickPathView::dragMargin() const +{ + Q_D(const QQuickPathView); + return d->dragMargin; +} + +void QQuickPathView::setDragMargin(qreal dragMargin) +{ + Q_D(QQuickPathView); + if (d->dragMargin == dragMargin) + return; + d->dragMargin = dragMargin; + emit dragMarginChanged(); +} + +/*! + \qmlproperty real QtQuick2::PathView::flickDeceleration + This property holds the rate at which a flick will decelerate. + + The default is 100. +*/ +qreal QQuickPathView::flickDeceleration() const +{ + Q_D(const QQuickPathView); + return d->deceleration; +} + +void QQuickPathView::setFlickDeceleration(qreal dec) +{ + Q_D(QQuickPathView); + if (d->deceleration == dec) + return; + d->deceleration = dec; + emit flickDecelerationChanged(); +} + +/*! + \qmlproperty bool QtQuick2::PathView::interactive + + A user cannot drag or flick a PathView that is not interactive. + + This property is useful for temporarily disabling flicking. This allows + special interaction with PathView's children. +*/ +bool QQuickPathView::isInteractive() const +{ + Q_D(const QQuickPathView); + return d->interactive; +} + +void QQuickPathView::setInteractive(bool interactive) +{ + Q_D(QQuickPathView); + if (interactive != d->interactive) { + d->interactive = interactive; + if (!interactive) + d->tl.clear(); + emit interactiveChanged(); + } +} + +/*! + \qmlproperty bool QtQuick2::PathView::moving + + This property holds whether the view is currently moving + due to the user either dragging or flicking the view. +*/ +bool QQuickPathView::isMoving() const +{ + Q_D(const QQuickPathView); + return d->moving; +} + +/*! + \qmlproperty bool QtQuick2::PathView::flicking + + This property holds whether the view is currently moving + due to the user flicking the view. +*/ +bool QQuickPathView::isFlicking() const +{ + Q_D(const QQuickPathView); + return d->flicking; +} + +/*! + \qmlsignal QtQuick2::PathView::onMovementStarted() + + This handler is called when the view begins moving due to user + interaction. +*/ + +/*! + \qmlsignal QtQuick2::PathView::onMovementEnded() + + This handler is called when the view stops moving due to user + interaction. If a flick was generated, this handler will + be triggered once the flick stops. If a flick was not + generated, the handler will be triggered when the + user stops dragging - i.e. a mouse or touch release. +*/ + +/*! + \qmlsignal QtQuick2::PathView::onFlickStarted() + + This handler is called when the view is flicked. A flick + starts from the point that the mouse or touch is released, + while still in motion. +*/ + +/*! + \qmlsignal QtQuick2::PathView::onFlickEnded() + + This handler is called when the view stops moving due to a flick. +*/ + +/*! + \qmlproperty Component QtQuick2::PathView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view when pathItemCount is specified. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + Note that the PathView will layout the items based on the size of the root + item in the delegate. + + Here is an example delegate: + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 +*/ +QDeclarativeComponent *QQuickPathView::delegate() const +{ + Q_D(const QQuickPathView); + if (d->model) { + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QQuickPathView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QQuickPathView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + d->modelCount = dataModel->count(); + d->regenerate(); + if (oldCount != dataModel->count()) + emit countChanged(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int QtQuick2::PathView::pathItemCount + This property holds the number of items visible on the path at any one time. +*/ +int QQuickPathView::pathItemCount() const +{ + Q_D(const QQuickPathView); + return d->pathItems; +} + +void QQuickPathView::setPathItemCount(int i) +{ + Q_D(QQuickPathView); + if (i == d->pathItems) + return; + if (i < 1) + i = 1; + d->pathItems = i; + d->updateMappedRange(); + if (d->isValid() && isComponentComplete()) { + d->regenerate(); + } + emit pathItemCountChanged(); +} + +QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const +{ + //XXX maybe do recursively at increasing resolution. + qreal mindist = 1e10; // big number + QPointF nearPoint = path->pointAt(0); + qreal nearPc = 0; + for (qreal i=1; i < 1000; i++) { + QPointF pt = path->pointAt(i/1000.0); + QPointF diff = pt - point; + qreal dist = diff.x()*diff.x() + diff.y()*diff.y(); + if (dist < mindist) { + nearPoint = pt; + nearPc = i; + mindist = dist; + } + } + + if (nearPercent) + *nearPercent = nearPc / 1000.0; + + return nearPoint; +} + +void QQuickPathView::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickPathView); + if (d->interactive) { + d->handleMousePressEvent(event); + event->accept(); + } else { + QQuickItem::mousePressEvent(event); + } +} + +void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event) +{ + Q_Q(QQuickPathView); + if (!interactive || !items.count()) + return; + QPointF scenePoint = q->mapToScene(event->localPos()); + int idx = 0; + for (; idx < items.count(); ++idx) { + QRectF rect = items.at(idx)->boundingRect(); + rect = items.at(idx)->mapRectToScene(rect); + if (rect.contains(scenePoint)) + break; + } + if (idx == items.count() && dragMargin == 0.) // didn't click on an item + return; + + startPoint = pointNear(event->localPos(), &startPc); + if (idx == items.count()) { + qreal distance = qAbs(event->localPos().x() - startPoint.x()) + qAbs(event->localPos().y() - startPoint.y()); + if (distance > dragMargin) + return; + } + + if (tl.isActive() && flicking) + stealMouse = true; // If we've been flicked then steal the click. + else + stealMouse = false; + + lastElapsed = 0; + lastDist = 0; + QQuickItemPrivate::start(lastPosTime); + tl.clear(); +} + +void QQuickPathView::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickPathView); + if (d->interactive) { + d->handleMouseMoveEvent(event); + if (d->stealMouse) + setKeepMouseGrab(true); + event->accept(); + } else { + QQuickItem::mouseMoveEvent(event); + } +} + +void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event) +{ + Q_Q(QQuickPathView); + if (!interactive || !lastPosTime.isValid()) + return; + + qreal newPc; + QPointF pathPoint = pointNear(event->localPos(), &newPc); + if (!stealMouse) { + QPointF delta = pathPoint - startPoint; + if (qAbs(delta.x()) > qApp->styleHints()->startDragDistance() || qAbs(delta.y()) > qApp->styleHints()->startDragDistance()) { + stealMouse = true; + startPc = newPc; + } + } + + if (stealMouse) { + moveReason = QQuickPathViewPrivate::Mouse; + qreal diff = (newPc - startPc)*modelCount*mappedRange; + if (diff) { + q->setOffset(offset + diff); + + if (diff > modelCount/2) + diff -= modelCount; + else if (diff < -modelCount/2) + diff += modelCount; + + lastElapsed = QQuickItemPrivate::restart(lastPosTime); + lastDist = diff; + startPc = newPc; + } + if (!moving) { + moving = true; + emit q->movingChanged(); + emit q->movementStarted(); + } + } +} + +void QQuickPathView::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickPathView); + if (d->interactive) { + d->handleMouseReleaseEvent(event); + event->accept(); + ungrabMouse(); + } else { + QQuickItem::mouseReleaseEvent(event); + } +} + +void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *) +{ + Q_Q(QQuickPathView); + stealMouse = false; + q->setKeepMouseGrab(false); + if (!interactive || !lastPosTime.isValid()) + return; + + qreal elapsed = qreal(lastElapsed + QQuickItemPrivate::elapsed(lastPosTime)) / 1000.; + qreal velocity = elapsed > 0. ? lastDist / elapsed : 0; + if (model && modelCount && qAbs(velocity) > 1.) { + qreal count = pathItems == -1 ? modelCount : pathItems; + if (qAbs(velocity) > count * 2) // limit velocity + velocity = (velocity > 0 ? count : -count) * 2; + // Calculate the distance to be travelled + qreal v2 = velocity*velocity; + qreal accel = deceleration/10; + // + 0.25 to encourage moving at least one item in the flick direction + qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25)); + if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) { + // round to nearest item. + if (velocity > 0.) + dist = qRound(dist + offset) - offset; + else + dist = qRound(dist - offset) + offset; + // Calculate accel required to stop on item boundary + if (dist <= 0.) { + dist = 0.; + accel = 0.; + } else { + accel = v2 / (2.0f * qAbs(dist)); + } + } + offsetAdj = 0.0; + moveOffset.setValue(offset); + tl.accel(moveOffset, velocity, accel, dist); + tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this)); + if (!flicking) { + flicking = true; + emit q->flickingChanged(); + emit q->flickStarted(); + } + } else { + fixOffset(); + } + + lastPosTime.invalidate(); + if (!tl.isActive()) + q->movementEnding(); +} + +bool QQuickPathView::sendMouseEvent(QMouseEvent *event) +{ + Q_D(QQuickPathView); + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { + QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + mouseEvent.setAccepted(false); + + switch (mouseEvent.type()) { + case QEvent::MouseMove: + d->handleMouseMoveEvent(&mouseEvent); + break; + case QEvent::MouseButtonPress: + d->handleMousePressEvent(&mouseEvent); + stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above + break; + case QEvent::MouseButtonRelease: + d->handleMouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = c->mouseGrabberItem(); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return d->stealMouse; + } else if (d->lastPosTime.isValid()) { + d->lastPosTime.invalidate(); + d->fixOffset(); + } + if (event->type() == QEvent::MouseButtonRelease) + d->stealMouse = false; + return false; +} + +bool QQuickPathView::childMouseEventFilter(QQuickItem *i, QEvent *e) +{ + Q_D(QQuickPathView); + if (!isVisible() || !d->interactive) + return QQuickItem::childMouseEventFilter(i, e); + + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast(e)); + default: + break; + } + + return QQuickItem::childMouseEventFilter(i, e); +} + +void QQuickPathView::mouseUngrabEvent() +{ + Q_D(QQuickPathView); + if (d->stealMouse) { + // if our mouse grab has been removed (probably by a Flickable), + // fix our state + d->stealMouse = false; + setKeepMouseGrab(false); + d->lastPosTime.invalidate(); + } +} + +void QQuickPathView::updatePolish() +{ + QQuickItem::updatePolish(); + refill(); +} + +void QQuickPathView::componentComplete() +{ + Q_D(QQuickPathView); + if (d->model && d->ownModel) + static_cast(d->model.data())->componentComplete(); + + QQuickItem::componentComplete(); + + d->createHighlight(); + // It is possible that a refill has already happended to to Path + // bindings being handled in the componentComplete(). If so + // don't do it again. + if (d->items.count() == 0 && d->model) { + d->modelCount = d->model->count(); + d->regenerate(); + } + d->updateHighlight(); + + if (d->modelCount) + emit countChanged(); +} + +void QQuickPathView::refill() +{ + Q_D(QQuickPathView); + if (!d->isValid() || !isComponentComplete()) + return; + + d->layoutScheduled = false; + bool currentVisible = false; + + // first move existing items and remove items off path + int idx = d->firstIndex; + QList::iterator it = d->items.begin(); + while (it != d->items.end()) { + qreal pos = d->positionOfIndex(idx); + QQuickItem *item = *it; + if (pos >= 0.0) { + d->updateItem(item, pos); + if (idx == d->currentIndex) { + currentVisible = true; + d->currentItemOffset = pos; + } + ++it; + } else { + // qDebug() << "release"; + d->updateItem(item, 1.0); + d->releaseItem(item); + if (it == d->items.begin()) { + if (++d->firstIndex >= d->modelCount) + d->firstIndex = 0; + } + it = d->items.erase(it); + } + ++idx; + if (idx >= d->modelCount) + idx = 0; + } + if (!d->items.count()) + d->firstIndex = -1; + + if (d->modelCount) { + // add items to beginning and end + int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount); + if (d->items.count() < count) { + int idx = qRound(d->modelCount - d->offset) % d->modelCount; + qreal startPos = 0.0; + if (d->haveHighlightRange && d->highlightRangeMode != QQuickPathView::NoHighlightRange) + startPos = d->highlightRangeStart; + if (d->firstIndex >= 0) { + startPos = d->positionOfIndex(d->firstIndex); + idx = (d->firstIndex + d->items.count()) % d->modelCount; + } + qreal pos = d->positionOfIndex(idx); + while ((pos > startPos || !d->items.count()) && d->items.count() < count) { + // qDebug() << "append" << idx; + QQuickItem *item = d->getItem(idx); + if (d->model->completePending()) + item->setZ(idx+1); + if (d->currentIndex == idx) { + currentVisible = true; + d->currentItemOffset = pos; + } + if (d->items.count() == 0) + d->firstIndex = idx; + d->items.append(item); + d->updateItem(item, pos); + if (d->model->completePending()) + d->model->completeItem(); + ++idx; + if (idx >= d->modelCount) + idx = 0; + pos = d->positionOfIndex(idx); + } + + idx = d->firstIndex - 1; + if (idx < 0) + idx = d->modelCount - 1; + pos = d->positionOfIndex(idx); + while ((pos >= 0.0 && pos < startPos) && d->items.count() < count) { + // qDebug() << "prepend" << idx; + QQuickItem *item = d->getItem(idx); + if (d->model->completePending()) + item->setZ(idx+1); + if (d->currentIndex == idx) { + currentVisible = true; + d->currentItemOffset = pos; + } + d->items.prepend(item); + d->updateItem(item, pos); + if (d->model->completePending()) + d->model->completeItem(); + d->firstIndex = idx; + idx = d->firstIndex - 1; + if (idx < 0) + idx = d->modelCount - 1; + pos = d->positionOfIndex(idx); + } + } + } + + if (!currentVisible) { + d->currentItemOffset = 1.0; + if (d->currentItem) { + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setOnPath(false); + } else if (d->currentIndex >= 0 && d->currentIndex < d->modelCount) { + d->currentItem = d->getItem(d->currentIndex, false); + d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0); + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + if (d->model->completePending()) + d->model->completeItem(); + } + } else if (!d->currentItem) { + d->currentItem = d->model->item(d->currentIndex, true); + d->currentItem->setFocus(true); + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + } + + if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) { + d->updateItem(d->highlightItem, d->highlightRangeStart); + if (QQuickPathViewAttached *att = d->attached(d->highlightItem)) + att->setOnPath(true); + } else if (d->highlightItem && d->moveReason != QQuickPathViewPrivate::SetIndex) { + d->updateItem(d->highlightItem, d->currentItemOffset); + if (QQuickPathViewAttached *att = d->attached(d->highlightItem)) + att->setOnPath(currentVisible); + } + while (d->itemCache.count()) + d->releaseItem(d->itemCache.takeLast()); +} + +void QQuickPathView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) +{ + Q_D(QQuickPathView); + if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete()) + return; + + if (reset) { + d->modelCount = d->model->count(); + d->regenerate(); + emit countChanged(); + return; + } + + if (changeSet.removes().isEmpty() && changeSet.inserts().isEmpty()) + return; + + const int modelCount = d->modelCount; + int moveId = -1; + int moveOffset; + bool currentChanged = false; + bool changedOffset = false; + bool removed = false; + bool inserted = false; + foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) { + removed = true; + if (moveId == -1 && d->currentIndex >= r.index + r.count) { + d->currentIndex -= r.count; + currentChanged = true; + } else if (moveId == -1 && d->currentIndex >= r.index && d->currentIndex < r.index + r.count) { + // current item has been removed. + d->currentIndex = qMin(r.index, d->modelCount - r.count - 1); + if (r.isMove()) { + moveId = r.moveId; + moveOffset = d->currentIndex - r.index; + } else if (d->currentItem) { + if (QQuickPathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + d->releaseItem(d->currentItem); + d->currentItem = 0; + } + currentChanged = true; + } + + if (r.index > d->currentIndex) { + if (d->offset >= r.count) { + changedOffset = true; + d->offset -= r.count; + d->offsetAdj -= r.count; + } + } + d->modelCount -= r.count; + } + foreach (const QDeclarativeChangeSet::Insert &i, changeSet.inserts()) { + inserted = true; + if (d->modelCount) { + if (moveId == -1 && i.index <= d->currentIndex) { + d->currentIndex += i.count; + } else if (d->offset != 0) { + if (moveId != -1 && moveId == i.moveId) + d->currentIndex = i.index + moveOffset; + d->offset += i.count; + d->offsetAdj += i.count; + } + } + d->modelCount += i.count; + } + + d->itemCache += d->items; + d->items.clear(); + + if (!d->modelCount) { + while (d->itemCache.count()) + d->releaseItem(d->itemCache.takeLast()); + d->offset = 0; + changedOffset = true; + d->tl.reset(d->moveOffset); + } else if (removed) { + d->regenerate(); + d->updateCurrent(); + if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) + d->snapToCurrent(); + } else if (inserted) { + d->firstIndex = -1; + d->updateMappedRange(); + d->scheduleLayout(); + } + if (changedOffset) + emit offsetChanged(); + if (currentChanged) + emit currentIndexChanged(); + if (d->modelCount != modelCount) + emit countChanged(); +} + +void QQuickPathView::createdItem(int index, QQuickItem *item) +{ + Q_D(QQuickPathView); + if (d->requestedIndex != index) { + if (!d->attType) { + // pre-create one metatype to share with all attached objects + d->attType = new QDeclarativeOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(this)); + foreach (const QString &attr, d->path->attributes()) + d->attType->createProperty(attr.toUtf8()); + } + qPathViewAttachedType = d->attType; + QQuickPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); + qPathViewAttachedType = 0; + if (att) { + att->m_view = this; + att->setOnPath(false); + } + item->setParentItem(this); + d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0); + } +} + +void QQuickPathView::destroyingItem(QQuickItem *item) +{ + Q_UNUSED(item); +} + +void QQuickPathView::ticked() +{ + Q_D(QQuickPathView); + d->updateCurrent(); +} + +void QQuickPathView::movementEnding() +{ + Q_D(QQuickPathView); + if (d->flicking) { + d->flicking = false; + emit flickingChanged(); + emit flickEnded(); + } + if (d->moving && !d->stealMouse) { + d->moving = false; + emit movingChanged(); + emit movementEnded(); + } +} + +// find the item closest to the snap position +int QQuickPathViewPrivate::calcCurrentIndex() +{ + int current = -1; + if (modelCount && model && items.count()) { + offset = qmlMod(offset, modelCount); + if (offset < 0) + offset += modelCount; + current = qRound(qAbs(qmlMod(modelCount - offset, modelCount))); + current = current % modelCount; + } + + return current; +} + +void QQuickPathViewPrivate::updateCurrent() +{ + Q_Q(QQuickPathView); + if (moveReason != Mouse) + return; + if (!modelCount || !haveHighlightRange || highlightRangeMode != QQuickPathView::StrictlyEnforceRange) + return; + + int idx = calcCurrentIndex(); + if (model && idx != currentIndex) { + if (currentItem) { + if (QQuickPathViewAttached *att = attached(currentItem)) + att->setIsCurrentItem(false); + releaseItem(currentItem); + } + currentIndex = idx; + currentItem = 0; + int itemIndex = (idx - firstIndex + modelCount) % modelCount; + if (itemIndex < items.count()) { + currentItem = model->item(currentIndex, true); + currentItem->setFocus(true); + if (QQuickPathViewAttached *att = attached(currentItem)) + att->setIsCurrentItem(true); + } else if (currentIndex >= 0 && currentIndex < modelCount) { + currentItem = getItem(currentIndex, false); + updateItem(currentItem, currentIndex < firstIndex ? 0.0 : 1.0); + if (QQuickPathViewAttached *att = attached(currentItem)) + att->setIsCurrentItem(true); + if (model->completePending()) + model->completeItem(); + } + emit q->currentIndexChanged(); + } +} + +void QQuickPathViewPrivate::fixOffsetCallback(void *d) +{ + ((QQuickPathViewPrivate *)d)->fixOffset(); +} + +void QQuickPathViewPrivate::fixOffset() +{ + Q_Q(QQuickPathView); + if (model && items.count()) { + if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) { + int curr = calcCurrentIndex(); + if (curr != currentIndex) + q->setCurrentIndex(curr); + else + snapToCurrent(); + } + } +} + +void QQuickPathViewPrivate::snapToCurrent() +{ + if (!model || modelCount <= 0) + return; + + qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount); + + moveReason = Other; + offsetAdj = 0.0; + tl.reset(moveOffset); + moveOffset.setValue(offset); + + const int duration = highlightMoveDuration; + + if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) { + qreal distance = modelCount - targetOffset + offset; + if (targetOffset > moveOffset) { + tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance)); + tl.set(moveOffset, modelCount); + tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance)); + } else { + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) { + qreal distance = modelCount - offset + targetOffset; + if (targetOffset < moveOffset) { + tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance)); + tl.set(moveOffset, 0.0); + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance)); + } else { + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + } else { + tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); + } + moveDirection = Shortest; +} + +QQuickPathViewAttached *QQuickPathView::qmlAttachedProperties(QObject *obj) +{ + return new QQuickPathViewAttached(obj); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/items/qquickpathview_p.h b/src/declarative/items/qquickpathview_p.h new file mode 100644 index 0000000000..657d721ae6 --- /dev/null +++ b/src/declarative/items/qquickpathview_p.h @@ -0,0 +1,257 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHVIEW_P_H +#define QQUICKPATHVIEW_P_H + +#include "qquickitem.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeChangeSet; + +class QQuickPathViewPrivate; +class QQuickPathViewAttached; +class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentIndexChanged) + Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged) + + Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) + Q_PROPERTY(QQuickItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) + + Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged) + Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged) + Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) + Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) + + Q_PROPERTY(qreal dragMargin READ dragMargin WRITE setDragMargin NOTIFY dragMarginChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) + Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) + + Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) + Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) + + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount NOTIFY pathItemCountChanged) + + Q_ENUMS(HighlightRangeMode) + +public: + QQuickPathView(QQuickItem *parent=0); + virtual ~QQuickPathView(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativePath *path() const; + void setPath(QDeclarativePath *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + QQuickItem *currentItem() const; + + qreal offset() const; + void setOffset(qreal offset); + + QDeclarativeComponent *highlight() const; + void setHighlight(QDeclarativeComponent *highlight); + QQuickItem *highlightItem(); + + enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + HighlightRangeMode highlightRangeMode() const; + void setHighlightRangeMode(HighlightRangeMode mode); + + qreal preferredHighlightBegin() const; + void setPreferredHighlightBegin(qreal); + + qreal preferredHighlightEnd() const; + void setPreferredHighlightEnd(qreal); + + int highlightMoveDuration() const; + void setHighlightMoveDuration(int); + + qreal dragMargin() const; + void setDragMargin(qreal margin); + + qreal flickDeceleration() const; + void setFlickDeceleration(qreal dec); + + bool isInteractive() const; + void setInteractive(bool); + + bool isMoving() const; + bool isFlicking() const; + + int count() const; + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int pathItemCount() const; + void setPathItemCount(int); + + static QQuickPathViewAttached *qmlAttachedProperties(QObject *); + +public Q_SLOTS: + void incrementCurrentIndex(); + void decrementCurrentIndex(); + +Q_SIGNALS: + void currentIndexChanged(); + void offsetChanged(); + void modelChanged(); + void countChanged(); + void pathChanged(); + void preferredHighlightBeginChanged(); + void preferredHighlightEndChanged(); + void highlightRangeModeChanged(); + void dragMarginChanged(); + void snapPositionChanged(); + void delegateChanged(); + void pathItemCountChanged(); + void flickDecelerationChanged(); + void interactiveChanged(); + void movingChanged(); + void flickingChanged(); + void highlightChanged(); + void highlightItemChanged(); + void highlightMoveDurationChanged(); + void movementStarted(); + void movementEnded(); + void flickStarted(); + void flickEnded(); + +protected: + virtual void updatePolish(); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *); + bool sendMouseEvent(QMouseEvent *event); + bool childMouseEventFilter(QQuickItem *, QEvent *); + void mouseUngrabEvent(); + void componentComplete(); + +private Q_SLOTS: + void refill(); + void ticked(); + void movementEnding(); + void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); + void createdItem(int index, QQuickItem *item); + void destroyingItem(QQuickItem *item); + void pathUpdated(); + +private: + friend class QQuickPathViewAttached; + Q_DISABLE_COPY(QQuickPathView) + Q_DECLARE_PRIVATE(QQuickPathView) +}; + +class QDeclarativeOpenMetaObject; +class QQuickPathViewAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QQuickPathView *view READ view CONSTANT) + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) + Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged) + +public: + QQuickPathViewAttached(QObject *parent); + ~QQuickPathViewAttached(); + + QQuickPathView *view() { return m_view; } + + bool isCurrentItem() const { return m_isCurrent; } + void setIsCurrentItem(bool c) { + if (m_isCurrent != c) { + m_isCurrent = c; + emit currentItemChanged(); + } + } + + QVariant value(const QByteArray &name) const; + void setValue(const QByteArray &name, const QVariant &val); + + bool isOnPath() const { return m_onPath; } + void setOnPath(bool on) { + if (on != m_onPath) { + m_onPath = on; + emit pathChanged(); + } + } + qreal m_percent; + +Q_SIGNALS: + void currentItemChanged(); + void pathChanged(); + +private: + friend class QQuickPathViewPrivate; + friend class QQuickPathView; + QQuickPathView *m_view; + QDeclarativeOpenMetaObject *m_metaobject; + bool m_onPath : 1; + bool m_isCurrent : 1; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPathView) +QML_DECLARE_TYPEINFO(QQuickPathView, QML_HAS_ATTACHED_PROPERTIES) +QT_END_HEADER + +#endif // QQUICKPATHVIEW_P_H diff --git a/src/declarative/items/qquickpathview_p_p.h b/src/declarative/items/qquickpathview_p_p.h new file mode 100644 index 0000000000..87d9313645 --- /dev/null +++ b/src/declarative/items/qquickpathview_p_p.h @@ -0,0 +1,194 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPATHVIEW_P_P_H +#define QQUICKPATHVIEW_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpathview_p.h" +#include "qquickitem_p.h" +#include "qquickvisualdatamodel_p.h" + +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeOpenMetaObjectType; +class QQuickPathViewAttached; +class QQuickPathViewPrivate : public QQuickItemPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickPathView) + +public: + QQuickPathViewPrivate() + : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0), lastDist(0) + , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0) + , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true) + , autoHighlight(true), highlightUp(false), layoutScheduled(false) + , moving(false), flicking(false) + , dragMargin(0), deceleration(100) + , moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset) + , firstIndex(-1), pathItems(-1), requestedIndex(-1) + , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0) + , moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition) + , highlightPosition(0) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeMode(QQuickPathView::StrictlyEnforceRange) + , highlightMoveDuration(300), modelCount(0) + { + } + + void init(); + + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + if ((newGeometry.size() != oldGeometry.size()) + && (!highlightItem || item != highlightItem)) { + if (QQuickPathViewAttached *att = attached(item)) + att->m_percent = -1; + scheduleLayout(); + } + } + + void scheduleLayout() { + Q_Q(QQuickPathView); + if (!layoutScheduled) { + layoutScheduled = true; + q->polish(); + } + } + + QQuickItem *getItem(int modelIndex, bool onPath = true); + void releaseItem(QQuickItem *item); + QQuickPathViewAttached *attached(QQuickItem *item); + void clear(); + void updateMappedRange(); + qreal positionOfIndex(qreal index) const; + void createHighlight(); + void updateHighlight(); + void setHighlightPosition(qreal pos); + bool isValid() const { + return model && model->count() > 0 && model->isValid() && path; + } + + void handleMousePressEvent(QMouseEvent *event); + void handleMouseMoveEvent(QMouseEvent *event); + void handleMouseReleaseEvent(QMouseEvent *); + + int calcCurrentIndex(); + void updateCurrent(); + static void fixOffsetCallback(void*); + void fixOffset(); + void setOffset(qreal offset); + void setAdjustedOffset(qreal offset); + void regenerate(); + void updateItem(QQuickItem *, qreal); + void snapToCurrent(); + QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const; + + QDeclarativePath *path; + int currentIndex; + QDeclarativeGuard currentItem; + qreal currentItemOffset; + qreal startPc; + QPointF startPoint; + qreal lastDist; + int lastElapsed; + qreal offset; + qreal offsetAdj; + qreal mappedRange; + bool stealMouse : 1; + bool ownModel : 1; + bool interactive : 1; + bool haveHighlightRange : 1; + bool autoHighlight : 1; + bool highlightUp : 1; + bool layoutScheduled : 1; + bool moving : 1; + bool flicking : 1; + QElapsedTimer lastPosTime; + QPointF lastPos; + qreal dragMargin; + qreal deceleration; + QDeclarativeTimeLine tl; + QDeclarativeTimeLineValueProxy moveOffset; + int firstIndex; + int pathItems; + int requestedIndex; + QList items; + QList itemCache; + QDeclarativeGuard model; + QVariant modelVariant; + enum MovementReason { Other, SetIndex, Mouse }; + MovementReason moveReason; + enum MovementDirection { Shortest, Negative, Positive }; + MovementDirection moveDirection; + QDeclarativeOpenMetaObjectType *attType; + QDeclarativeComponent *highlightComponent; + QQuickItem *highlightItem; + QDeclarativeTimeLineValueProxy moveHighlight; + qreal highlightPosition; + qreal highlightRangeStart; + qreal highlightRangeEnd; + QQuickPathView::HighlightRangeMode highlightRangeMode; + int highlightMoveDuration; + int modelCount; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/items/qquickpincharea.cpp b/src/declarative/items/qquickpincharea.cpp new file mode 100644 index 0000000000..c1f60ae640 --- /dev/null +++ b/src/declarative/items/qquickpincharea.cpp @@ -0,0 +1,600 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtSG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpincharea_p_p.h" +#include "qquickcanvas.h" + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PinchEvent QQuickPinchEvent + \inqmlmodule QtQuick 2 + \ingroup qml-event-elements + \brief The PinchEvent object provides information about a pinch event. + + \bold {The PinchEvent element was added in QtQuick 1.1} + + The \c center, \c startCenter, \c previousCenter properties provide the center position between the two touch points. + + The \c scale and \c previousScale properties provide the scale factor. + + The \c angle, \c previousAngle and \c rotation properties provide the angle between the two points and the amount of rotation. + + The \c point1, \c point2, \c startPoint1, \c startPoint2 properties provide the positions of the touch points. + + The \c accepted property may be set to false in the \c onPinchStarted handler if the gesture should not + be handled. + + \sa PinchArea +*/ + +/*! + \qmlproperty QPointF QtQuick2::PinchEvent::center + \qmlproperty QPointF QtQuick2::PinchEvent::startCenter + \qmlproperty QPointF QtQuick2::PinchEvent::previousCenter + + These properties hold the position of the center point between the two touch points. + + \list + \o \c center is the current center point + \o \c previousCenter is the center point of the previous event. + \o \c startCenter is the center point when the gesture began + \endlist +*/ + +/*! + \qmlproperty real QtQuick2::PinchEvent::scale + \qmlproperty real QtQuick2::PinchEvent::previousScale + + These properties hold the scale factor determined by the change in distance between the two touch points. + + \list + \o \c scale is the current scale factor. + \o \c previousScale is the scale factor of the previous event. + \endlist + + When a pinch gesture is started, the scale is 1.0. +*/ + +/*! + \qmlproperty real QtQuick2::PinchEvent::angle + \qmlproperty real QtQuick2::PinchEvent::previousAngle + \qmlproperty real QtQuick2::PinchEvent::rotation + + These properties hold the angle between the two touch points. + + \list + \o \c angle is the current angle between the two points in the range -180 to 180. + \o \c previousAngle is the angle of the previous event. + \o \c rotation is the total rotation since the pinch gesture started. + \endlist + + When a pinch gesture is started, the rotation is 0.0. +*/ + +/*! + \qmlproperty QPointF QtQuick2::PinchEvent::point1 + \qmlproperty QPointF QtQuick2::PinchEvent::startPoint1 + \qmlproperty QPointF QtQuick2::PinchEvent::point2 + \qmlproperty QPointF QtQuick2::PinchEvent::startPoint2 + + These properties provide the actual touch points generating the pinch. + + \list + \o \c point1 and \c point2 hold the current positions of the points. + \o \c startPoint1 and \c startPoint2 hold the positions of the points when the second point was touched. + \endlist +*/ + +/*! + \qmlproperty bool QtQuick2::PinchEvent::accepted + + Setting this property to false in the \c PinchArea::onPinchStarted handler + will result in no further pinch events being generated, and the gesture + ignored. +*/ + +/*! + \qmlproperty int QtQuick2::PinchEvent::pointCount + + Holds the number of points currently touched. The PinchArea will not react + until two touch points have initited a gesture, but will remain active until + all touch points have been released. +*/ + +QQuickPinch::QQuickPinch() + : m_target(0), m_minScale(1.0), m_maxScale(1.0) + , m_minRotation(0.0), m_maxRotation(0.0) + , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX) + , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false) +{ +} + +QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate() +{ + delete pinch; +} + +/*! + \qmlclass PinchArea QQuickPinchArea + \inqmlmodule QtQuick 2 + \brief The PinchArea item enables simple pinch gesture handling. + \inherits Item + + \bold {The PinchArea element was added in QtQuick 1.1} + + A PinchArea is an invisible item that is typically used in conjunction with + a visible item in order to provide pinch gesture handling for that item. + + The \l enabled property is used to enable and disable pinch handling for + the proxied item. When disabled, the pinch area becomes transparent to + mouse/touch events. + + PinchArea can be used in two ways: + + \list + \o setting a \c pinch.target to provide automatic interaction with an element + \o using the onPinchStarted, onPinchUpdated and onPinchFinished handlers + \endlist + + \sa PinchEvent +*/ + +/*! + \qmlsignal QtQuick2::PinchArea::onPinchStarted() + + This handler is called when the pinch area detects that a pinch gesture has started. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. + + To ignore this gesture set the \c pinch.accepted property to false. The gesture + will be cancelled and no further events will be sent. +*/ + +/*! + \qmlsignal QtQuick2::PinchArea::onPinchUpdated() + + This handler is called when the pinch area detects that a pinch gesture has changed. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. +*/ + +/*! + \qmlsignal QtQuick2::PinchArea::onPinchFinished() + + This handler is called when the pinch area detects that a pinch gesture has finished. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. +*/ + + +/*! + \qmlproperty Item QtQuick2::PinchArea::pinch.target + \qmlproperty bool QtQuick2::PinchArea::pinch.active + \qmlproperty real QtQuick2::PinchArea::pinch.minimumScale + \qmlproperty real QtQuick2::PinchArea::pinch.maximumScale + \qmlproperty real QtQuick2::PinchArea::pinch.minimumRotation + \qmlproperty real QtQuick2::PinchArea::pinch.maximumRotation + \qmlproperty enumeration QtQuick2::PinchArea::pinch.dragAxis + \qmlproperty real QtQuick2::PinchArea::pinch.minimumX + \qmlproperty real QtQuick2::PinchArea::pinch.maximumX + \qmlproperty real QtQuick2::PinchArea::pinch.minimumY + \qmlproperty real QtQuick2::PinchArea::pinch.maximumY + + \c pinch provides a convenient way to make an item react to pinch gestures. + + \list + \i \c pinch.target specifies the id of the item to drag. + \i \c pinch.active specifies if the target item is currently being dragged. + \i \c pinch.minimumScale and \c pinch.maximumScale limit the range of the Item::scale property. + \i \c pinch.minimumRotation and \c pinch.maximumRotation limit the range of the Item::rotation property. + \i \c pinch.dragAxis specifies whether dragging in not allowed (\c Pinch.NoDrag), can be done horizontally (\c Pinch.XAxis), vertically (\c Pinch.YAxis), or both (\c Pinch.XandYAxis) + \i \c pinch.minimum and \c pinch.maximum limit how far the target can be dragged along the corresponding axes. + \endlist +*/ + +QQuickPinchArea::QQuickPinchArea(QQuickItem *parent) + : QQuickItem(*(new QQuickPinchAreaPrivate), parent) +{ + Q_D(QQuickPinchArea); + d->init(); +} + +QQuickPinchArea::~QQuickPinchArea() +{ +} +/*! + \qmlproperty bool QtQuick2::PinchArea::enabled + This property holds whether the item accepts pinch gestures. + + This property defaults to true. +*/ +bool QQuickPinchArea::isEnabled() const +{ + Q_D(const QQuickPinchArea); + return d->absorb; +} + +void QQuickPinchArea::setEnabled(bool a) +{ + Q_D(QQuickPinchArea); + if (a != d->absorb) { + d->absorb = a; + emit enabledChanged(); + } +} + +void QQuickPinchArea::touchEvent(QTouchEvent *event) +{ + Q_D(QQuickPinchArea); + if (!d->absorb || !isVisible()) { + QQuickItem::event(event); + return; + } + + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + d->touchPoints.clear(); + for (int i = 0; i < event->touchPoints().count(); ++i) { + if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) { + d->touchPoints << event->touchPoints().at(i); + } + } + updatePinch(); + break; + case QEvent::TouchEnd: + d->touchPoints.clear(); + updatePinch(); + break; + default: + QQuickItem::event(event); + } +} + +void QQuickPinchArea::updatePinch() +{ + Q_D(QQuickPinchArea); + if (d->touchPoints.count() == 0) { + if (d->inPinch) { + d->inPinch = false; + QPointF pinchCenter = mapFromScene(d->sceneLastCenter); + QQuickPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(pinchCenter); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(mapFromScene(d->lastPoint1)); + pe.setPoint2(mapFromScene(d->lastPoint2)); + emit pinchFinished(&pe); + d->pinchStartDist = 0; + d->pinchActivated = false; + if (d->pinch && d->pinch->target()) + d->pinch->setActive(false); + } + d->initPinch = false; + d->pinchRejected = false; + d->stealMouse = false; + setKeepMouseGrab(false); + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + return; + } + QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0); + QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0); + if (d->touchPoints.count() == 2 + && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) { + d->id1 = touchPoint1.id(); + d->sceneStartPoint1 = touchPoint1.scenePos(); + d->sceneStartPoint2 = touchPoint2.scenePos(); + d->pinchActivated = true; + d->initPinch = true; + } + if (d->pinchActivated && !d->pinchRejected){ + const int dragThreshold = qApp->styleHints()->startDragDistance(); + QPointF p1 = touchPoint1.scenePos(); + QPointF p2 = touchPoint2.scenePos(); + qreal dx = p1.x() - p2.x(); + qreal dy = p1.y() - p2.y(); + qreal dist = sqrt(dx*dx + dy*dy); + QPointF sceneCenter = (p1 + p2)/2; + qreal angle = QLineF(p1, p2).angle(); + if (d->touchPoints.count() == 1) { + // If we only have one point then just move the center + if (d->id1 == touchPoint1.id()) + sceneCenter = d->sceneLastCenter + touchPoint1.scenePos() - d->lastPoint1; + else + sceneCenter = d->sceneLastCenter + touchPoint2.scenePos() - d->lastPoint2; + angle = d->pinchLastAngle; + } + d->id1 = touchPoint1.id(); + if (angle > 180) + angle -= 360; + if (!d->inPinch || d->initPinch) { + if (d->touchPoints.count() >= 2 + && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold + || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold + || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold + || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) { + d->initPinch = false; + d->sceneStartCenter = sceneCenter; + d->sceneLastCenter = sceneCenter; + d->pinchStartCenter = mapFromScene(sceneCenter); + d->pinchStartDist = dist; + d->pinchStartAngle = angle; + d->pinchLastScale = 1.0; + d->pinchLastAngle = angle; + d->pinchRotation = 0.0; + d->lastPoint1 = p1; + d->lastPoint2 = p2; + QQuickPinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(d->pinchStartCenter); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(mapFromScene(d->lastPoint1)); + pe.setPoint2(mapFromScene(d->lastPoint2)); + pe.setPointCount(d->touchPoints.count()); + emit pinchStarted(&pe); + if (pe.accepted()) { + d->inPinch = true; + d->stealMouse = true; + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() != this) + grabMouse(); + setKeepMouseGrab(true); + if (d->pinch && d->pinch->target()) { + d->pinchStartPos = pinch()->target()->pos(); + d->pinchStartScale = d->pinch->target()->scale(); + d->pinchStartRotation = d->pinch->target()->rotation(); + d->pinch->setActive(true); + } + } else { + d->pinchRejected = true; + } + } + } else if (d->pinchStartDist > 0) { + qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale; + qreal da = d->pinchLastAngle - angle; + if (da > 180) + da -= 360; + else if (da < -180) + da += 360; + d->pinchRotation += da; + QPointF pinchCenter = mapFromScene(sceneCenter); + QQuickPinchEvent pe(pinchCenter, scale, angle, d->pinchRotation); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(mapFromScene(d->sceneLastCenter)); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(touchPoint1.pos()); + pe.setPoint2(touchPoint2.pos()); + pe.setPointCount(d->touchPoints.count()); + d->pinchLastScale = scale; + d->sceneLastCenter = sceneCenter; + d->pinchLastAngle = angle; + d->lastPoint1 = touchPoint1.scenePos(); + d->lastPoint2 = touchPoint2.scenePos(); + emit pinchUpdated(&pe); + if (d->pinch && d->pinch->target()) { + qreal s = d->pinchStartScale * scale; + s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale()); + pinch()->target()->setScale(s); + QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos; + if (pinch()->axis() & QQuickPinch::XAxis) { + qreal x = pos.x(); + if (x < pinch()->xmin()) + x = pinch()->xmin(); + else if (x > pinch()->xmax()) + x = pinch()->xmax(); + pinch()->target()->setX(x); + } + if (pinch()->axis() & QQuickPinch::YAxis) { + qreal y = pos.y(); + if (y < pinch()->ymin()) + y = pinch()->ymin(); + else if (y > pinch()->ymax()) + y = pinch()->ymax(); + pinch()->target()->setY(y); + } + if (d->pinchStartRotation >= pinch()->minimumRotation() + && d->pinchStartRotation <= pinch()->maximumRotation()) { + qreal r = d->pinchRotation + d->pinchStartRotation; + r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation()); + pinch()->target()->setRotation(r); + } + } + } + } +} + +void QQuickPinchArea::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickPinchArea); + d->stealMouse = false; + if (!d->absorb) + QQuickItem::mousePressEvent(event); + else { + setKeepMouseGrab(false); + event->setAccepted(true); + } +} + +void QQuickPinchArea::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickPinchArea); + if (!d->absorb) { + QQuickItem::mouseMoveEvent(event); + return; + } +} + +void QQuickPinchArea::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickPinchArea); + d->stealMouse = false; + if (!d->absorb) { + QQuickItem::mouseReleaseEvent(event); + } else { + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } +} + +void QQuickPinchArea::mouseUngrabEvent() +{ + setKeepMouseGrab(false); +} + +bool QQuickPinchArea::sendMouseEvent(QMouseEvent *event) +{ + Q_D(QQuickPinchArea); + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { + QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + mouseEvent.setAccepted(false); + + switch (mouseEvent.type()) { + case QEvent::MouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::MouseButtonPress: + mousePressEvent(&mouseEvent); + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = c->mouseGrabberItem(); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return stealThisEvent; + } + if (event->type() == QEvent::MouseButtonRelease) { + d->stealMouse = false; + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } + return false; +} + +bool QQuickPinchArea::childMouseEventFilter(QQuickItem *i, QEvent *e) +{ + Q_D(QQuickPinchArea); + if (!d->absorb || !isVisible()) + return QQuickItem::childMouseEventFilter(i, e); + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast(e)); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: { + QTouchEvent *touch = static_cast(e); + d->touchPoints.clear(); + for (int i = 0; i < touch->touchPoints().count(); ++i) + if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) + d->touchPoints << touch->touchPoints().at(i); + updatePinch(); + } + return d->inPinch; + case QEvent::TouchEnd: + d->touchPoints.clear(); + updatePinch(); + break; + default: + break; + } + + return QQuickItem::childMouseEventFilter(i, e); +} + +void QQuickPinchArea::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + QQuickItem::geometryChanged(newGeometry, oldGeometry); +} + +void QQuickPinchArea::itemChange(ItemChange change, const ItemChangeData &value) +{ + QQuickItem::itemChange(change, value); +} + +QQuickPinch *QQuickPinchArea::pinch() +{ + Q_D(QQuickPinchArea); + if (!d->pinch) + d->pinch = new QQuickPinch; + return d->pinch; +} + + +QT_END_NAMESPACE + diff --git a/src/declarative/items/qquickpincharea_p.h b/src/declarative/items/qquickpincharea_p.h new file mode 100644 index 0000000000..c98a1fdd9e --- /dev/null +++ b/src/declarative/items/qquickpincharea_p.h @@ -0,0 +1,315 @@ +// Commit: f707672eb4c51ea82fbd98e1da16ece61a74c690 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtSG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPINCHAREA_H +#define QQUICKPINCHAREA_H + +#include "qquickitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QQuickPinch : public QObject +{ + Q_OBJECT + + Q_ENUMS(Axis) + Q_PROPERTY(QQuickItem *target READ target WRITE setTarget RESET resetTarget) + Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged) + Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged) + Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) + Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) + Q_PROPERTY(Axis dragAxis READ axis WRITE setAxis NOTIFY dragAxisChanged) + Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) + Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged) + Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged) + Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + +public: + QQuickPinch(); + + QQuickItem *target() const { return m_target; } + void setTarget(QQuickItem *target) { + if (target == m_target) + return; + m_target = target; + emit targetChanged(); + } + void resetTarget() { + if (!m_target) + return; + m_target = 0; + emit targetChanged(); + } + + qreal minimumScale() const { return m_minScale; } + void setMinimumScale(qreal s) { + if (s == m_minScale) + return; + m_minScale = s; + emit minimumScaleChanged(); + } + qreal maximumScale() const { return m_maxScale; } + void setMaximumScale(qreal s) { + if (s == m_maxScale) + return; + m_maxScale = s; + emit maximumScaleChanged(); + } + + qreal minimumRotation() const { return m_minRotation; } + void setMinimumRotation(qreal r) { + if (r == m_minRotation) + return; + m_minRotation = r; + emit minimumRotationChanged(); + } + qreal maximumRotation() const { return m_maxRotation; } + void setMaximumRotation(qreal r) { + if (r == m_maxRotation) + return; + m_maxRotation = r; + emit maximumRotationChanged(); + } + + enum Axis { NoDrag=0x00, XAxis=0x01, YAxis=0x02, XandYAxis=0x03 }; + Axis axis() const { return m_axis; } + void setAxis(Axis a) { + if (a == m_axis) + return; + m_axis = a; + emit dragAxisChanged(); + } + + qreal xmin() const { return m_xmin; } + void setXmin(qreal x) { + if (x == m_xmin) + return; + m_xmin = x; + emit minimumXChanged(); + } + qreal xmax() const { return m_xmax; } + void setXmax(qreal x) { + if (x == m_xmax) + return; + m_xmax = x; + emit maximumXChanged(); + } + qreal ymin() const { return m_ymin; } + void setYmin(qreal y) { + if (y == m_ymin) + return; + m_ymin = y; + emit minimumYChanged(); + } + qreal ymax() const { return m_ymax; } + void setYmax(qreal y) { + if (y == m_ymax) + return; + m_ymax = y; + emit maximumYChanged(); + } + + bool active() const { return m_active; } + void setActive(bool a) { + if (a == m_active) + return; + m_active = a; + emit activeChanged(); + } + +signals: + void targetChanged(); + void minimumScaleChanged(); + void maximumScaleChanged(); + void minimumRotationChanged(); + void maximumRotationChanged(); + void dragAxisChanged(); + void minimumXChanged(); + void maximumXChanged(); + void minimumYChanged(); + void maximumYChanged(); + void activeChanged(); + +private: + QQuickItem *m_target; + qreal m_minScale; + qreal m_maxScale; + qreal m_minRotation; + qreal m_maxRotation; + Axis m_axis; + qreal m_xmin; + qreal m_xmax; + qreal m_ymin; + qreal m_ymax; + bool m_active; +}; + +class Q_AUTOTEST_EXPORT QQuickPinchEvent : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPointF center READ center) + Q_PROPERTY(QPointF startCenter READ startCenter) + Q_PROPERTY(QPointF previousCenter READ previousCenter) + Q_PROPERTY(qreal scale READ scale) + Q_PROPERTY(qreal previousScale READ previousScale) + Q_PROPERTY(qreal angle READ angle) + Q_PROPERTY(qreal previousAngle READ previousAngle) + Q_PROPERTY(qreal rotation READ rotation) + Q_PROPERTY(QPointF point1 READ point1) + Q_PROPERTY(QPointF startPoint1 READ startPoint1) + Q_PROPERTY(QPointF point2 READ point2) + Q_PROPERTY(QPointF startPoint2 READ startPoint2) + Q_PROPERTY(int pointCount READ pointCount) + Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) + +public: + QQuickPinchEvent(QPointF c, qreal s, qreal a, qreal r) + : QObject(), m_center(c), m_scale(s), m_angle(a), m_rotation(r) + , m_pointCount(0), m_accepted(true) {} + + QPointF center() const { return m_center; } + QPointF startCenter() const { return m_startCenter; } + void setStartCenter(QPointF c) { m_startCenter = c; } + QPointF previousCenter() const { return m_lastCenter; } + void setPreviousCenter(QPointF c) { m_lastCenter = c; } + qreal scale() const { return m_scale; } + qreal previousScale() const { return m_lastScale; } + void setPreviousScale(qreal s) { m_lastScale = s; } + qreal angle() const { return m_angle; } + qreal previousAngle() const { return m_lastAngle; } + void setPreviousAngle(qreal a) { m_lastAngle = a; } + qreal rotation() const { return m_rotation; } + QPointF point1() const { return m_point1; } + void setPoint1(QPointF p) { m_point1 = p; } + QPointF startPoint1() const { return m_startPoint1; } + void setStartPoint1(QPointF p) { m_startPoint1 = p; } + QPointF point2() const { return m_point2; } + void setPoint2(QPointF p) { m_point2 = p; } + QPointF startPoint2() const { return m_startPoint2; } + void setStartPoint2(QPointF p) { m_startPoint2 = p; } + int pointCount() const { return m_pointCount; } + void setPointCount(int count) { m_pointCount = count; } + + bool accepted() const { return m_accepted; } + void setAccepted(bool a) { m_accepted = a; } + +private: + QPointF m_center; + QPointF m_startCenter; + QPointF m_lastCenter; + qreal m_scale; + qreal m_lastScale; + qreal m_angle; + qreal m_lastAngle; + qreal m_rotation; + QPointF m_point1; + QPointF m_point2; + QPointF m_startPoint1; + QPointF m_startPoint2; + int m_pointCount; + bool m_accepted; +}; + + +class QQuickMouseEvent; +class QQuickPinchAreaPrivate; +class Q_AUTOTEST_EXPORT QQuickPinchArea : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QQuickPinch *pinch READ pinch CONSTANT) + +public: + QQuickPinchArea(QQuickItem *parent=0); + ~QQuickPinchArea(); + + bool isEnabled() const; + void setEnabled(bool); + + QQuickPinch *pinch(); + +Q_SIGNALS: + void enabledChanged(); + void pinchStarted(QQuickPinchEvent *pinch); + void pinchUpdated(QQuickPinchEvent *pinch); + void pinchFinished(QQuickPinchEvent *pinch); + +protected: + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseUngrabEvent(); + virtual bool sendMouseEvent(QMouseEvent *event); + virtual bool childMouseEventFilter(QQuickItem *i, QEvent *e); + virtual void touchEvent(QTouchEvent *event); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual void itemChange(ItemChange change, const ItemChangeData& value); + +private: + void updatePinch(); + void handlePress(); + void handleRelease(); + +private: + Q_DISABLE_COPY(QQuickPinchArea) + Q_DECLARE_PRIVATE(QQuickPinchArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPinch) +QML_DECLARE_TYPE(QQuickPinchEvent) +QML_DECLARE_TYPE(QQuickPinchArea) + +QT_END_HEADER + +#endif // QQUICKPINCHAREA_H + diff --git a/src/declarative/items/qquickpincharea_p_p.h b/src/declarative/items/qquickpincharea_p_p.h new file mode 100644 index 0000000000..1c9d5c5ba4 --- /dev/null +++ b/src/declarative/items/qquickpincharea_p_p.h @@ -0,0 +1,116 @@ +// Commit: f707672eb4c51ea82fbd98e1da16ece61a74c690 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtSG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPINCHAREA_P_H +#define QQUICKPINCHAREA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include "qquickitem_p.h" +#include "qquickpincharea_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickPinch; +class QQuickPinchAreaPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickPinchArea) +public: + QQuickPinchAreaPrivate() + : absorb(true), stealMouse(false), inPinch(false) + , pinchRejected(false), pinchActivated(false), initPinch(false) + , pinch(0), pinchStartDist(0), pinchStartScale(1.0) + , pinchLastScale(1.0), pinchStartRotation(0.0), pinchStartAngle(0.0) + , pinchLastAngle(0.0), pinchRotation(0.0) + { + } + + ~QQuickPinchAreaPrivate(); + + void init() + { + Q_Q(QQuickPinchArea); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildMouseEvents(true); + } + + bool absorb : 1; + bool stealMouse : 1; + bool inPinch : 1; + bool pinchRejected : 1; + bool pinchActivated : 1; + bool initPinch : 1; + QQuickPinch *pinch; + QPointF sceneStartPoint1; + QPointF sceneStartPoint2; + QPointF lastPoint1; + QPointF lastPoint2; + qreal pinchStartDist; + qreal pinchStartScale; + qreal pinchLastScale; + qreal pinchStartRotation; + qreal pinchStartAngle; + qreal pinchLastAngle; + qreal pinchRotation; + QPointF sceneStartCenter; + QPointF pinchStartCenter; + QPointF sceneLastCenter; + QPointF pinchStartPos; + QList touchPoints; + int id1; +}; + +QT_END_NAMESPACE + +#endif // QQUICKPINCHAREA_P_H + diff --git a/src/declarative/items/qquickpositioners.cpp b/src/declarative/items/qquickpositioners.cpp new file mode 100644 index 0000000000..15cd35c2f0 --- /dev/null +++ b/src/declarative/items/qquickpositioners.cpp @@ -0,0 +1,1533 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpositioners_p.h" +#include "qquickpositioners_p_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const QQuickItemPrivate::ChangeTypes watchedChanges + = QQuickItemPrivate::Geometry + | QQuickItemPrivate::SiblingOrder + | QQuickItemPrivate::Visibility + | QQuickItemPrivate::Destroyed; + +void QQuickBasePositionerPrivate::watchChanges(QQuickItem *other) +{ + QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other); + otherPrivate->addItemChangeListener(this, watchedChanges); +} + +void QQuickBasePositionerPrivate::unwatchChanges(QQuickItem* other) +{ + QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other); + otherPrivate->removeItemChangeListener(this, watchedChanges); +} + +QQuickBasePositioner::QQuickBasePositioner(PositionerType at, QQuickItem *parent) + : QQuickImplicitSizeItem(*(new QQuickBasePositionerPrivate), parent) +{ + Q_D(QQuickBasePositioner); + d->init(at); +} +/*! + \internal + \class QQuickBasePositioner + \brief The QQuickBasePositioner class provides a base for QQuickGraphics layouts. + + To create a QQuickGraphics Positioner, simply subclass QQuickBasePositioner and implement + doLayout(), which is automatically called when the layout might need + updating. In doLayout() use the setX and setY functions from QQuickBasePositioner, and the + base class will apply the positions along with the appropriate transitions. The items to + position are provided in order as the protected member positionedItems. + + You also need to set a PositionerType, to declare whether you are positioning the x, y or both + for the child items. Depending on the chosen type, only x or y changes will be applied. + + Note that the subclass is responsible for adding the spacing in between items. + + Positioning is usually delayed until before a frame is rendered, to batch multiple repositioning + changes into one calculation. +*/ + +QQuickBasePositioner::QQuickBasePositioner(QQuickBasePositionerPrivate &dd, PositionerType at, QQuickItem *parent) + : QQuickImplicitSizeItem(dd, parent) +{ + Q_D(QQuickBasePositioner); + d->init(at); +} + +QQuickBasePositioner::~QQuickBasePositioner() +{ + Q_D(QQuickBasePositioner); + for (int i = 0; i < positionedItems.count(); ++i) + d->unwatchChanges(positionedItems.at(i).item); + positionedItems.clear(); +} + +void QQuickBasePositioner::updatePolish() +{ + Q_D(QQuickBasePositioner); + if (d->positioningDirty) + prePositioning(); +} + +int QQuickBasePositioner::spacing() const +{ + Q_D(const QQuickBasePositioner); + return d->spacing; +} + +void QQuickBasePositioner::setSpacing(int s) +{ + Q_D(QQuickBasePositioner); + if (s==d->spacing) + return; + d->spacing = s; + d->setPositioningDirty(); + emit spacingChanged(); +} + +QDeclarativeTransition *QQuickBasePositioner::move() const +{ + Q_D(const QQuickBasePositioner); + return d->moveTransition; +} + +void QQuickBasePositioner::setMove(QDeclarativeTransition *mt) +{ + Q_D(QQuickBasePositioner); + if (mt == d->moveTransition) + return; + d->moveTransition = mt; + emit moveChanged(); +} + +QDeclarativeTransition *QQuickBasePositioner::add() const +{ + Q_D(const QQuickBasePositioner); + return d->addTransition; +} + +void QQuickBasePositioner::setAdd(QDeclarativeTransition *add) +{ + Q_D(QQuickBasePositioner); + if (add == d->addTransition) + return; + + d->addTransition = add; + emit addChanged(); +} + +void QQuickBasePositioner::componentComplete() +{ + QQuickItem::componentComplete(); + positionedItems.reserve(childItems().count()); + prePositioning(); + reportConflictingAnchors(); +} + +void QQuickBasePositioner::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickBasePositioner); + if (change == ItemChildAddedChange){ + d->setPositioningDirty(); + } else if (change == ItemChildRemovedChange) { + QQuickItem *child = value.item; + QQuickBasePositioner::PositionedItem posItem(child); + int idx = positionedItems.find(posItem); + if (idx >= 0) { + d->unwatchChanges(child); + positionedItems.remove(idx); + } + d->setPositioningDirty(); + } + + QQuickItem::itemChange(change, value); +} + +void QQuickBasePositioner::prePositioning() +{ + Q_D(QQuickBasePositioner); + if (!isComponentComplete()) + return; + + if (d->doingPositioning) + return; + + d->positioningDirty = false; + d->doingPositioning = true; + //Need to order children by creation order modified by stacking order + QList children = childItems(); + + QPODVector oldItems; + positionedItems.copyAndClear(oldItems); + for (int ii = 0; ii < children.count(); ++ii) { + QQuickItem *child = children.at(ii); + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); + PositionedItem *item = 0; + PositionedItem posItem(child); + int wIdx = oldItems.find(posItem); + if (wIdx < 0) { + d->watchChanges(child); + positionedItems.append(posItem); + item = &positionedItems[positionedItems.count()-1]; + item->isNew = true; + if (!childPrivate->explicitVisible || !child->width() || !child->height()) + item->isVisible = false; + } else { + item = &oldItems[wIdx]; + // Items are only omitted from positioning if they are explicitly hidden + // i.e. their positioning is not affected if an ancestor is hidden. + if (!childPrivate->explicitVisible || !child->width() || !child->height()) { + item->isVisible = false; + } else if (!item->isVisible) { + item->isVisible = true; + item->isNew = true; + } else { + item->isNew = false; + } + positionedItems.append(*item); + } + } + QSizeF contentSize(0,0); + doPositioning(&contentSize); + updateAttachedProperties(); + if (!d->addActions.isEmpty() || !d->moveActions.isEmpty()) + finishApplyTransitions(); + d->doingPositioning = false; + //Set implicit size to the size of its children + setImplicitHeight(contentSize.height()); + setImplicitWidth(contentSize.width()); +} + +void QQuickBasePositioner::positionX(int x, const PositionedItem &target) +{ + Q_D(QQuickBasePositioner); + if (d->type == Horizontal || d->type == Both) { + if (target.isNew) { + if (!d->addTransition || !d->addTransition->enabled()) + target.item->setX(x); + else + d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); + } else if (x != target.item->x()) { + if (!d->moveTransition || !d->moveTransition->enabled()) + target.item->setX(x); + else + d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); + } + } +} + +void QQuickBasePositioner::positionY(int y, const PositionedItem &target) +{ + Q_D(QQuickBasePositioner); + if (d->type == Vertical || d->type == Both) { + if (target.isNew) { + if (!d->addTransition || !d->addTransition->enabled()) + target.item->setY(y); + else + d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); + } else if (y != target.item->y()) { + if (!d->moveTransition || !d->moveTransition->enabled()) + target.item->setY(y); + else + d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); + } + } +} + +void QQuickBasePositioner::finishApplyTransitions() +{ + Q_D(QQuickBasePositioner); + // Note that if a transition is not set the transition manager will + // apply the changes directly, in the case add/move aren't set + d->addTransitionManager.transition(d->addActions, d->addTransition); + d->moveTransitionManager.transition(d->moveActions, d->moveTransition); + d->addActions.clear(); + d->moveActions.clear(); +} + +QQuickPositionerAttached *QQuickBasePositioner::qmlAttachedProperties(QObject *obj) +{ + return new QQuickPositionerAttached(obj); +} + +void QQuickBasePositioner::updateAttachedProperties(QQuickPositionerAttached *specificProperty, QQuickItem *specificPropertyOwner) const +{ + // If this function is deemed too expensive or shows up in profiles, it could + // be changed to run only when there are attached properties present. This + // could be a flag in the positioner that is set by the attached property + // constructor. + QQuickPositionerAttached *prevLastProperty = 0; + QQuickPositionerAttached *lastProperty = 0; + + int visibleItemIndex = 0; + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item) + continue; + + QQuickPositionerAttached *property = 0; + + if (specificProperty) { + if (specificPropertyOwner == child.item) { + property = specificProperty; + } + } else { + property = static_cast(qmlAttachedPropertiesObject(child.item, false)); + } + + if (child.isVisible) { + if (property) { + property->setIndex(visibleItemIndex); + property->setIsFirstItem(visibleItemIndex == 0); + + if (property->isLastItem()) + prevLastProperty = property; + } + + lastProperty = property; + ++visibleItemIndex; + } else if (property) { + property->setIndex(-1); + property->setIsFirstItem(false); + property->setIsLastItem(false); + } + } + + if (prevLastProperty && prevLastProperty != lastProperty) + prevLastProperty->setIsLastItem(false); + if (lastProperty) + lastProperty->setIsLastItem(true); +} + +/*! + \qmlclass Positioner QQuickPositionerAttached + \inqmlmodule QtQuick 2 + \ingroup qml-positioning-elements + \brief The Positioner type provides attached properties that contain details on where an item exists in a positioner. + + Positioner items (such as Column, Row, Flow and Grid) provide automatic layout + for child items. Attaching this property allows a child item to determine + where it exists within the positioner. +*/ + +QQuickPositionerAttached::QQuickPositionerAttached(QObject *parent) : QObject(parent), m_index(-1), m_isFirstItem(false), m_isLastItem(false) +{ + QQuickItem *attachedItem = qobject_cast(parent); + if (attachedItem) { + QQuickBasePositioner *positioner = qobject_cast(attachedItem->parent()); + if (positioner) { + positioner->updateAttachedProperties(this, attachedItem); + } + } +} + +/*! + \qmlattachedproperty Item QtQuick2::Positioner::index + + This property allows the item to determine + its index within the positioner. +*/ +void QQuickPositionerAttached::setIndex(int index) +{ + if (m_index == index) + return; + m_index = index; + emit indexChanged(); +} + +/*! + \qmlattachedproperty Item QtQuick2::Positioner::isFirstItem + \qmlattachedproperty Item QtQuick2::Positioner::isLastItem + + These properties allow the item to determine if it + is the first or last item in the positioner, respectively. +*/ +void QQuickPositionerAttached::setIsFirstItem(bool isFirstItem) +{ + if (m_isFirstItem == isFirstItem) + return; + m_isFirstItem = isFirstItem; + emit isFirstItemChanged(); +} + +void QQuickPositionerAttached::setIsLastItem(bool isLastItem) +{ + if (m_isLastItem == isLastItem) + return; + m_isLastItem = isLastItem; + emit isLastItemChanged(); +} + +/*! + \qmlclass Column QQuickColumn + \inqmlmodule QtQuick 2 + \ingroup qml-positioning-elements + \brief The Column item arranges its children vertically. + \inherits Item + + The Column item positions its child items so that they are vertically + aligned and not overlapping. + + Spacing between items can be added using the \l spacing property. + Transitions can be used for cases where items managed by a Column are + added or moved. These are stored in the \l add and \l move properties + respectively. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example positions differently shaped rectangles using a Column + item. + + \image verticalpositioner_example.png + + \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Column item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Column item. + + The use of transitions with positioners is described in more detail in the + \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML + Positioner and Repeater Items} document. + + \image verticalpositioner_transition.gif + + \qml + Column { + spacing: 2 + add: Transition { + // Define an animation for adding a new item... + } + move: Transition { + // Define an animation for moving items within the column... + } + // ... + } + \endqml + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + height of a child depend on the position of a child, then the + positioner may exhibit strange behavior. If you need to perform any of these + actions, consider positioning the items without the use of a Column. + + Items with a width or height of 0 will not be positioned. + + Positioning is batched and syncronized with painting to reduce the number of + calculations needed. This means that positioners may not reposition items immediately + when changes occur, but it will have moved by the next frame. + + \sa Row, Grid, Flow, Positioner, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition QtQuick2::Column::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition QtQuick2::Column::move + + This property holds the transition to apply when moving an item + within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \image positioner-move.gif + + \qml + Column { + move: Transition { + NumberAnimation { + properties: "y" + duration: 1000 + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int QtQuick2::Column::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + \sa Grid::spacing +*/ +QQuickColumn::QQuickColumn(QQuickItem *parent) +: QQuickBasePositioner(Vertical, parent) +{ +} + +void QQuickColumn::doPositioning(QSizeF *contentSize) +{ + int voffset = 0; + + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || !child.isVisible) + continue; + + if (child.item->y() != voffset) + positionY(voffset, child); + + contentSize->setWidth(qMax(contentSize->width(), child.item->width())); + + voffset += child.item->height(); + voffset += spacing(); + } + + if (voffset != 0)//If we positioned any items, undo the spacing from the last item + voffset -= spacing(); + contentSize->setHeight(voffset); +} + +void QQuickColumn::reportConflictingAnchors() +{ + QQuickBasePositionerPrivate *d = static_cast(QQuickBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors) { + QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QQuickAnchors::TopAnchor || + usedAnchors & QQuickAnchors::BottomAnchor || + usedAnchors & QQuickAnchors::VCenterAnchor || + anchors->fill() || anchors->centerIn()) { + d->anchorConflict = true; + break; + } + } + } + } + if (d->anchorConflict) { + qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"; + } +} +/*! + \qmlclass Row QQuickRow + \inqmlmodule QtQuick 2 + \ingroup qml-positioning-elements + \brief The Row item arranges its children horizontally. + \inherits Item + + The Row item positions its child items so that they are horizontally + aligned and not overlapping. + + Use \l spacing to set the spacing between items in a Row, and use the + \l add and \l move properties to set the transitions that should be applied + when items are added to, removed from, or re-positioned within the Row. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example lays out differently shaped rectangles using a Row. + + \image horizontalpositioner_example.png + + \snippet doc/src/snippets/declarative/row/row.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Grid item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Row item. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Row. + + Items with a width or height of 0 will not be positioned. + + Positioning is batched and syncronized with painting to reduce the number of + calculations needed. This means that positioners may not reposition items immediately + when changes occur, but it will have moved by the next frame. + + \sa Column, Grid, Flow, Positioner, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition QtQuick2::Row::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition QtQuick2::Row::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Row { + id: positioner + move: Transition { + NumberAnimation { + properties: "x" + duration: 1000 + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int QtQuick2::Row::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + \sa Grid::spacing +*/ + +QQuickRow::QQuickRow(QQuickItem *parent) +: QQuickBasePositioner(Horizontal, parent) +{ +} +/*! + \qmlproperty enumeration QtQuick2::Row::layoutDirection + + This property holds the layoutDirection of the row. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set, + the left anchor remains to the left of the row. + \o Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set, + the right anchor remains to the right of the row. + \endlist + + \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} +*/ + +Qt::LayoutDirection QQuickRow::layoutDirection() const +{ + return QQuickBasePositionerPrivate::getLayoutDirection(this); +} + +void QQuickRow::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + QQuickBasePositionerPrivate *d = static_cast(QQuickBasePositionerPrivate::get(this)); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + // For RTL layout the positioning changes when the width changes. + if (d->layoutDirection == Qt::RightToLeft) + d->addItemChangeListener(d, QQuickItemPrivate::Geometry); + else + d->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + prePositioning(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} +/*! + \qmlproperty enumeration QtQuick2::Row::effectiveLayoutDirection + This property holds the effective layout direction of the row positioner. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the row positioner will be mirrored. However, the + property \l {Row::layoutDirection}{layoutDirection} will remain unchanged. + + \sa Row::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QQuickRow::effectiveLayoutDirection() const +{ + return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QQuickRow::doPositioning(QSizeF *contentSize) +{ + QQuickBasePositionerPrivate *d = static_cast(QQuickBasePositionerPrivate::get(this)); + int hoffset = 0; + + QList hoffsets; + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || !child.isVisible) + continue; + + if (d->isLeftToRight()) { + if (child.item->x() != hoffset) + positionX(hoffset, child); + } else { + hoffsets << hoffset; + } + + contentSize->setHeight(qMax(contentSize->height(), child.item->height())); + + hoffset += child.item->width(); + hoffset += spacing(); + } + + if (hoffset != 0)//If we positioned any items, undo the extra spacing from the last item + hoffset -= spacing(); + contentSize->setWidth(hoffset); + + if (d->isLeftToRight()) + return; + + //Right to Left layout + int end = 0; + if (!widthValid()) + end = contentSize->width(); + else + end = width(); + + int acc = 0; + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || !child.isVisible) + continue; + hoffset = end - hoffsets[acc++] - child.item->width(); + if (child.item->x() != hoffset) + positionX(hoffset, child); + } +} + +void QQuickRow::reportConflictingAnchors() +{ + QQuickBasePositionerPrivate *d = static_cast(QQuickBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors) { + QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QQuickAnchors::LeftAnchor || + usedAnchors & QQuickAnchors::RightAnchor || + usedAnchors & QQuickAnchors::HCenterAnchor || + anchors->fill() || anchors->centerIn()) { + d->anchorConflict = true; + break; + } + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"; +} + +/*! + \qmlclass Grid QQuickGrid + \inqmlmodule QtQuick 2 + \ingroup qml-positioning-elements + \brief The Grid item positions its children in a grid. + \inherits Item + + The Grid item positions its child items so that they are + aligned in a grid and are not overlapping. + + The grid positioner calculates a grid of rectangular cells of sufficient + size to hold all items, placing the items in the cells, from left to right + and top to bottom. Each item is positioned in the top-left corner of its + cell with position (0, 0). + + A Grid defaults to four columns, and as many rows as are necessary to + fit all child items. The number of rows and columns can be constrained + by setting the \l rows and \l columns properties. + + Spacing can be added between child items by setting the \l spacing + property. The amount of spacing applied will be the same in the + horizontal and vertical directions. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example demonstrates this. + + \image gridLayout_example.png + + \snippet doc/src/snippets/declarative/grid/grid.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Grid item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Grid item. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Grid. + + Items with a width or height of 0 will not be positioned. + + Positioning is batched and syncronized with painting to reduce the number of + calculations needed. This means that positioners may not reposition items immediately + when changes occur, but it will have moved by the next frame. + + \sa Flow, Row, Column, Positioner, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition QtQuick2::Grid::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition QtQuick2::Grid::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Grid { + move: Transition { + NumberAnimation { + properties: "x,y" + duration: 1000 + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int QtQuick2::Grid::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + The below example places a Grid containing a red, a blue and a + green rectangle on a gray background. The area the grid positioner + occupies is colored white. The positioner on the left has the + no spacing (the default), and the positioner on the right has + a spacing of 6. + + \inlineimage qml-grid-no-spacing.png + \inlineimage qml-grid-spacing.png + + \sa rows, columns +*/ +QQuickGrid::QQuickGrid(QQuickItem *parent) : + QQuickBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_rowSpacing(-1), m_columnSpacing(-1), m_flow(LeftToRight) +{ +} + +/*! + \qmlproperty int QtQuick2::Grid::columns + + This property holds the number of columns in the grid. The default + number of columns is 4. + + If the grid does not have enough items to fill the specified + number of columns, some columns will be of zero width. +*/ + +/*! + \qmlproperty int QtQuick2::Grid::rows + This property holds the number of rows in the grid. + + If the grid does not have enough items to fill the specified + number of rows, some rows will be of zero width. +*/ + +void QQuickGrid::setColumns(const int columns) +{ + if (columns == m_columns) + return; + m_columns = columns; + prePositioning(); + emit columnsChanged(); +} + +void QQuickGrid::setRows(const int rows) +{ + if (rows == m_rows) + return; + m_rows = rows; + prePositioning(); + emit rowsChanged(); +} + +/*! + \qmlproperty enumeration QtQuick2::Grid::flow + This property holds the flow of the layout. + + Possible values are: + + \list + \o Grid.LeftToRight (default) - Items are positioned next to + each other in the \l layoutDirection, then wrapped to the next line. + \o Grid.TopToBottom - Items are positioned next to each + other from top to bottom, then wrapped to the next column. + \endlist +*/ +QQuickGrid::Flow QQuickGrid::flow() const +{ + return m_flow; +} + +void QQuickGrid::setFlow(Flow flow) +{ + if (m_flow != flow) { + m_flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +/*! + \qmlproperty int QtQuick2::Grid::rowSpacing + + This property holds the spacing in pixels between rows. + + \sa columnSpacing + \since QtQuick2.0 +*/ +void QQuickGrid::setRowSpacing(const int rowSpacing) +{ + if (rowSpacing == m_rowSpacing) + return; + m_rowSpacing = rowSpacing; + prePositioning(); + emit rowSpacingChanged(); +} + +/*! + \qmlproperty int QtQuick2::Grid::columnSpacing + + This property holds the spacing in pixels between columns. + + \sa rowSpacing + \since QtQuick2.0 +*/ +void QQuickGrid::setColumnSpacing(const int columnSpacing) +{ + if (columnSpacing == m_columnSpacing) + return; + m_columnSpacing = columnSpacing; + prePositioning(); + emit columnSpacingChanged(); +} + +/*! + \qmlproperty enumeration QtQuick2::Grid::layoutDirection + + This property holds the layout direction of the layout. + + Possible values are: + + \list + \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, + and left to right. The flow direction is dependent on the + \l Grid::flow property. + \o Qt.RightToLeft - Items are positioned from the top to bottom, + and right to left. The flow direction is dependent on the + \l Grid::flow property. + \endlist + + \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} +*/ +Qt::LayoutDirection QQuickGrid::layoutDirection() const +{ + return QQuickBasePositionerPrivate::getLayoutDirection(this); +} + +void QQuickGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + QQuickBasePositionerPrivate *d = static_cast(QQuickBasePositionerPrivate::get(this)); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + // For RTL layout the positioning changes when the width changes. + if (d->layoutDirection == Qt::RightToLeft) + d->addItemChangeListener(d, QQuickItemPrivate::Geometry); + else + d->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + prePositioning(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick2::Grid::effectiveLayoutDirection + This property holds the effective layout direction of the grid positioner. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the grid positioner will be mirrored. However, the + property \l {Grid::layoutDirection}{layoutDirection} will remain unchanged. + + \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ +Qt::LayoutDirection QQuickGrid::effectiveLayoutDirection() const +{ + return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QQuickGrid::doPositioning(QSizeF *contentSize) +{ + QQuickBasePositionerPrivate *d = static_cast(QQuickBasePositionerPrivate::get(this)); + int c = m_columns; + int r = m_rows; + //Is allocating the extra QPODVector too much overhead? + QPODVector visibleItems;//we aren't concerned with invisible items + visibleItems.reserve(positionedItems.count()); + for (int i=0; i maxColWidth; + QList maxRowHeight; + int childIndex =0; + if (m_flow == LeftToRight) { + for (int i=0; i < r; i++){ + for (int j=0; j < c; j++){ + if (j==0) + maxRowHeight << 0; + if (i==0) + maxColWidth << 0; + + if (childIndex == visibleItems.count()) + break; + + const PositionedItem &child = visibleItems.at(childIndex++); + if (child.item->width() > maxColWidth[j]) + maxColWidth[j] = child.item->width(); + if (child.item->height() > maxRowHeight[i]) + maxRowHeight[i] = child.item->height(); + } + } + } else { + for (int j=0; j < c; j++){ + for (int i=0; i < r; i++){ + if (j==0) + maxRowHeight << 0; + if (i==0) + maxColWidth << 0; + + if (childIndex == visibleItems.count()) + break; + + const PositionedItem &child = visibleItems.at(childIndex++); + if (child.item->width() > maxColWidth[j]) + maxColWidth[j] = child.item->width(); + if (child.item->height() > maxRowHeight[i]) + maxRowHeight[i] = child.item->height(); + } + } + } + + int columnSpacing = m_columnSpacing; + if (columnSpacing == -1) + columnSpacing = spacing(); + + int rowSpacing = m_rowSpacing; + if (rowSpacing == -1) + rowSpacing = spacing(); + + int widthSum = 0; + for (int j=0; j < maxColWidth.size(); j++){ + if (j) + widthSum += columnSpacing; + widthSum += maxColWidth[j]; + } + + int heightSum = 0; + for (int i=0; i < maxRowHeight.size(); i++){ + if (i) + heightSum += rowSpacing; + heightSum += maxRowHeight[i]; + } + + contentSize->setHeight(heightSum); + contentSize->setWidth(widthSum); + + int end = 0; + if (widthValid()) + end = width(); + else + end = widthSum; + + int xoffset=0; + if (!d->isLeftToRight()) + xoffset = end; + int yoffset=0; + int curRow =0; + int curCol =0; + for (int i = 0; i < visibleItems.count(); ++i) { + const PositionedItem &child = visibleItems.at(i); + int childXOffset = xoffset; + if (!d->isLeftToRight()) + childXOffset -= child.item->width(); + if ((child.item->x() != childXOffset) || (child.item->y() != yoffset)){ + positionX(childXOffset, child); + positionY(yoffset, child); + } + + if (m_flow == LeftToRight) { + if (d->isLeftToRight()) + xoffset += maxColWidth[curCol]+columnSpacing; + else + xoffset -= maxColWidth[curCol]+columnSpacing; + curCol++; + curCol%=c; + if (!curCol){ + yoffset += maxRowHeight[curRow]+rowSpacing; + if (d->isLeftToRight()) + xoffset = 0; + else + xoffset = end; + curRow++; + if (curRow>=r) + break; + } + } else { + yoffset+=maxRowHeight[curRow]+rowSpacing; + curRow++; + curRow%=r; + if (!curRow){ + if (d->isLeftToRight()) + xoffset += maxColWidth[curCol]+columnSpacing; + else + xoffset -= maxColWidth[curCol]+columnSpacing; + yoffset=0; + curCol++; + if (curCol>=c) + break; + } + } + } +} + +void QQuickGrid::reportConflictingAnchors() +{ + QQuickBasePositionerPrivate *d = static_cast(QQuickBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { + d->anchorConflict = true; + break; + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify anchors for items inside Grid"; +} + +/*! + \qmlclass Flow QQuickFlow + \inqmlmodule QtQuick 2 + \ingroup qml-positioning-elements + \brief The Flow item arranges its children side by side, wrapping as necessary. + \inherits Item + + The Flow item positions its child items like words on a page, wrapping them + to create rows or columns of items that do not overlap. + + Spacing between items can be added using the \l spacing property. + Transitions can be used for cases where items managed by a Column are + added or moved. These are stored in the \l add and \l move properties + respectively. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example positions \l Text items within a parent item using + a Flow item. + + \image qml-flow-snippet.png + + \snippet doc/src/snippets/declarative/flow.qml flow item + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Flow item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Flow item. + + The use of transitions with positioners is described in more detail in the + \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML + Positioner and Repeater Items} document. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Flow. + + Items with a width or height of 0 will not be positioned. + + Positioning is batched and syncronized with painting to reduce the number of + calculations needed. This means that positioners may not reposition items immediately + when changes occur, but it will have moved by the next frame. + + \sa Column, Row, Grid, Positioner, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition QtQuick2::Flow::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition QtQuick2::Flow::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Flow { + id: positioner + move: Transition { + NumberAnimation { + properties: "x,y" + ease: "easeOutBounce" + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int QtQuick2::Flow::spacing + + spacing is the amount in pixels left empty between each adjacent + item, and defaults to 0. + + \sa Grid::spacing +*/ + +class QQuickFlowPrivate : public QQuickBasePositionerPrivate +{ + Q_DECLARE_PUBLIC(QQuickFlow) + +public: + QQuickFlowPrivate() + : QQuickBasePositionerPrivate(), flow(QQuickFlow::LeftToRight) + {} + + QQuickFlow::Flow flow; +}; + +QQuickFlow::QQuickFlow(QQuickItem *parent) +: QQuickBasePositioner(*(new QQuickFlowPrivate), Both, parent) +{ + Q_D(QQuickFlow); + // Flow layout requires relayout if its own size changes too. + d->addItemChangeListener(d, QQuickItemPrivate::Geometry); +} + +/*! + \qmlproperty enumeration QtQuick2::Flow::flow + This property holds the flow of the layout. + + Possible values are: + + \list + \o Flow.LeftToRight (default) - Items are positioned next to + to each other according to the \l layoutDirection until the width of the Flow + is exceeded, then wrapped to the next line. + \o Flow.TopToBottom - Items are positioned next to each + other from top to bottom until the height of the Flow is exceeded, + then wrapped to the next column. + \endlist +*/ +QQuickFlow::Flow QQuickFlow::flow() const +{ + Q_D(const QQuickFlow); + return d->flow; +} + +void QQuickFlow::setFlow(Flow flow) +{ + Q_D(QQuickFlow); + if (d->flow != flow) { + d->flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick2::Flow::layoutDirection + + This property holds the layout direction of the layout. + + Possible values are: + + \list + \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, + and left to right. The flow direction is dependent on the + \l Flow::flow property. + \o Qt.RightToLeft - Items are positioned from the top to bottom, + and right to left. The flow direction is dependent on the + \l Flow::flow property. + \endlist + + \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} +*/ + +Qt::LayoutDirection QQuickFlow::layoutDirection() const +{ + Q_D(const QQuickFlow); + return d->layoutDirection; +} + +void QQuickFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QQuickFlow); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + prePositioning(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick2::Flow::effectiveLayoutDirection + This property holds the effective layout direction of the flow positioner. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the grid positioner will be mirrored. However, the + property \l {Flow::layoutDirection}{layoutDirection} will remain unchanged. + + \sa Flow::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QQuickFlow::effectiveLayoutDirection() const +{ + return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QQuickFlow::doPositioning(QSizeF *contentSize) +{ + Q_D(QQuickFlow); + + int hoffset = 0; + int voffset = 0; + int linemax = 0; + QList hoffsets; + + for (int i = 0; i < positionedItems.count(); ++i) { + const PositionedItem &child = positionedItems.at(i); + if (!child.item || !child.isVisible) + continue; + + if (d->flow == LeftToRight) { + if (widthValid() && hoffset && hoffset + child.item->width() > width()) { + hoffset = 0; + voffset += linemax + spacing(); + linemax = 0; + } + } else { + if (heightValid() && voffset && voffset + child.item->height() > height()) { + voffset = 0; + hoffset += linemax + spacing(); + linemax = 0; + } + } + + if (d->isLeftToRight()) { + if (child.item->x() != hoffset) + positionX(hoffset, child); + } else { + hoffsets << hoffset; + } + if (child.item->y() != voffset) + positionY(voffset, child); + + contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width())); + contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height())); + + if (d->flow == LeftToRight) { + hoffset += child.item->width(); + hoffset += spacing(); + linemax = qMax(linemax, qCeil(child.item->height())); + } else { + voffset += child.item->height(); + voffset += spacing(); + linemax = qMax(linemax, qCeil(child.item->width())); + } + } + if (d->isLeftToRight()) + return; + + int end; + if (widthValid()) + end = width(); + else + end = contentSize->width(); + int acc = 0; + for (int i = 0; i < positionedItems.count(); ++i) { + const PositionedItem &child = positionedItems.at(i); + if (!child.item || !child.isVisible) + continue; + hoffset = end - hoffsets[acc++] - child.item->width(); + if (child.item->x() != hoffset) + positionX(hoffset, child); + } +} + +void QQuickFlow::reportConflictingAnchors() +{ + Q_D(QQuickFlow); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast(child.item))->_anchors; + if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { + d->anchorConflict = true; + break; + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify anchors for items inside Flow"; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickpositioners_p.h b/src/declarative/items/qquickpositioners_p.h new file mode 100644 index 0000000000..b8e8879205 --- /dev/null +++ b/src/declarative/items/qquickpositioners_p.h @@ -0,0 +1,295 @@ +// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPOSITIONERS_P_H +#define QQUICKPOSITIONERS_P_H + +#include "qquickimplicitsizeitem_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickBasePositionerPrivate; + +class QQuickPositionerAttached : public QObject +{ + Q_OBJECT + +public: + QQuickPositionerAttached(QObject *parent); + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + Q_PROPERTY(bool isFirstItem READ isFirstItem NOTIFY isFirstItemChanged) + Q_PROPERTY(bool isLastItem READ isLastItem NOTIFY isLastItemChanged) + + int index() const { return m_index; } + void setIndex(int index); + + bool isFirstItem() const { return m_isFirstItem; } + void setIsFirstItem(bool isFirstItem); + + bool isLastItem() const { return m_isLastItem; } + void setIsLastItem(bool isLastItem); + +Q_SIGNALS: + void indexChanged(); + void isFirstItemChanged(); + void isLastItemChanged(); + +private: + int m_index; + bool m_isFirstItem; + bool m_isLastItem; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QQuickBasePositioner : public QQuickImplicitSizeItem +{ + Q_OBJECT + + Q_PROPERTY(int spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) + Q_PROPERTY(QDeclarativeTransition *move READ move WRITE setMove NOTIFY moveChanged) + Q_PROPERTY(QDeclarativeTransition *add READ add WRITE setAdd NOTIFY addChanged) +public: + enum PositionerType { None = 0x0, Horizontal = 0x1, Vertical = 0x2, Both = 0x3 }; + QQuickBasePositioner(PositionerType, QQuickItem *parent); + ~QQuickBasePositioner(); + + int spacing() const; + void setSpacing(int); + + QDeclarativeTransition *move() const; + void setMove(QDeclarativeTransition *); + + QDeclarativeTransition *add() const; + void setAdd(QDeclarativeTransition *); + + static QQuickPositionerAttached *qmlAttachedProperties(QObject *obj); + + void updateAttachedProperties(QQuickPositionerAttached *specificProperty = 0, QQuickItem *specificPropertyOwner = 0) const; + +protected: + QQuickBasePositioner(QQuickBasePositionerPrivate &dd, PositionerType at, QQuickItem *parent); + virtual void componentComplete(); + virtual void itemChange(ItemChange, const ItemChangeData &); + void finishApplyTransitions(); + + virtual void updatePolish(); + +Q_SIGNALS: + void spacingChanged(); + void moveChanged(); + void addChanged(); + +protected Q_SLOTS: + void prePositioning(); + +protected: + virtual void doPositioning(QSizeF *contentSize)=0; + virtual void reportConflictingAnchors()=0; + class PositionedItem { + public : + PositionedItem(QQuickItem *i) : item(i), isNew(false), isVisible(true) {} + bool operator==(const PositionedItem &other) const { return other.item == item; } + QQuickItem *item; + bool isNew; + bool isVisible; + }; + + QPODVector positionedItems; + void positionX(int,const PositionedItem &target); + void positionY(int,const PositionedItem &target); + +private: + Q_DISABLE_COPY(QQuickBasePositioner) + Q_DECLARE_PRIVATE(QQuickBasePositioner) +}; + +class Q_AUTOTEST_EXPORT QQuickColumn : public QQuickBasePositioner +{ + Q_OBJECT +public: + QQuickColumn(QQuickItem *parent=0); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +private: + Q_DISABLE_COPY(QQuickColumn) +}; + +class Q_AUTOTEST_EXPORT QQuickRow: public QQuickBasePositioner +{ + Q_OBJECT + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) +public: + QQuickRow(QQuickItem *parent=0); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection (Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + +Q_SIGNALS: + void layoutDirectionChanged(); + void effectiveLayoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +private: + Q_DISABLE_COPY(QQuickRow) +}; + +class Q_AUTOTEST_EXPORT QQuickGrid : public QQuickBasePositioner +{ + Q_OBJECT + Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) + Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged) + Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged) + Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged) + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) + +public: + QQuickGrid(QQuickItem *parent=0); + + int rows() const {return m_rows;} + void setRows(const int rows); + + int columns() const {return m_columns;} + void setColumns(const int columns); + + int rowSpacing() const { return m_rowSpacing; } + void setRowSpacing(int); + + int columnSpacing() const { return m_columnSpacing; } + void setColumnSpacing(int); + + Q_ENUMS(Flow) + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection (Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + +Q_SIGNALS: + void rowsChanged(); + void columnsChanged(); + void flowChanged(); + void layoutDirectionChanged(); + void effectiveLayoutDirectionChanged(); + void rowSpacingChanged(); + void columnSpacingChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); + +private: + int m_rows; + int m_columns; + int m_rowSpacing; + int m_columnSpacing; + Flow m_flow; + Q_DISABLE_COPY(QQuickGrid) +}; + +class QQuickFlowPrivate; +class Q_AUTOTEST_EXPORT QQuickFlow: public QQuickBasePositioner +{ + Q_OBJECT + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) +public: + QQuickFlow(QQuickItem *parent=0); + + Q_ENUMS(Flow) + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection (Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + +Q_SIGNALS: + void flowChanged(); + void layoutDirectionChanged(); + void effectiveLayoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +protected: + QQuickFlow(QQuickFlowPrivate &dd, QQuickItem *parent); +private: + Q_DISABLE_COPY(QQuickFlow) + Q_DECLARE_PRIVATE(QQuickFlow) +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickColumn) +QML_DECLARE_TYPE(QQuickRow) +QML_DECLARE_TYPE(QQuickGrid) +QML_DECLARE_TYPE(QQuickFlow) + +QML_DECLARE_TYPE(QQuickBasePositioner) +QML_DECLARE_TYPEINFO(QQuickBasePositioner, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QQUICKPOSITIONERS_P_H diff --git a/src/declarative/items/qquickpositioners_p_p.h b/src/declarative/items/qquickpositioners_p_p.h new file mode 100644 index 0000000000..86c6c74ba6 --- /dev/null +++ b/src/declarative/items/qquickpositioners_p_p.h @@ -0,0 +1,164 @@ +// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPOSITIONERS_P_P_H +#define QQUICKPOSITIONERS_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickpositioners_p.h" +#include "qquickimplicitsizeitem_p_p.h" + +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickBasePositionerPrivate : public QQuickImplicitSizeItemPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickBasePositioner) + +public: + QQuickBasePositionerPrivate() + : spacing(0), type(QQuickBasePositioner::None) + , moveTransition(0), addTransition(0), positioningDirty(false) + , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) + { + } + + void init(QQuickBasePositioner::PositionerType at) + { + type = at; + childrenDoNotOverlap = true; + } + + int spacing; + + QQuickBasePositioner::PositionerType type; + QDeclarativeTransition *moveTransition; + QDeclarativeTransition *addTransition; + QDeclarativeStateOperation::ActionList addActions; + QDeclarativeStateOperation::ActionList moveActions; + QDeclarativeTransitionManager addTransitionManager; + QDeclarativeTransitionManager moveTransitionManager; + + void watchChanges(QQuickItem *other); + void unwatchChanges(QQuickItem* other); + void setPositioningDirty() { + Q_Q(QQuickBasePositioner); + if (!positioningDirty) { + positioningDirty = true; + q->polish(); + } + } + + bool positioningDirty : 1; + bool doingPositioning : 1; + bool anchorConflict : 1; + + Qt::LayoutDirection layoutDirection; + + void mirrorChange() { + if (type != QQuickBasePositioner::Vertical) + setPositioningDirty(); + } + bool isLeftToRight() const { + if (type == QQuickBasePositioner::Vertical) + return true; + else + return effectiveLayoutMirror ? layoutDirection == Qt::RightToLeft : layoutDirection == Qt::LeftToRight; + } + + virtual void itemSiblingOrderChanged(QQuickItem* other) + { + Q_UNUSED(other); + setPositioningDirty(); + } + + void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) + { + if (newGeometry.size() != oldGeometry.size()) + setPositioningDirty(); + } + + virtual void itemVisibilityChanged(QQuickItem *) + { + setPositioningDirty(); + } + + void itemDestroyed(QQuickItem *item) + { + Q_Q(QQuickBasePositioner); + q->positionedItems.removeOne(QQuickBasePositioner::PositionedItem(item)); + } + + static Qt::LayoutDirection getLayoutDirection(const QQuickBasePositioner *positioner) + { + return positioner->d_func()->layoutDirection; + } + + static Qt::LayoutDirection getEffectiveLayoutDirection(const QQuickBasePositioner *positioner) + { + if (positioner->d_func()->effectiveLayoutMirror) + return positioner->d_func()->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return positioner->d_func()->layoutDirection; + } +}; + +QT_END_NAMESPACE + +#endif // QQUICKPOSITIONERS_P_P_H diff --git a/src/declarative/items/qquickrectangle.cpp b/src/declarative/items/qquickrectangle.cpp new file mode 100644 index 0000000000..a7f592eac3 --- /dev/null +++ b/src/declarative/items/qquickrectangle.cpp @@ -0,0 +1,555 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickrectangle_p.h" +#include "qquickrectangle_p_p.h" + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// XXX todo - should we change rectangle to draw entirely within its width/height? +/*! + \internal + \class QQuickPen + \brief The QQuickPen class provides a pen used for drawing rectangle borders on a QQuickView. + + By default, the pen is invalid and nothing is drawn. You must either set a color (then the default + width is 1) or a width (then the default color is black). + + A width of 1 indicates is a single-pixel line on the border of the item being painted. + + Example: + \qml + Rectangle { + border.width: 2 + border.color: "red" + } + \endqml +*/ + +QQuickPen::QQuickPen(QObject *parent) + : QObject(parent) + , m_width(1) + , m_color("#000000") + , m_aligned(true) + , m_valid(false) +{ +} + +qreal QQuickPen::width() const +{ + return m_width; +} + +void QQuickPen::setWidth(qreal w) +{ + if (m_width == w && m_valid) + return; + + m_width = w; + m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); + emit penChanged(); +} + +QColor QQuickPen::color() const +{ + return m_color; +} + +void QQuickPen::setColor(const QColor &c) +{ + m_color = c; + m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); + emit penChanged(); +} + +bool QQuickPen::aligned() const +{ + return m_aligned; +} + +void QQuickPen::setAligned(bool aligned) +{ + if (aligned == m_aligned) + return; + m_aligned = aligned; + m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); + emit penChanged(); +} + +bool QQuickPen::isValid() const +{ + return m_valid; +} + +/*! + \qmlclass GradientStop QQuickGradientStop + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The GradientStop item defines the color at a position in a Gradient. + + \sa Gradient +*/ + +/*! + \qmlproperty real QtQuick2::GradientStop::position + \qmlproperty color QtQuick2::GradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default position is 0.0; the default color is black. + + \sa Gradient +*/ +QQuickGradientStop::QQuickGradientStop(QObject *parent) + : QObject(parent) +{ +} + +qreal QQuickGradientStop::position() const +{ + return m_position; +} + +void QQuickGradientStop::setPosition(qreal position) +{ + m_position = position; updateGradient(); +} + +QColor QQuickGradientStop::color() const +{ + return m_color; +} + +void QQuickGradientStop::setColor(const QColor &color) +{ + m_color = color; updateGradient(); +} + +void QQuickGradientStop::updateGradient() +{ + if (QQuickGradient *grad = qobject_cast(parent())) + grad->doUpdate(); +} + +/*! + \qmlclass Gradient QQuickGradient + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The Gradient item defines a gradient fill. + + A gradient is defined by two or more colors, which will be blended seamlessly. + + The colors are specified as a set of GradientStop child items, each of + which defines a position on the gradient from 0.0 to 1.0 and a color. + The position of each GradientStop is defined by setting its + \l{GradientStop::}{position} property; its color is defined using its + \l{GradientStop::}{color} property. + + A gradient without any gradient stops is rendered as a solid white fill. + + Note that this item is not a visual representation of a gradient. To display a + gradient, use a visual element (like \l Rectangle) which supports the use + of gradients. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage qml-gradient.png + \enddiv + + The following example declares a \l Rectangle item with a gradient starting + with red, blending to yellow at one third of the height of the rectangle, + and ending with green: + + \snippet doc/src/snippets/declarative/gradient.qml code + + \clearfloat + \section1 Performance and Limitations + + Calculating gradients can be computationally expensive compared to the use + of solid color fills or images. Consider using gradients for static items + in a user interface. + + In Qt 4.7, only vertical, linear gradients can be applied to items. If you + need to apply different orientations of gradients, a combination of rotation + and clipping will need to be applied to the relevant items. This can + introduce additional performance requirements for your application. + + The use of animations involving gradient stops may not give the desired + result. An alternative way to animate gradients is to use pre-generated + images or SVG drawings containing gradients. + + \sa GradientStop +*/ + +/*! + \qmlproperty list QtQuick2::Gradient::stops + \default + + This property holds the gradient stops describing the gradient. + + By default, this property contains an empty list. + + To set the gradient stops, define them as children of the Gradient element. +*/ +QQuickGradient::QQuickGradient(QObject *parent) +: QObject(parent), m_gradient(0) +{ +} + +QQuickGradient::~QQuickGradient() +{ + delete m_gradient; +} + +QDeclarativeListProperty QQuickGradient::stops() +{ + return QDeclarativeListProperty(this, m_stops); +} + +const QGradient *QQuickGradient::gradient() const +{ + if (!m_gradient && !m_stops.isEmpty()) { + m_gradient = new QLinearGradient(0,0,0,1.0); + for (int i = 0; i < m_stops.count(); ++i) { + const QQuickGradientStop *stop = m_stops.at(i); + m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode); + m_gradient->setColorAt(stop->position(), stop->color()); + } + } + + return m_gradient; +} + +void QQuickGradient::doUpdate() +{ + delete m_gradient; + m_gradient = 0; + emit updated(); +} + +int QQuickRectanglePrivate::doUpdateSlotIdx = -1; + +/*! + \qmlclass Rectangle QQuickRectangle + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The Rectangle item provides a filled rectangle with an optional border. + \inherits Item + + Rectangle items are used to fill areas with solid color or gradients, and are + often used to hold other items. + + \section1 Appearance + + Each Rectangle item is painted using either a solid fill color, specified using + the \l color property, or a gradient, defined using a Gradient element and set + using the \l gradient property. If both a color and a gradient are specified, + the gradient is used. + + You can add an optional border to a rectangle with its own color and thickness + by settting the \l border.color and \l border.width properties. + + You can also create rounded rectangles using the \l radius property. Since this + introduces curved edges to the corners of a rectangle, it may be appropriate to + set the \l smooth property to improve its appearance. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage declarative-rect.png + \enddiv + + The following example shows the effects of some of the common properties on a + Rectangle item, which in this case is used to create a square: + + \snippet doc/src/snippets/declarative/rectangle/rectangle.qml document + + \clearfloat + \section1 Performance + + Using the \l smooth property improves the appearance of a rounded rectangle at + the cost of rendering performance. You should consider unsetting this property + for rectangles in motion, and only set it when they are stationary. + + \sa Image +*/ + +QQuickRectangle::QQuickRectangle(QQuickItem *parent) +: QQuickItem(*(new QQuickRectanglePrivate), parent) +{ + setFlag(ItemHasContents); +} + +void QQuickRectangle::doUpdate() +{ + Q_D(QQuickRectangle); + qreal penMargin = 0; + qreal penOffset = 0; + if (d->pen && d->pen->isValid()) { + if (d->pen->aligned()) { + const int pw = qRound(d->pen->width()); + penMargin = qreal(0.5) * pw; + penOffset = (pw & 1) * qreal(0.5); + } else { + penMargin = qreal(0.5) * d->pen->width(); + } + } + if (penMargin != d->penMargin || penOffset != d->penOffset) { + d->penMargin = penMargin; + d->penOffset = penOffset; + d->dirty(QQuickItemPrivate::Size); // update clip + } + update(); +} + +/*! + \qmlproperty int QtQuick2::Rectangle::border.width + \qmlproperty color QtQuick2::Rectangle::border.color + + The width and color used to draw the border of the rectangle. + + A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color. + + \note The width of the rectangle's border does not affect the geometry of the + rectangle itself or its position relative to other items if anchors are used. + + If \c border.width is an odd number, the rectangle is painted at a half-pixel offset to retain + border smoothness. Also, the border is rendered evenly on either side of the + rectangle's boundaries, and the spare pixel is rendered to the right and below the + rectangle (as documented for QRect rendering). This can cause unintended effects if + \c border.width is 1 and the rectangle is \l{Item::clip}{clipped} by a parent item: + + \div {class="float-right"} + \inlineimage rect-border-width.png + \enddiv + + \snippet doc/src/snippets/declarative/rectangle/rect-border-width.qml 0 + + \clearfloat + Here, the innermost rectangle's border is clipped on the bottom and right edges by its + parent. To avoid this, the border width can be set to two instead of one. +*/ +QQuickPen *QQuickRectangle::border() +{ + Q_D(QQuickRectangle); + return d->getPen(); +} + +/*! + \qmlproperty Gradient QtQuick2::Rectangle::gradient + + The gradient to use to fill the rectangle. + + This property allows for the construction of simple vertical gradients. + Other gradients may by formed by adding rotation to the rectangle. + + \div {class="float-left"} + \inlineimage declarative-rect_gradient.png + \enddiv + + \snippet doc/src/snippets/declarative/rectangle/rectangle-gradient.qml rectangles + \clearfloat + + If both a gradient and a color are specified, the gradient will be used. + + \sa Gradient, color +*/ +QQuickGradient *QQuickRectangle::gradient() const +{ + Q_D(const QQuickRectangle); + return d->gradient; +} + +void QQuickRectangle::setGradient(QQuickGradient *gradient) +{ + Q_D(QQuickRectangle); + if (d->gradient == gradient) + return; + static int updatedSignalIdx = -1; + if (updatedSignalIdx < 0) + updatedSignalIdx = QQuickGradient::staticMetaObject.indexOfSignal("updated()"); + if (d->doUpdateSlotIdx < 0) + d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()"); + if (d->gradient) + QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); + d->gradient = gradient; + if (d->gradient) + QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); + update(); +} + +/*! + \qmlproperty real QtQuick2::Rectangle::radius + This property holds the corner radius used to draw a rounded rectangle. + + If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be + painted as a normal rectangle. The same radius is used by all 4 corners; there is currently + no way to specify different radii for different corners. +*/ +qreal QQuickRectangle::radius() const +{ + Q_D(const QQuickRectangle); + return d->radius; +} + +void QQuickRectangle::setRadius(qreal radius) +{ + Q_D(QQuickRectangle); + if (d->radius == radius) + return; + + d->radius = radius; + update(); + emit radiusChanged(); +} + +/*! + \qmlproperty color QtQuick2::Rectangle::color + This property holds the color used to fill the rectangle. + + The default color is white. + + \div {class="float-right"} + \inlineimage rect-color.png + \enddiv + + The following example shows rectangles with colors specified + using hexadecimal and named color notation: + + \snippet doc/src/snippets/declarative/rectangle/rectangle-colors.qml rectangles + + \clearfloat + If both a gradient and a color are specified, the gradient will be used. + + \sa gradient +*/ +QColor QQuickRectangle::color() const +{ + Q_D(const QQuickRectangle); + return d->color; +} + +void QQuickRectangle::setColor(const QColor &c) +{ + Q_D(QQuickRectangle); + if (d->color == c) + return; + + d->color = c; + update(); + emit colorChanged(); +} + +QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + Q_D(QQuickRectangle); + + if (width() <= 0 || height() <= 0) { + delete oldNode; + return 0; + } + + QSGRectangleNode *rectangle = static_cast(oldNode); + if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode(); + + rectangle->setRect(QRectF(0, 0, width(), height())); + rectangle->setColor(d->color); + + if (d->pen && d->pen->isValid()) { + rectangle->setPenColor(d->pen->color()); + rectangle->setPenWidth(d->pen->width()); + rectangle->setAligned(d->pen->aligned()); + } else { + rectangle->setPenWidth(0); + } + + rectangle->setRadius(d->radius); + + QGradientStops stops; + if (d->gradient) { + QList qxstops = d->gradient->m_stops; + for (int i = 0; i < qxstops.size(); ++i){ + int j = 0; + while (j < stops.size() && stops.at(j).first < qxstops[i]->position()) + j++; + stops.insert(j, QGradientStop(qxstops.at(i)->position(), qxstops.at(i)->color())); + } + } + rectangle->setGradientStops(stops); + + rectangle->update(); + + return rectangle; +} +/*! + \qmlproperty bool QtQuick2::Rectangle::smooth + + Set this property if you want the item to be smoothly scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. + + \image rect-smooth.png + On this image, smooth is turned off on the top half and on on the bottom half. +*/ + +QRectF QQuickRectangle::boundingRect() const +{ + Q_D(const QQuickRectangle); + return QRectF(d->penOffset - d->penMargin, d->penOffset - d->penMargin, + d->width + 2 * d->penMargin, d->height + 2 * d->penMargin); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickrectangle_p.h b/src/declarative/items/qquickrectangle_p.h new file mode 100644 index 0000000000..838b944c62 --- /dev/null +++ b/src/declarative/items/qquickrectangle_p.h @@ -0,0 +1,189 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKRECTANGLE_P_H +#define QQUICKRECTANGLE_P_H + +#include "qquickitem.h" + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_PRIVATE_EXPORT QQuickPen : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY penChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY penChanged) + Q_PROPERTY(bool aligned READ aligned WRITE setAligned NOTIFY penChanged) +public: + QQuickPen(QObject *parent=0); + + qreal width() const; + void setWidth(qreal w); + + QColor color() const; + void setColor(const QColor &c); + + bool aligned() const; + void setAligned(bool aligned); + + bool isValid() const; + +Q_SIGNALS: + void penChanged(); + +private: + qreal m_width; + QColor m_color; + bool m_aligned : 1; + bool m_valid : 1; +}; + +class Q_AUTOTEST_EXPORT QQuickGradientStop : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QQuickGradientStop(QObject *parent=0); + + qreal position() const; + void setPosition(qreal position); + + QColor color() const; + void setColor(const QColor &color); + +private: + void updateGradient(); + +private: + qreal m_position; + QColor m_color; +}; + +class Q_AUTOTEST_EXPORT QQuickGradient : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeListProperty stops READ stops) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QQuickGradient(QObject *parent=0); + ~QQuickGradient(); + + QDeclarativeListProperty stops(); + + const QGradient *gradient() const; + +Q_SIGNALS: + void updated(); + +private: + void doUpdate(); + +private: + QList m_stops; + mutable QGradient *m_gradient; + friend class QQuickRectangle; + friend class QQuickGradientStop; +}; + +class QQuickRectanglePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QQuickRectangle : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QQuickGradient *gradient READ gradient WRITE setGradient) + Q_PROPERTY(QQuickPen * border READ border CONSTANT) + Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) +public: + QQuickRectangle(QQuickItem *parent=0); + + QColor color() const; + void setColor(const QColor &); + + QQuickPen *border(); + + QQuickGradient *gradient() const; + void setGradient(QQuickGradient *gradient); + + qreal radius() const; + void setRadius(qreal radius); + + virtual QRectF boundingRect() const; + +Q_SIGNALS: + void colorChanged(); + void radiusChanged(); + +protected: + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private Q_SLOTS: + void doUpdate(); + +private: + Q_DISABLE_COPY(QQuickRectangle) + Q_DECLARE_PRIVATE(QQuickRectangle) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPen) +QML_DECLARE_TYPE(QQuickGradientStop) +QML_DECLARE_TYPE(QQuickGradient) +QML_DECLARE_TYPE(QQuickRectangle) + +QT_END_HEADER + +#endif // QQUICKRECTANGLE_P_H diff --git a/src/declarative/items/qquickrectangle_p_p.h b/src/declarative/items/qquickrectangle_p_p.h new file mode 100644 index 0000000000..93140ce75a --- /dev/null +++ b/src/declarative/items/qquickrectangle_p_p.h @@ -0,0 +1,103 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKRECTANGLE_P_P_H +#define QQUICKRECTANGLE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickGradient; +class QQuickRectangle; +class QQuickRectanglePrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickRectangle) + +public: + QQuickRectanglePrivate() : + color(Qt::white), gradient(0), pen(0), radius(0), penMargin(0), penOffset(0) + { + } + + ~QQuickRectanglePrivate() + { + delete pen; + } + + QColor color; + QQuickGradient *gradient; + QQuickPen *pen; + qreal radius; + qreal penMargin; + qreal penOffset; + static int doUpdateSlotIdx; + + QQuickPen *getPen() { + if (!pen) { + Q_Q(QQuickRectangle); + pen = new QQuickPen; + static int penChangedSignalIdx = -1; + if (penChangedSignalIdx < 0) + penChangedSignalIdx = QQuickPen::staticMetaObject.indexOfSignal("penChanged()"); + if (doUpdateSlotIdx < 0) + doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()"); + QMetaObject::connect(pen, penChangedSignalIdx, q, doUpdateSlotIdx); + } + return pen; + } +}; + +QT_END_NAMESPACE + +#endif // QQUICKRECTANGLE_P_P_H diff --git a/src/declarative/items/qquickrepeater.cpp b/src/declarative/items/qquickrepeater.cpp new file mode 100644 index 0000000000..48632efe83 --- /dev/null +++ b/src/declarative/items/qquickrepeater.cpp @@ -0,0 +1,438 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickrepeater_p.h" +#include "qquickrepeater_p_p.h" +#include "qquickvisualdatamodel_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickRepeaterPrivate::QQuickRepeaterPrivate() +: model(0), ownModel(false) +{ +} + +QQuickRepeaterPrivate::~QQuickRepeaterPrivate() +{ + if (ownModel) + delete model; +} + +/*! + \qmlclass Repeater QQuickRepeater + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \inherits Item + + \brief The Repeater element allows you to repeat an Item-based component using a model. + + The Repeater element is used to create a large number of + similar items. Like other view elements, a Repeater has a \l model and a \l delegate: + for each entry in the model, the delegate is instantiated + in a context seeded with data from the model. A Repeater item is usually + enclosed in a positioner element such as \l Row or \l Column to visually + position the multiple delegate items created by the Repeater. + + The following Repeater creates three instances of a \l Rectangle item within + a \l Row: + + \snippet doc/src/snippets/declarative/repeaters/repeater.qml import + \codeline + \snippet doc/src/snippets/declarative/repeaters/repeater.qml simple + + \image repeater-simple.png + + A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}. + Additionally, like delegates for other views, a Repeater delegate can access + its index within the repeater, as well as the model data relevant to the + delegate. See the \l delegate property documentation for details. + + Items instantiated by the Repeater are inserted, in order, as + children of the Repeater's parent. The insertion starts immediately after + the repeater's position in its parent stacking list. This allows + a Repeater to be used inside a layout. For example, the following Repeater's + items are stacked between a red rectangle and a blue rectangle: + + \snippet doc/src/snippets/declarative/repeaters/repeater.qml layout + + \image repeater.png + + + \note A Repeater item owns all items it instantiates. Removing or dynamically destroying + an item created by a Repeater results in unpredictable behavior. + + + \section2 Considerations when using Repeater + + The Repeater element creates all of its delegate items when the repeater is first + created. This can be inefficient if there are a large number of delegate items and + not all of the items are required to be visible at the same time. If this is the case, + consider using other view elements like ListView (which only creates delegate items + when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to + create items as they are required. + + Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects. + For example, it cannot be used to repeat QtObjects: + \badcode + Item { + //XXX does not work! Can't repeat QtObject as it doesn't derive from Item. + Repeater { + model: 10 + QtObject {} + } + } + \endcode + */ + +/*! + \qmlsignal QtQuick2::Repeater::onItemAdded(int index, Item item) + + This handler is called when an item is added to the repeater. The \a index + parameter holds the index at which the item has been inserted within the + repeater, and the \a item parameter holds the \l Item that has been added. +*/ + +/*! + \qmlsignal QtQuick2::Repeater::onItemRemoved(int index, Item item) + + This handler is called when an item is removed from the repeater. The \a index + parameter holds the index at which the item was removed from the repeater, + and the \a item parameter holds the \l Item that was removed. + + Do not keep a reference to \a item if it was created by this repeater, as + in these cases it will be deleted shortly after the handler is called. +*/ +QQuickRepeater::QQuickRepeater(QQuickItem *parent) + : QQuickItem(*(new QQuickRepeaterPrivate), parent) +{ +} + +QQuickRepeater::~QQuickRepeater() +{ +} + +/*! + \qmlproperty any QtQuick2::Repeater::model + + The model providing data for the repeater. + + This property can be set to any of the supported \l {qmlmodels}{data models}: + + \list + \o A number that indicates the number of delegates to be created by the repeater + \o A model (e.g. a ListModel item, or a QAbstractItemModel subclass) + \o A string list + \o An object list + \endlist + + The type of model affects the properties that are exposed to the \l delegate. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QQuickRepeater::model() const +{ + Q_D(const QQuickRepeater); + return d->dataSource; +} + +void QQuickRepeater::setModel(const QVariant &model) +{ + Q_D(QQuickRepeater); + if (d->dataSource == model) + return; + + clear(); + if (d->model) { + disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + /* + disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + */ + } + d->dataSource = model; + QObject *object = qvariant_cast(model); + QQuickVisualModel *vim = 0; + if (object && (vim = qobject_cast(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this)); + d->ownModel = true; + if (isComponentComplete()) + static_cast(d->model)->componentComplete(); + } + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + dataModel->setModel(model); + } + if (d->model) { + connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + /* + connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + */ + regenerate(); + } + emit modelChanged(); + emit countChanged(); +} + +/*! + \qmlproperty Component QtQuick2::Repeater::delegate + \default + + The delegate provides a template defining each item instantiated by the repeater. + + Delegates are exposed to a read-only \c index property that indicates the index + of the delegate within the repeater. For example, the following \l Text delegate + displays the index of each repeated item: + + \table + \row + \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index + \o \image repeater-index.png + \endtable + + If the \l model is a \l{QStringList-based model}{string list} or + \l{QObjectList-based model}{object list}, the delegate is also exposed to + a read-only \c modelData property that holds the string or object data. For + example: + + \table + \row + \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata + \o \image repeater-modeldata.png + \endtable + + If the \l model is a model object (such as a \l ListModel) the delegate + can access all model roles as named properties, in the same way that delegates + do for view classes like ListView. + + \sa {QML Data Models} + */ +QDeclarativeComponent *QQuickRepeater::delegate() const +{ + Q_D(const QQuickRepeater); + if (d->model) { + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QQuickRepeater::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QQuickRepeater); + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (delegate == dataModel->delegate()) + return; + + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) { + dataModel->setDelegate(delegate); + regenerate(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int QtQuick2::Repeater::count + + This property holds the number of items in the repeater. +*/ +int QQuickRepeater::count() const +{ + Q_D(const QQuickRepeater); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlmethod Item QtQuick2::Repeater::itemAt(index) + + Returns the \l Item that has been created at the given \a index, or \c null + if no item exists at \a index. +*/ +QQuickItem *QQuickRepeater::itemAt(int index) const +{ + Q_D(const QQuickRepeater); + if (index >= 0 && index < d->deletables.count()) + return d->deletables[index]; + return 0; +} + +void QQuickRepeater::componentComplete() +{ + Q_D(QQuickRepeater); + if (d->model && d->ownModel) + static_cast(d->model)->componentComplete(); + QQuickItem::componentComplete(); + regenerate(); + if (d->model && d->model->count()) + emit countChanged(); +} + +void QQuickRepeater::itemChange(ItemChange change, const ItemChangeData &value) +{ + QQuickItem::itemChange(change, value); + if (change == ItemParentHasChanged) { + regenerate(); + } +} + +void QQuickRepeater::clear() +{ + Q_D(QQuickRepeater); + bool complete = isComponentComplete(); + + if (d->model) { + while (d->deletables.count() > 0) { + QQuickItem *item = d->deletables.takeLast(); + if (complete) + emit itemRemoved(d->deletables.count()-1, item); + d->model->release(item); + } + } + d->deletables.clear(); +} + +void QQuickRepeater::regenerate() +{ + Q_D(QQuickRepeater); + if (!isComponentComplete()) + return; + + clear(); + + if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete()) + return; + + for (int ii = 0; ii < count(); ++ii) { + QQuickItem *item = d->model->item(ii); + if (item) { + QDeclarative_setParent_noEvent(item, parentItem()); + item->setParentItem(parentItem()); + item->stackBefore(this); + d->deletables << item; + emit itemAdded(ii, item); + } + } +} + +void QQuickRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) +{ + Q_D(QQuickRepeater); + + if (!isComponentComplete()) + return; + + if (reset) { + regenerate(); + emit countChanged(); + } + + int difference = 0; + QHash > > moved; + foreach (const QDeclarativeChangeSet::Remove &remove, changeSet.removes()) { + int index = qMin(remove.index, d->deletables.count()); + int count = qMin(remove.index + remove.count, d->deletables.count()) - index; + if (remove.isMove()) { + moved.insert(remove.moveId, d->deletables.mid(index, count)); + d->deletables.erase( + d->deletables.begin() + index, + d->deletables.begin() + index + count); + } else while (count--) { + QQuickItem *item = d->deletables.takeAt(index); + emit itemRemoved(index, item); + if (item) + d->model->release(item); + } + + difference -= remove.count; + } + + foreach (const QDeclarativeChangeSet::Insert &insert, changeSet.inserts()) { + int index = qMin(insert.index, d->deletables.count()); + if (insert.isMove()) { + QList > items = moved.value(insert.moveId); + d->deletables = d->deletables.mid(0, index) + items + d->deletables.mid(index); + QQuickItem *stackBefore = index + items.count() < d->deletables.count() + ? d->deletables.at(index + items.count()) + : this; + for (int i = index; i < index + items.count(); ++i) + d->deletables.at(i)->stackBefore(stackBefore); + } else for (int i = 0; i < insert.count; ++i) { + int modelIndex = index + i; + QQuickItem *item = d->model->item(modelIndex); + if (item) { + QDeclarative_setParent_noEvent(item, parentItem()); + item->setParentItem(parentItem()); + if (modelIndex < d->deletables.count()) + item->stackBefore(d->deletables.at(modelIndex)); + else + item->stackBefore(this); + d->deletables.insert(modelIndex, item); + emit itemAdded(modelIndex, item); + } + } + difference += insert.count; + } + + if (difference != 0) + emit countChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickrepeater_p.h b/src/declarative/items/qquickrepeater_p.h new file mode 100644 index 0000000000..66e583e406 --- /dev/null +++ b/src/declarative/items/qquickrepeater_p.h @@ -0,0 +1,110 @@ +// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKREPEATER_P_H +#define QQUICKREPEATER_P_H + +#include "qquickitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeChangeSet; + +class QQuickRepeaterPrivate; +class Q_AUTOTEST_EXPORT QQuickRepeater : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_CLASSINFO("DefaultProperty", "delegate") + +public: + QQuickRepeater(QQuickItem *parent=0); + virtual ~QQuickRepeater(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int count() const; + + Q_INVOKABLE QQuickItem *itemAt(int index) const; + +Q_SIGNALS: + void modelChanged(); + void delegateChanged(); + void countChanged(); + + void itemAdded(int index, QQuickItem *item); + void itemRemoved(int index, QQuickItem *item); + +private: + void clear(); + void regenerate(); + +protected: + virtual void componentComplete(); + void itemChange(ItemChange change, const ItemChangeData &value); + +private Q_SLOTS: + void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); + +private: + Q_DISABLE_COPY(QQuickRepeater) + Q_DECLARE_PRIVATE(QQuickRepeater) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickRepeater) + +QT_END_HEADER + +#endif // QQUICKREPEATER_P_H diff --git a/src/declarative/items/qquickrepeater_p_p.h b/src/declarative/items/qquickrepeater_p_p.h new file mode 100644 index 0000000000..a1ec15960a --- /dev/null +++ b/src/declarative/items/qquickrepeater_p_p.h @@ -0,0 +1,83 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKREPEATER_P_P_H +#define QQUICKREPEATER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickrepeater_p.h" +#include "qquickitem_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QQuickVisualModel; +class QQuickRepeaterPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickRepeater) + +public: + QQuickRepeaterPrivate(); + ~QQuickRepeaterPrivate(); + + QQuickVisualModel *model; + QVariant dataSource; + bool ownModel; + + QList > deletables; +}; + +QT_END_NAMESPACE + +#endif // QQUICKREPEATER_P_P_H diff --git a/src/declarative/items/qquickscalegrid.cpp b/src/declarative/items/qquickscalegrid.cpp new file mode 100644 index 0000000000..9a8f652a9b --- /dev/null +++ b/src/declarative/items/qquickscalegrid.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickscalegrid_p_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QQuickScaleGrid + \brief The QQuickScaleGrid class allows you to specify a 3x3 grid to use in scaling an image. +*/ + +QQuickScaleGrid::QQuickScaleGrid(QObject *parent) : QObject(parent), _left(0), _top(0), _right(0), _bottom(0) +{ +} + +QQuickScaleGrid::~QQuickScaleGrid() +{ +} + +bool QQuickScaleGrid::isNull() const +{ + return !_left && !_top && !_right && !_bottom; +} + +void QQuickScaleGrid::setLeft(int pos) +{ + if (_left != pos) { + _left = pos; + emit borderChanged(); + } +} + +void QQuickScaleGrid::setTop(int pos) +{ + if (_top != pos) { + _top = pos; + emit borderChanged(); + } +} + +void QQuickScaleGrid::setRight(int pos) +{ + if (_right != pos) { + _right = pos; + emit borderChanged(); + } +} + +void QQuickScaleGrid::setBottom(int pos) +{ + if (_bottom != pos) { + _bottom = pos; + emit borderChanged(); + } +} + +QQuickGridScaledImage::QQuickGridScaledImage() +: _l(-1), _r(-1), _t(-1), _b(-1), + _h(QQuickBorderImage::Stretch), _v(QQuickBorderImage::Stretch) +{ +} + +QQuickGridScaledImage::QQuickGridScaledImage(const QQuickGridScaledImage &o) +: _l(o._l), _r(o._r), _t(o._t), _b(o._b), _h(o._h), _v(o._v), _pix(o._pix) +{ +} + +QQuickGridScaledImage &QQuickGridScaledImage::operator=(const QQuickGridScaledImage &o) +{ + _l = o._l; + _r = o._r; + _t = o._t; + _b = o._b; + _h = o._h; + _v = o._v; + _pix = o._pix; + return *this; +} + +QQuickGridScaledImage::QQuickGridScaledImage(QIODevice *data) +: _l(-1), _r(-1), _t(-1), _b(-1), _h(QQuickBorderImage::Stretch), _v(QQuickBorderImage::Stretch) +{ + int l = -1; + int r = -1; + int t = -1; + int b = -1; + QString imgFile; + + QByteArray raw; + while (raw = data->readLine(), !raw.isEmpty()) { + QString line = QString::fromUtf8(raw.trimmed()); + if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) + continue; + + int colonId = line.indexOf(QLatin1Char(':')); + if (colonId <= 0) + return; + + QStringList list; + list.append(line.left(colonId).trimmed()); + list.append(line.mid(colonId+1).trimmed()); + + if (list[0] == QLatin1String("border.left")) + l = list[1].toInt(); + else if (list[0] == QLatin1String("border.right")) + r = list[1].toInt(); + else if (list[0] == QLatin1String("border.top")) + t = list[1].toInt(); + else if (list[0] == QLatin1String("border.bottom")) + b = list[1].toInt(); + else if (list[0] == QLatin1String("source")) + imgFile = list[1]; + else if (list[0] == QLatin1String("horizontalTileRule")) + _h = stringToRule(list[1]); + else if (list[0] == QLatin1String("verticalTileRule")) + _v = stringToRule(list[1]); + } + + if (l < 0 || r < 0 || t < 0 || b < 0 || imgFile.isEmpty()) + return; + + _l = l; _r = r; _t = t; _b = b; + + _pix = imgFile; + if (_pix.startsWith(QLatin1Char('"')) && _pix.endsWith(QLatin1Char('"'))) + _pix = _pix.mid(1, _pix.size() - 2); // remove leading/trailing quotes. +} + +QQuickBorderImage::TileMode QQuickGridScaledImage::stringToRule(const QString &s) +{ + if (s == QLatin1String("Stretch")) + return QQuickBorderImage::Stretch; + if (s == QLatin1String("Repeat")) + return QQuickBorderImage::Repeat; + if (s == QLatin1String("Round")) + return QQuickBorderImage::Round; + + qWarning("QQuickGridScaledImage: Invalid tile rule specified. Using Stretch."); + return QQuickBorderImage::Stretch; +} + +bool QQuickGridScaledImage::isValid() const +{ + return _l >= 0; +} + +int QQuickGridScaledImage::gridLeft() const +{ + return _l; +} + +int QQuickGridScaledImage::gridRight() const +{ + return _r; +} + +int QQuickGridScaledImage::gridTop() const +{ + return _t; +} + +int QQuickGridScaledImage::gridBottom() const +{ + return _b; +} + +QString QQuickGridScaledImage::pixmapUrl() const +{ + return _pix; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickscalegrid_p_p.h b/src/declarative/items/qquickscalegrid_p_p.h new file mode 100644 index 0000000000..8d93c3b1c9 --- /dev/null +++ b/src/declarative/items/qquickscalegrid_p_p.h @@ -0,0 +1,134 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSCALEGRID_P_P_H +#define QQUICKSCALEGRID_P_P_H + +#include "qquickborderimage_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_PRIVATE_EXPORT QQuickScaleGrid : public QObject +{ + Q_OBJECT + Q_ENUMS(TileRule) + + Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged) + Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged) + Q_PROPERTY(int right READ right WRITE setRight NOTIFY borderChanged) + Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY borderChanged) + +public: + QQuickScaleGrid(QObject *parent=0); + ~QQuickScaleGrid(); + + bool isNull() const; + + int left() const { return _left; } + void setLeft(int); + + int top() const { return _top; } + void setTop(int); + + int right() const { return _right; } + void setRight(int); + + int bottom() const { return _bottom; } + void setBottom(int); + +Q_SIGNALS: + void borderChanged(); + +private: + int _left; + int _top; + int _right; + int _bottom; +}; + +class Q_DECLARATIVE_PRIVATE_EXPORT QQuickGridScaledImage +{ +public: + QQuickGridScaledImage(); + QQuickGridScaledImage(const QQuickGridScaledImage &); + QQuickGridScaledImage(QIODevice*); + QQuickGridScaledImage &operator=(const QQuickGridScaledImage &); + bool isValid() const; + int gridLeft() const; + int gridRight() const; + int gridTop() const; + int gridBottom() const; + QQuickBorderImage::TileMode horizontalTileRule() const { return _h; } + QQuickBorderImage::TileMode verticalTileRule() const { return _v; } + + QString pixmapUrl() const; + +private: + static QQuickBorderImage::TileMode stringToRule(const QString &); + +private: + int _l; + int _r; + int _t; + int _b; + QQuickBorderImage::TileMode _h; + QQuickBorderImage::TileMode _v; + QString _pix; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickScaleGrid) + +QT_END_HEADER + +#endif // QQUICKSCALEGRID_P_P_H diff --git a/src/declarative/items/qquickshadereffect.cpp b/src/declarative/items/qquickshadereffect.cpp new file mode 100644 index 0000000000..a3b57be1eb --- /dev/null +++ b/src/declarative/items/qquickshadereffect.cpp @@ -0,0 +1,649 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qsgmaterial.h" +#include "qquickitem_p.h" + +#include +#include +#include "qquickcanvas.h" + +#include "qquickimage_p.h" +#include "qquickshadereffectsource_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +static const char qt_default_vertex_code[] = + "uniform highp mat4 qt_Matrix; \n" + "attribute highp vec4 qt_Vertex; \n" + "attribute highp vec2 qt_MultiTexCoord0; \n" + "varying highp vec2 qt_TexCoord0; \n" + "void main() { \n" + " qt_TexCoord0 = qt_MultiTexCoord0; \n" + " gl_Position = qt_Matrix * qt_Vertex; \n" + "}"; + +static const char qt_default_fragment_code[] = + "varying highp vec2 qt_TexCoord0; \n" + "uniform sampler2D source; \n" + "uniform lowp float qt_Opacity; \n" + "void main() { \n" + " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n" + "}"; + +static const char qt_position_attribute_name[] = "qt_Vertex"; +static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; + +const char *qtPositionAttributeName() +{ + return qt_position_attribute_name; +} + +const char *qtTexCoordAttributeName() +{ + return qt_texcoord_attribute_name; +} + +// TODO: Remove after grace period. +QQuickShaderEffectItem::QQuickShaderEffectItem(QQuickItem *parent) + : QQuickShaderEffect(parent) +{ + qWarning("ShaderEffectItem has been deprecated. Use ShaderEffect instead."); +} + + +/*! + \qmlclass ShaderEffect QQuickShaderEffect + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The ShaderEffect element applies custom shaders to a rectangle. + \inherits Item + + The ShaderEffect element applies a custom OpenGL + \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a + rectangle. It allows you to write effects such as drop shadow, blur, + colorize and page curl directly in QML. + + There are two types of input to the \l vertexShader: + uniform variables and attributes. Some are predefined: + \list + \o uniform mat4 qt_Matrix - combined transformation + matrix, the product of the matrices from the root item to this + ShaderEffect, and an orthogonal projection. + \o uniform float qt_Opacity - combined opacity, the product of the + opacities from the root item to this ShaderEffect. + \o attribute vec4 qt_Vertex - vertex position, the top-left vertex has + position (0, 0), the bottom-right (\l{Item::width}{width}, + \l{Item::height}{height}). + \o attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left + coordinate is (0, 0), the bottom-right (1, 1). + \endlist + + In addition, any property that can be mapped to an OpenGL Shading Language + (GLSL) type is available as a uniform variable. The following list shows + how properties are mapped to GLSL uniform variables: + \list + \o bool, int, qreal -> bool, int, float - If the type in the shader is not + the same as in QML, the value is converted automatically. + \o QColor -> vec4 - When colors are passed to the shader, they are first + premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes + vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example. + \o QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in + the shader. + \o QPoint, QPointF, QSize, QSizeF -> vec2 + \o QVector3D -> vec3 + \o QTransform -> mat4 + \o \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left + corner, and the color values are premultiplied. + \endlist + + The output from the \l fragmentShader should be premultiplied. If + \l blending is enabled, source-over blending is used. However, additive + blending can be achieved by outputting zero in the alpha channel. + + \row + \o \image declarative-shadereffectitem.png + \o \qml + import QtQuick 2.0 + + Rectangle { + width: 200; height: 100 + Row { + Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" } + ShaderEffect { + width: 100; height: 100 + property variant src: img + vertexShader: " + uniform highp mat4 qt_Matrix; + attribute highp vec4 qt_Vertex; + attribute highp vec2 qt_MultiTexCoord0; + varying highp vec2 coord; + void main() { + coord = qt_MultiTexCoord0; + gl_Position = qt_Matrix * qt_Vertex; + }" + fragmentShader: " + varying highp vec2 coord; + uniform sampler2D src; + uniform lowp float qt_Opacity; + void main() { + lowp vec4 tex = texture2D(src, coord); + gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity; + }" + } + } + } + \endqml + \endrow + + By default, the ShaderEffect consists of four vertices, one for each + corner. For non-linear vertex transformations, like page curl, you can + specify a fine grid of vertices by specifying a \l mesh resolution. + + \note Scene Graph textures have origin in the top-left corner rather than + bottom-left which is common in OpenGL. +*/ + +QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent) + : QQuickItem(parent) + , m_meshResolution(1, 1) + , m_mesh(0) + , m_cullMode(NoCulling) + , m_blending(true) + , m_dirtyData(true) + , m_programDirty(true) + , m_dirtyMesh(true) + , m_dirtyGeometry(true) +{ + setFlag(QQuickItem::ItemHasContents); +} + +QQuickShaderEffect::~QQuickShaderEffect() +{ + reset(); +} + +void QQuickShaderEffect::componentComplete() +{ + updateProperties(); + QQuickItem::componentComplete(); +} + +/*! + \qmlproperty string QtQuick2::ShaderEffect::fragmentShader + + This property holds the fragment shader's GLSL source code. + The default shader passes the texture coordinate along to the fragment + shader as "varying highp vec2 qt_TexCoord0". +*/ + +void QQuickShaderEffect::setFragmentShader(const QByteArray &code) +{ + if (m_source.fragmentCode.constData() == code.constData()) + return; + m_source.fragmentCode = code; + if (isComponentComplete()) { + reset(); + updateProperties(); + } + emit fragmentShaderChanged(); +} + +/*! + \qmlproperty string QtQuick2::ShaderEffect::vertexShader + + This property holds the vertex shader's GLSL source code. + The default shader expects the texture coordinate to be passed from the + vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a + sampler2D named "source". +*/ + +void QQuickShaderEffect::setVertexShader(const QByteArray &code) +{ + if (m_source.vertexCode.constData() == code.constData()) + return; + m_source.vertexCode = code; + if (isComponentComplete()) { + reset(); + updateProperties(); + } + emit vertexShaderChanged(); +} + +/*! + \qmlproperty bool QtQuick2::ShaderEffect::blending + + If this property is true, the output from the \l fragmentShader is blended + with the background using source-over blend mode. If false, the background + is disregarded. Blending decreases the performance, so you should set this + property to false when blending is not needed. The default value is true. +*/ + +void QQuickShaderEffect::setBlending(bool enable) +{ + if (blending() == enable) + return; + + m_blending = enable; + update(); + + emit blendingChanged(); +} + +/*! + \qmlproperty variant QtQuick2::ShaderEffect::mesh + + This property defines the mesh used to draw the ShaderEffect. It can hold + any mesh object deriving from \l QQuickShaderEffectMesh, such as \l GridMesh. + If a size value is assigned to this property, the ShaderEffect implicitly + uses a \l GridMesh with the value as + \l{GridMesh::resolution}{mesh resolution}. By default, this property is + the size 1x1. + + \sa GridMesh +*/ + +QVariant QQuickShaderEffect::mesh() const +{ + return m_mesh ? qVariantFromValue(static_cast(m_mesh)) + : qVariantFromValue(m_meshResolution); +} + +void QQuickShaderEffect::setMesh(const QVariant &mesh) +{ + QQuickShaderEffectMesh *newMesh = qobject_cast(qVariantValue(mesh)); + if (newMesh && newMesh == m_mesh) + return; + if (m_mesh) + disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0); + m_mesh = newMesh; + if (m_mesh) { + connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry())); + } else { + if (qVariantCanConvert(mesh)) { + m_meshResolution = mesh.toSize(); + } else { + QList res = mesh.toByteArray().split('x'); + bool ok = res.size() == 2; + if (ok) { + int w = res.at(0).toInt(&ok); + if (ok) { + int h = res.at(1).toInt(&ok); + if (ok) + m_meshResolution = QSize(w, h); + } + } + if (!ok) + qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh."); + } + m_defaultMesh.setResolution(m_meshResolution); + } + + m_dirtyMesh = true; + update(); + emit meshChanged(); +} + +/*! + \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode + + This property defines which sides of the element should be visible. + + \list + \o ShaderEffect.NoCulling - Both sides are visible + \o ShaderEffect.BackFaceCulling - only front side is visible + \o ShaderEffect.FrontFaceCulling - only back side is visible + \endlist + + The default is NoCulling. +*/ + +void QQuickShaderEffect::setCullMode(CullMode face) +{ + if (face == m_cullMode) + return; + m_cullMode = face; + update(); + emit cullModeChanged(); +} + +void QQuickShaderEffect::changeSource(int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + QVariant v = property(m_sources.at(index).name.constData()); + setSource(v, index); +} + +void QQuickShaderEffect::updateData() +{ + m_dirtyData = true; + update(); +} + +void QQuickShaderEffect::updateGeometry() +{ + m_dirtyGeometry = true; + update(); +} + +void QQuickShaderEffect::setSource(const QVariant &var, int index) +{ + Q_ASSERT(index >= 0 && index < m_sources.size()); + + SourceData &source = m_sources[index]; + + source.sourceObject = 0; + if (var.isNull()) { + return; + } else if (!qVariantCanConvert(var)) { + qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); + return; + } + + QObject *obj = qVariantValue(var); + QQuickItem *item = qobject_cast(obj); + if (!item || !item->isTextureProvider()) { + qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]", + source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className()); + return; + } + + source.sourceObject = item; + + + // TODO: Find better solution. + // 'item' needs a canvas to get a scenegraph node. + // The easiest way to make sure it gets a canvas is to + // make it a part of the same item tree as 'this'. + if (item && item->parentItem() == 0) { + item->setParentItem(this); + item->setVisible(false); + } +} + +void QQuickShaderEffect::disconnectPropertySignals() +{ + disconnect(this, 0, this, SLOT(updateData())); + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + disconnect(this, 0, source.mapper, 0); + disconnect(source.mapper, 0, this, 0); + } +} + +void QQuickShaderEffect::connectPropertySignals() +{ + QSet::const_iterator it; + for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { + int pi = metaObject()->indexOfProperty(it->constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("QQuickShaderEffect: property '%s' does not have notification method!", it->constData()); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, this, SLOT(updateData())); + } else { + qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData()); + } + } + for (int i = 0; i < m_sources.size(); ++i) { + SourceData &source = m_sources[i]; + int pi = metaObject()->indexOfProperty(source.name.constData()); + if (pi >= 0) { + QMetaProperty mp = metaObject()->property(pi); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().signature()); + connect(this, signalName, source.mapper, SLOT(map())); + source.mapper->setMapping(this, i); + connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); + } else { + qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData()); + } + } +} + +void QQuickShaderEffect::reset() +{ + disconnectPropertySignals(); + + m_source.attributeNames.clear(); + m_source.uniformNames.clear(); + m_source.respectsOpacity = false; + m_source.respectsMatrix = false; + m_source.className = metaObject()->className(); + + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + delete source.mapper; + QQuickItem *item = qobject_cast(source.sourceObject); + if (item && item->parentItem() == this) + item->setParentItem(0); + } + m_sources.clear(); + + m_programDirty = true; + m_dirtyMesh = true; +} + +void QQuickShaderEffect::updateProperties() +{ + QByteArray vertexCode = m_source.vertexCode; + QByteArray fragmentCode = m_source.fragmentCode; + if (vertexCode.isEmpty()) + vertexCode = qt_default_vertex_code; + if (fragmentCode.isEmpty()) + fragmentCode = qt_default_fragment_code; + + lookThroughShaderCode(vertexCode); + lookThroughShaderCode(fragmentCode); + + if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) + qWarning("QQuickShaderEffect: Missing reference to \'%s\'.", qt_position_attribute_name); + if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) + qWarning("QQuickShaderEffect: Missing reference to \'%s\'.", qt_texcoord_attribute_name); + if (!m_source.respectsMatrix) + qWarning("QQuickShaderEffect: Missing reference to \'qt_Matrix\'."); + if (!m_source.respectsOpacity) + qWarning("QQuickShaderEffect: Missing reference to \'qt_Opacity\'."); + + for (int i = 0; i < m_sources.size(); ++i) { + QVariant v = property(m_sources.at(i).name); + setSource(v, i); + } + + connectPropertySignals(); +} + +void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code) +{ + // Regexp for matching attributes and uniforms. + // In human readable form: attribute|uniform [lowp|mediump|highp] + static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); + Q_ASSERT(re.isValid()); + + int pos = -1; + + QString wideCode = QString::fromLatin1(code.constData(), code.size()); + + while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { + QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute + QByteArray type = re.cap(2).toLatin1(); // type + QByteArray name = re.cap(3).toLatin1(); // variable name + + if (decl == "attribute") { + m_source.attributeNames.append(name); + } else { + Q_ASSERT(decl == "uniform"); + + if (name == "qt_Matrix") { + m_source.respectsMatrix = true; + } else if (name == "qt_ModelViewProjectionMatrix") { + // TODO: Remove after grace period. + qWarning("ShaderEffect: qt_ModelViewProjectionMatrix is deprecated. Use qt_Matrix instead."); + m_source.respectsMatrix = true; + } else if (name == "qt_Opacity") { + m_source.respectsOpacity = true; + } else { + m_source.uniformNames.insert(name); + if (type == "sampler2D") { + SourceData d; + d.mapper = new QSignalMapper; + d.name = name; + d.sourceObject = 0; + m_sources.append(d); + } + } + } + } +} + +void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + m_dirtyGeometry = true; + QQuickItem::geometryChanged(newGeometry, oldGeometry); +} + +QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + QQuickShaderEffectNode *node = static_cast(oldNode); + + // In the case of a bad vertex shader, don't try to create a node... + if (m_source.attributeNames.isEmpty()) { + if (node) + delete node; + return 0; + } + + if (!node) { + node = new QQuickShaderEffectNode; + m_programDirty = true; + m_dirtyData = true; + m_dirtyGeometry = true; + } + + QQuickShaderEffectMaterial *material = node->shaderMaterial(); + + if (m_dirtyMesh) { + node->setGeometry(0); + m_dirtyMesh = false; + m_dirtyGeometry = true; + } + + if (m_dirtyGeometry) { + node->setFlag(QSGNode::OwnsGeometry, false); + QSGGeometry *geometry = node->geometry(); + QRectF rect(0, 0, width(), height()); + QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh; + + geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect); + if (!geometry) { + delete node; + return 0; + } + + node->setGeometry(geometry); + node->setFlag(QSGNode::OwnsGeometry, true); + + m_dirtyGeometry = false; + } + + if (m_programDirty) { + QQuickShaderEffectProgram s = m_source; + if (s.fragmentCode.isEmpty()) + s.fragmentCode = qt_default_fragment_code; + if (s.vertexCode.isEmpty()) + s.vertexCode = qt_default_vertex_code; + s.className = metaObject()->className(); + + material->setProgramSource(s); + node->markDirty(QSGNode::DirtyMaterial); + m_programDirty = false; + } + + // Update blending + if (bool(material->flags() & QSGMaterial::Blending) != m_blending) { + material->setFlag(QSGMaterial::Blending, m_blending); + node->markDirty(QSGNode::DirtyMaterial); + } + + if (int(material->cullMode()) != int(m_cullMode)) { + material->setCullMode(QQuickShaderEffectMaterial::CullMode(m_cullMode)); + node->markDirty(QSGNode::DirtyMaterial); + } + + if (m_dirtyData) { + QVector > values; + QVector > textures; + const QVector > &oldTextures = material->textureProviders(); + + for (QSet::const_iterator it = m_source.uniformNames.begin(); + it != m_source.uniformNames.end(); ++it) { + values.append(qMakePair(*it, property(*it))); + } + for (int i = 0; i < oldTextures.size(); ++i) { + QSGTextureProvider *t = oldTextures.at(i).second; + if (t) + disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + } + for (int i = 0; i < m_sources.size(); ++i) { + const SourceData &source = m_sources.at(i); + QSGTextureProvider *t = source.sourceObject->textureProvider(); + textures.append(qMakePair(source.name, t)); + if (t) + connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); + } + material->setUniforms(values); + material->setTextureProviders(textures); + node->markDirty(QSGNode::DirtyMaterial); + m_dirtyData = false; + } + + return node; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickshadereffect_p.h b/src/declarative/items/qquickshadereffect_p.h new file mode 100644 index 0000000000..257fa40aa8 --- /dev/null +++ b/src/declarative/items/qquickshadereffect_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHADEREFFECT_P_H +#define QQUICKSHADEREFFECT_P_H + +#include "qquickitem.h" + +#include "qsgmaterial.h" +#include +#include +#include "qquickshadereffectmesh_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +const char *qtPositionAttributeName(); +const char *qtTexCoordAttributeName(); + +class QSGContext; +class QSignalMapper; +class QQuickCustomMaterialShader; + +class QQuickShaderEffect : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) + Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) + Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged) + Q_PROPERTY(QVariant mesh READ mesh WRITE setMesh NOTIFY meshChanged) + Q_PROPERTY(CullMode culling READ cullMode WRITE setCullMode NOTIFY cullModeChanged) + Q_ENUMS(CullMode) + +public: + enum CullMode + { + NoCulling = QQuickShaderEffectMaterial::NoCulling, + BackFaceCulling = QQuickShaderEffectMaterial::BackFaceCulling, + FrontFaceCulling = QQuickShaderEffectMaterial::FrontFaceCulling + }; + + QQuickShaderEffect(QQuickItem *parent = 0); + ~QQuickShaderEffect(); + + virtual void componentComplete(); + + QByteArray fragmentShader() const { return m_source.fragmentCode; } + void setFragmentShader(const QByteArray &code); + + QByteArray vertexShader() const { return m_source.vertexCode; } + void setVertexShader(const QByteArray &code); + + bool blending() const { return m_blending; } + void setBlending(bool enable); + + QVariant mesh() const; + void setMesh(const QVariant &mesh); + + CullMode cullMode() const { return m_cullMode; } + void setCullMode(CullMode face); + +Q_SIGNALS: + void fragmentShaderChanged(); + void vertexShaderChanged(); + void blendingChanged(); + void meshChanged(); + void cullModeChanged(); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private Q_SLOTS: + void changeSource(int index); + void updateData(); + void updateGeometry(); + +private: + friend class QQuickCustomMaterialShader; + friend class QQuickShaderEffectNode; + + void setSource(const QVariant &var, int index); + void disconnectPropertySignals(); + void connectPropertySignals(); + void reset(); + void updateProperties(); + void lookThroughShaderCode(const QByteArray &code); + + QQuickShaderEffectProgram m_source; + QSize m_meshResolution; + QQuickShaderEffectMesh *m_mesh; + QQuickGridMesh m_defaultMesh; + CullMode m_cullMode; + + struct SourceData + { + QSignalMapper *mapper; + QPointer sourceObject; + QByteArray name; + }; + QVector m_sources; + + uint m_blending : 1; + uint m_dirtyData : 1; + + uint m_programDirty : 1; + uint m_dirtyMesh : 1; + uint m_dirtyGeometry : 1; +}; + +// TODO: Remove after grace period. +class QQuickShaderEffectItem : public QQuickShaderEffect +{ +public: + QQuickShaderEffectItem(QQuickItem *parent = 0); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKSHADEREFFECT_P_H diff --git a/src/declarative/items/qquickshadereffectmesh.cpp b/src/declarative/items/qquickshadereffectmesh.cpp new file mode 100644 index 0000000000..79b37d40bd --- /dev/null +++ b/src/declarative/items/qquickshadereffectmesh.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshadereffectmesh_p.h" +#include "qsggeometry.h" +#include "qquickshadereffect_p.h" + +QT_BEGIN_NAMESPACE + +QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent) + : QObject(parent) +{ +} + +/*! + \qmlclass GridMesh QQuickGridMesh + \inqmlmodule QtQuick 2 + \ingroup qml-utility-elements + \brief GridMesh defines a mesh with vertices arranged in a grid. + + GridMesh defines a rectangular mesh consisting of vertices arranged in an + evenly spaced grid. It is used to generate \l{QSGGeometry}{geometry}. + The grid resolution is specified with the \l resolution property. +*/ + +QQuickGridMesh::QQuickGridMesh(QObject *parent) + : QQuickShaderEffectMesh(parent) + , m_resolution(1, 1) +{ + connect(this, SIGNAL(resolutionChanged()), this, SIGNAL(geometryChanged())); +} + +QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &dstRect) const +{ + int vmesh = m_resolution.height(); + int hmesh = m_resolution.width(); + int attrCount = attributes.count(); + + if (!geometry) { + bool error = true; + switch (attrCount) { + case 0: + qWarning("QQuickGridMesh:: No attributes specified."); + break; + case 1: + if (attributes.at(0) == qtPositionAttributeName()) { + error = false; + break; + } + qWarning("QQuickGridMesh:: Missing \'%s\' attribute.", + qtPositionAttributeName()); + break; + case 2: + if (attributes.contains(qtPositionAttributeName()) + && attributes.contains(qtTexCoordAttributeName())) + { + error = false; + break; + } + qWarning("QQuickGridMesh:: Missing \'%s\' or \'%s\' attribute.", + qtPositionAttributeName(), qtTexCoordAttributeName()); + break; + default: + qWarning("QQuickGridMesh:: Too many attributes specified."); + break; + } + + geometry = new QSGGeometry(attrCount == 1 + ? QSGGeometry::defaultAttributes_Point2D() + : QSGGeometry::defaultAttributes_TexturedPoint2D(), + (vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2), + GL_UNSIGNED_SHORT); + + } else { + geometry->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2)); + } + + QSGGeometry::Point2D *vdata = static_cast(geometry->vertexData()); + + bool positionFirst = attributes.at(0) == qtPositionAttributeName(); + + QRectF srcRect(0, 0, 1, 1); + for (int iy = 0; iy <= vmesh; ++iy) { + float fy = iy / float(vmesh); + float y = float(dstRect.top()) + fy * float(dstRect.height()); + float ty = float(srcRect.top()) + fy * float(srcRect.height()); + for (int ix = 0; ix <= hmesh; ++ix) { + float fx = ix / float(hmesh); + for (int ia = 0; ia < attrCount; ++ia) { + if (positionFirst == (ia == 0)) { + vdata->x = float(dstRect.left()) + fx * float(dstRect.width()); + vdata->y = y; + ++vdata; + } else { + vdata->x = float(srcRect.left()) + fx * float(srcRect.width()); + vdata->y = ty; + ++vdata; + } + } + } + } + + quint16 *indices = (quint16 *)geometry->indexDataAsUShort(); + int i = 0; + for (int iy = 0; iy < vmesh; ++iy) { + *(indices++) = i + hmesh + 1; + for (int ix = 0; ix <= hmesh; ++ix, ++i) { + *(indices++) = i + hmesh + 1; + *(indices++) = i; + } + *(indices++) = i - 1; + } + + return geometry; +} + +/*! + \qmlproperty size QtQuick2::GridMesh::resolution + + This property holds the grid resolution. The resolution's width and height + specify the number of cells or spacings between vertices horizontally and + vertically respectively. The minimum and default is 1x1, which corresponds + to four vertices in total, one in each corner. + For non-linear vertex transformations, you probably want to set the + resolution higher. + + \row + \o \image declarative-gridmesh.png + \o \qml + import QtQuick 2.0 + + ShaderEffect { + width: 200 + height: 200 + mesh: GridMesh { + resolution: Qt.size(20, 20) + } + property variant source: Image { + source: "qt-logo.png" + sourceSize { width: 200; height: 200 } + smooth: true + } + vertexShader: " + uniform highp mat4 qt_Matrix; + attribute highp vec4 qt_Vertex; + attribute highp vec2 qt_MultiTexCoord0; + varying highp vec2 qt_TexCoord0; + uniform highp float width; + void main() { + highp vec4 pos = qt_Vertex; + highp float d = .5 * smoothstep(0., 1., qt_MultiTexCoord0.y); + pos.x = width * mix(d, 1.0 - d, qt_MultiTexCoord0.x); + gl_Position = qt_Matrix * pos; + qt_TexCoord0 = qt_MultiTexCoord0; + }" + } + \endqml + \endrow +*/ + +void QQuickGridMesh::setResolution(const QSize &res) +{ + if (res == m_resolution) + return; + if (res.width() < 1 || res.height() < 1) { + return; + } + m_resolution = res; + emit resolutionChanged(); +} + +QSize QQuickGridMesh::resolution() const +{ + return m_resolution; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickshadereffectmesh_p.h b/src/declarative/items/qquickshadereffectmesh_p.h new file mode 100644 index 0000000000..40549f73a8 --- /dev/null +++ b/src/declarative/items/qquickshadereffectmesh_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeparserstatus.h" + +#include +#include +#include +#include +#include + +#ifndef QQUICKSHADEREFFECTMESH_P_H +#define QQUICKSHADEREFFECTMESH_P_H + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGGeometry; +class QRectF; + +class Q_DECLARATIVE_EXPORT QQuickShaderEffectMesh : public QObject +{ + Q_OBJECT +public: + QQuickShaderEffectMesh(QObject *parent = 0); + // If 'geometry' != 0, 'attributes' is the same as last time the function was called. + virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &rect) const = 0; + +Q_SIGNALS: + // Emitted when the geometry needs to be updated. + void geometryChanged(); +}; + +class QQuickGridMesh : public QQuickShaderEffectMesh +{ + Q_OBJECT + Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged) +public: + QQuickGridMesh(QObject *parent = 0); + virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &rect) const; + + void setResolution(const QSize &res); + QSize resolution() const; + +Q_SIGNALS: + void resolutionChanged(); + +private: + QSize m_resolution; +}; + +inline QColor qt_premultiply_color(const QColor &c) +{ + return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF()); +} + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKSHADEREFFECTMESH_P_H diff --git a/src/declarative/items/qquickshadereffectnode.cpp b/src/declarative/items/qquickshadereffectnode.cpp new file mode 100644 index 0000000000..67ca124c91 --- /dev/null +++ b/src/declarative/items/qquickshadereffectnode.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qquickshadereffectmesh_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickCustomMaterialShader : public QSGMaterialShader +{ +public: + QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector &attributes); + virtual void deactivate(); + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + virtual char const *const *attributeNames() const; + +protected: + friend class QQuickShaderEffectNode; + + virtual void initialize(); + virtual const char *vertexShader() const; + virtual const char *fragmentShader() const; + + const QQuickShaderEffectMaterialKey m_key; + QVector m_attributeNames; + const QVector m_attributes; + + QVector m_uniformLocs; + int m_opacityLoc; + int m_matrixLoc; + uint m_textureIndicesSet; +}; + +QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector &attributes) + : m_key(key) + , m_attributes(attributes) + , m_textureIndicesSet(false) +{ + for (int i = 0; i < attributes.count(); ++i) + m_attributeNames.append(attributes.at(i).constData()); + m_attributeNames.append(0); +} + +void QQuickCustomMaterialShader::deactivate() +{ + QSGMaterialShader::deactivate(); + glDisable(GL_CULL_FACE); +} + +void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(newEffect != 0); + + const QQuickShaderEffectMaterial *material = static_cast(newEffect); + + if (!m_textureIndicesSet) { + for (int i = 0; i < material->m_textures.size(); ++i) + program()->setUniformValue(material->m_textures.at(i).first.constData(), i); + m_textureIndicesSet = true; + } + + if (m_uniformLocs.size() != material->m_uniformValues.size()) { + m_uniformLocs.reserve(material->m_uniformValues.size()); + for (int i = 0; i < material->m_uniformValues.size(); ++i) { + const QByteArray &name = material->m_uniformValues.at(i).first; + m_uniformLocs.append(program()->uniformLocation(name.constData())); + } + } + + QOpenGLFunctions *functions = state.context()->functions(); + for (int i = material->m_textures.size() - 1; i >= 0; --i) { + functions->glActiveTexture(GL_TEXTURE0 + i); + if (QSGTextureProvider *provider = material->m_textures.at(i).second) { + if (QSGTexture *texture = provider->texture()) { + texture->bind(); + continue; + } + } + qWarning("ShaderEffectItem: source or provider missing when binding textures"); + glBindTexture(GL_TEXTURE_2D, 0); + } + + if (material->m_source.respectsOpacity) + program()->setUniformValue(m_opacityLoc, state.opacity()); + + for (int i = 0; i < material->m_uniformValues.count(); ++i) { + const QVariant &v = material->m_uniformValues.at(i).second; + + switch (v.type()) { + case QVariant::Color: + program()->setUniformValue(m_uniformLocs.at(i), qt_premultiply_color(qvariant_cast(v))); + break; + case QVariant::Double: + program()->setUniformValue(m_uniformLocs.at(i), (float) qvariant_cast(v)); + break; + case QVariant::Transform: + program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast(v)); + break; + case QVariant::Int: + program()->setUniformValue(m_uniformLocs.at(i), v.toInt()); + break; + case QVariant::Bool: + program()->setUniformValue(m_uniformLocs.at(i), GLint(v.toBool())); + break; + case QVariant::Size: + case QVariant::SizeF: + program()->setUniformValue(m_uniformLocs.at(i), v.toSizeF()); + break; + case QVariant::Point: + case QVariant::PointF: + program()->setUniformValue(m_uniformLocs.at(i), v.toPointF()); + break; + case QVariant::Rect: + case QVariant::RectF: + { + QRectF r = v.toRectF(); + program()->setUniformValue(m_uniformLocs.at(i), r.x(), r.y(), r.width(), r.height()); + } + break; + case QVariant::Vector3D: + program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast(v)); + break; + default: + break; + } + } + + const QQuickShaderEffectMaterial *oldMaterial = static_cast(oldEffect); + if (oldEffect == 0 || material->cullMode() != oldMaterial->cullMode()) { + switch (material->cullMode()) { + case QQuickShaderEffectMaterial::FrontFaceCulling: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + break; + case QQuickShaderEffectMaterial::BackFaceCulling: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + break; + default: + glDisable(GL_CULL_FACE); + break; + } + } + + if ((state.isMatrixDirty()) && material->m_source.respectsMatrix) + program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); +} + +char const *const *QQuickCustomMaterialShader::attributeNames() const +{ + return m_attributeNames.constData(); +} + +void QQuickCustomMaterialShader::initialize() +{ + m_opacityLoc = program()->uniformLocation("qt_Opacity"); + m_matrixLoc = program()->uniformLocation("qt_Matrix"); + // TODO: Remove after grace period. + if (m_matrixLoc == -1) + m_matrixLoc = program()->uniformLocation("qt_ModelViewProjectionMatrix"); +} + +const char *QQuickCustomMaterialShader::vertexShader() const +{ + return m_key.vertexCode.constData(); +} + +const char *QQuickCustomMaterialShader::fragmentShader() const +{ + return m_key.fragmentCode.constData(); +} + + +bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const +{ + return vertexCode == other.vertexCode && fragmentCode == other.fragmentCode && className == other.className; +} + +uint qHash(const QQuickShaderEffectMaterialKey &key) +{ + return qHash(qMakePair(qMakePair(key.vertexCode, key.fragmentCode), key.className)); +} + + +QHash > QQuickShaderEffectMaterial::materialMap; + +QQuickShaderEffectMaterial::QQuickShaderEffectMaterial() + : m_cullMode(NoCulling) +{ + setFlag(Blending, true); +} + +QSGMaterialType *QQuickShaderEffectMaterial::type() const +{ + return m_type.data(); +} + +QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const +{ + return new QQuickCustomMaterialShader(m_source, m_source.attributeNames); +} + +int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const +{ + return this - static_cast(other); +} + +void QQuickShaderEffectMaterial::setCullMode(QQuickShaderEffectMaterial::CullMode face) +{ + m_cullMode = face; +} + +QQuickShaderEffectMaterial::CullMode QQuickShaderEffectMaterial::cullMode() const +{ + return m_cullMode; +} + +void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectProgram &source) +{ + m_source = source; + m_type = materialMap.value(m_source); + if (m_type.isNull()) { + m_type = QSharedPointer(new QSGMaterialType); + materialMap.insert(m_source, m_type); + } +} + +void QQuickShaderEffectMaterial::setUniforms(const QVector > &uniformValues) +{ + m_uniformValues = uniformValues; +} + +void QQuickShaderEffectMaterial::setTextureProviders(const QVector > &textures) +{ + m_textures = textures; +} + +const QVector > &QQuickShaderEffectMaterial::textureProviders() const +{ + return m_textures; +} + +void QQuickShaderEffectMaterial::updateTextures() const +{ + for (int i = 0; i < m_textures.size(); ++i) { + if (QSGTextureProvider *provider = m_textures.at(i).second) { + if (QSGDynamicTexture *texture = qobject_cast(provider->texture())) + texture->updateTexture(); + } + } +} + + +QQuickShaderEffectNode::QQuickShaderEffectNode() +{ + QSGNode::setFlag(UsePreprocess, true); + setMaterial(&m_material); +} + +QQuickShaderEffectNode::~QQuickShaderEffectNode() +{ +} + +void QQuickShaderEffectNode::markDirtyTexture() +{ + markDirty(DirtyMaterial); +} + +void QQuickShaderEffectNode::preprocess() +{ + Q_ASSERT(material()); + static_cast(material())->updateTextures(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickshadereffectnode_p.h b/src/declarative/items/qquickshadereffectnode_p.h new file mode 100644 index 0000000000..50213ff396 --- /dev/null +++ b/src/declarative/items/qquickshadereffectnode_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHADEREFFECTNODE_P_H +#define QQUICKSHADEREFFECTNODE_P_H + +#include "qsgnode.h" +#include "qsgmaterial.h" +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +struct QQuickShaderEffectMaterialKey { + QByteArray vertexCode; + QByteArray fragmentCode; + const char *className; + + bool operator == (const QQuickShaderEffectMaterialKey &other) const; +}; + +uint qHash(const QQuickShaderEffectMaterialKey &key); + +// TODO: Implement support for multisampling. +struct QQuickShaderEffectProgram : public QQuickShaderEffectMaterialKey +{ + QQuickShaderEffectProgram() : respectsOpacity(false), respectsMatrix(false) {} + + QVector attributeNames; + QSet uniformNames; + + uint respectsOpacity : 1; + uint respectsMatrix : 1; +}; + + +class QQuickCustomMaterialShader; +class QQuickShaderEffectMaterial : public QSGMaterial +{ +public: + enum CullMode + { + NoCulling, + BackFaceCulling, + FrontFaceCulling + }; + + QQuickShaderEffectMaterial(); + virtual QSGMaterialType *type() const; + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const; + + void setCullMode(CullMode face); + CullMode cullMode() const; + + void setProgramSource(const QQuickShaderEffectProgram &); + void setUniforms(const QVector > &uniformValues); + void setTextureProviders(const QVector > &textures); + const QVector > &textureProviders() const; + void updateTextures() const; + +protected: + friend class QQuickCustomMaterialShader; + + // The type pointer needs to be unique. It is not safe to let the type object be part of the + // QQuickShaderEffectMaterial, since it can be deleted and a new one constructed on top of the old + // one. The new QQuickShaderEffectMaterial would then get the same type pointer as the old one, and + // CustomMaterialShaders based on the old one would incorrectly be used together with the new + // one. To guarantee that the type pointer is unique, the type object must live as long as + // there are any CustomMaterialShaders of that type. + QSharedPointer m_type; + + QQuickShaderEffectProgram m_source; + QVector > m_uniformValues; + QVector > m_textures; + CullMode m_cullMode; + + static QHash > materialMap; +}; + + +class QSGShaderEffectMesh; + +class QQuickShaderEffectNode : public QObject, public QSGGeometryNode +{ + Q_OBJECT +public: + QQuickShaderEffectNode(); + virtual ~QQuickShaderEffectNode(); + + virtual void preprocess(); + + QQuickShaderEffectMaterial *shaderMaterial() { return &m_material; } + +private Q_SLOTS: + void markDirtyTexture(); + +private: + QQuickShaderEffectMaterial m_material; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKSHADEREFFECTNODE_P_H diff --git a/src/declarative/items/qquickshadereffectsource.cpp b/src/declarative/items/qquickshadereffectsource.cpp new file mode 100644 index 0000000000..587f7bad98 --- /dev/null +++ b/src/declarative/items/qquickshadereffectsource.cpp @@ -0,0 +1,905 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshadereffectsource_p.h" + +#include "qquickitem_p.h" +#include "qquickcanvas_p.h" +#include +#include + +#include "qopenglframebufferobject.h" +#include "qmath.h" +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) + +class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider +{ + Q_OBJECT +public: + QQuickShaderEffectSourceTextureProvider() + : sourceTexture(0) + { + } + + QSGTexture *texture() const { + sourceTexture->setMipmapFiltering(mipmapFiltering); + sourceTexture->setFiltering(filtering); + sourceTexture->setHorizontalWrapMode(horizontalWrap); + sourceTexture->setVerticalWrapMode(verticalWrap); + return sourceTexture; + } + + QQuickShaderEffectTexture *sourceTexture; + + QSGTexture::Filtering mipmapFiltering; + QSGTexture::Filtering filtering; + QSGTexture::WrapMode horizontalWrap; + QSGTexture::WrapMode verticalWrap; +}; +#include "qquickshadereffectsource.moc" + + +QQuickShaderEffectSourceNode::QQuickShaderEffectSourceNode() +{ + setFlag(UsePreprocess, true); +} + +void QQuickShaderEffectSourceNode::markDirtyTexture() +{ + markDirty(DirtyMaterial); +} + + +QQuickShaderEffectTexture::QQuickShaderEffectTexture(QQuickItem *shaderSource) + : QSGDynamicTexture() + , m_item(0) + , m_format(GL_RGBA) + , m_shaderSource(shaderSource) + , m_renderer(0) + , m_fbo(0) + , m_secondaryFbo(0) +#ifdef QSG_DEBUG_FBO_OVERLAY + , m_debugOverlay(0) +#endif + , m_context(QQuickItemPrivate::get(shaderSource)->sceneGraphContext()) + , m_mipmap(false) + , m_live(true) + , m_recursive(false) + , m_dirtyTexture(true) + , m_multisamplingSupportChecked(false) + , m_multisampling(false) + , m_grab(false) +{ +} + +QQuickShaderEffectTexture::~QQuickShaderEffectTexture() +{ + delete m_renderer; + delete m_fbo; + delete m_secondaryFbo; +#ifdef QSG_DEBUG_FBO_OVERLAY + delete m_debugOverlay; +#endif +} + +int QQuickShaderEffectTexture::textureId() const +{ + return m_fbo ? m_fbo->texture() : 0; +} + +bool QQuickShaderEffectTexture::hasAlphaChannel() const +{ + return m_format != GL_RGB; +} + +bool QQuickShaderEffectTexture::hasMipmaps() const +{ + return m_mipmap; +} + + +void QQuickShaderEffectTexture::bind() +{ +#ifndef QT_NO_DEBUG + if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound())) + qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively."); +#endif + glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0); + updateBindOptions(); +} + +bool QQuickShaderEffectTexture::updateTexture() +{ + if ((m_live || m_grab) && m_dirtyTexture) { + grab(); + m_grab = false; + return true; + } + return false; +} + +void QQuickShaderEffectTexture::setHasMipmaps(bool mipmap) +{ + if (mipmap == m_mipmap) + return; + m_mipmap = mipmap; + if (m_mipmap && m_fbo && !m_fbo->format().mipmap()) + markDirtyTexture(); +} + + +void QQuickShaderEffectTexture::setItem(QSGNode *item) +{ + if (item == m_item) + return; + m_item = item; + markDirtyTexture(); +} + +void QQuickShaderEffectTexture::setRect(const QRectF &rect) +{ + if (rect == m_rect) + return; + m_rect = rect; + markDirtyTexture(); +} + +void QQuickShaderEffectTexture::setSize(const QSize &size) +{ + if (size == m_size) + return; + m_size = size; + markDirtyTexture(); +} + +void QQuickShaderEffectTexture::setFormat(GLenum format) +{ + if (format == m_format) + return; + m_format = format; + markDirtyTexture(); +} + +void QQuickShaderEffectTexture::setLive(bool live) +{ + if (live == m_live) + return; + m_live = live; + markDirtyTexture(); +} + +void QQuickShaderEffectTexture::scheduleUpdate() +{ + if (m_grab) + return; + m_grab = true; + if (m_dirtyTexture) + emit textureChanged(); +} + +void QQuickShaderEffectTexture::setRecursive(bool recursive) +{ + m_recursive = recursive; +} + +void QQuickShaderEffectTexture::markDirtyTexture() +{ + m_dirtyTexture = true; + if (m_live || m_grab) + emit textureChanged(); +} + +void QQuickShaderEffectTexture::grab() +{ + if (!m_item || m_size.isNull()) { + delete m_fbo; + delete m_secondaryFbo; + m_fbo = m_secondaryFbo = 0; + m_dirtyTexture = false; + return; + } + QSGNode *root = m_item; + while (root->firstChild() && root->type() != QSGNode::RootNodeType) + root = root->firstChild(); + if (root->type() != QSGNode::RootNodeType) + return; + + if (m_size.isEmpty()) { + delete m_fbo; + delete m_secondaryFbo; + m_secondaryFbo = m_fbo = 0; + return; + } + + if (!m_renderer) { + m_renderer = m_context->createRenderer(); + connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()), Qt::DirectConnection); + } + m_renderer->setRootNode(static_cast(root)); + + bool deleteFboLater = false; + if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format + || (!m_fbo->format().mipmap() && m_mipmap)) + { + if (!m_multisamplingSupportChecked) { + QList extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' '); + m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample") + && extensions.contains("GL_EXT_framebuffer_blit"); + m_multisamplingSupportChecked = true; + } + if (m_multisampling) { + // Don't delete the FBO right away in case it is used recursively. + deleteFboLater = true; + delete m_secondaryFbo; + QOpenGLFramebufferObjectFormat format; + + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setInternalTextureFormat(m_format); + format.setSamples(8); + m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); + } else { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setInternalTextureFormat(m_format); + format.setMipmap(m_mipmap); + if (m_recursive) { + deleteFboLater = true; + delete m_secondaryFbo; + m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); + glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); + updateBindOptions(true); + } else { + delete m_fbo; + delete m_secondaryFbo; + m_fbo = new QOpenGLFramebufferObject(m_size, format); + m_secondaryFbo = 0; + glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + updateBindOptions(true); + } + } + } + + if (m_recursive && !m_secondaryFbo) { + // m_fbo already created, m_recursive was just set. + Q_ASSERT(m_fbo); + Q_ASSERT(!m_multisampling); + + m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format()); + glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); + updateBindOptions(true); + } + + // Render texture. + root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update. + m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update. + +#ifdef QSG_DEBUG_FBO_OVERLAY + if (qmlFboOverlay()) { + if (!m_debugOverlay) + m_debugOverlay = m_context->createRectangleNode(); + m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height())); + m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40)); + m_debugOverlay->setPenColor(QColor()); + m_debugOverlay->setPenWidth(0); + m_debugOverlay->setRadius(0); + m_debugOverlay->update(); + root->appendChildNode(m_debugOverlay); + } +#endif + + m_dirtyTexture = false; + + QOpenGLContext *ctx = m_context->glContext(); + m_renderer->setDeviceRect(m_size); + m_renderer->setViewportRect(m_size); + QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height()); + m_renderer->setProjectionMatrixToRect(mirrored); + m_renderer->setClearColor(Qt::transparent); + + if (m_multisampling) { + m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); + + if (deleteFboLater) { + delete m_fbo; + QOpenGLFramebufferObjectFormat format; + format.setInternalTextureFormat(m_format); + format.setAttachment(QOpenGLFramebufferObject::NoAttachment); + format.setMipmap(m_mipmap); + format.setSamples(0); + m_fbo = new QOpenGLFramebufferObject(m_size, format); + glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + updateBindOptions(true); + } + + QRect r(QPoint(), m_size); + QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r); + } else { + if (m_recursive) { + m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); + + if (deleteFboLater) { + delete m_fbo; + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setInternalTextureFormat(m_format); + format.setMipmap(m_mipmap); + m_fbo = new QOpenGLFramebufferObject(m_size, format); + glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + updateBindOptions(true); + } + qSwap(m_fbo, m_secondaryFbo); + } else { + m_renderer->renderScene(QSGBindableFbo(m_fbo)); + } + } + + if (m_mipmap) { + glBindTexture(GL_TEXTURE_2D, textureId()); + ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); + } + + root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update. + +#ifdef QSG_DEBUG_FBO_OVERLAY + if (qmlFboOverlay()) + root->removeChildNode(m_debugOverlay); +#endif + if (m_recursive) + markDirtyTexture(); // Continuously update if 'live' and 'recursive'. +} + +QImage QQuickShaderEffectTexture::toImage() const +{ + if (m_fbo) + return m_fbo->toImage(); + + return QImage(); +} + +/*! + \qmlclass ShaderEffectSource QQuickShaderEffectSource + \since 5.0 + \ingroup qml-basic-visual-elements + \brief The ShaderEffectSource element renders a QML element into a texture + and displays it. + \inherits Item + + The ShaderEffectSource element renders \l sourceItem into a texture and + displays it in the scene. \l sourceItem is drawn into the texture as though + it was a fully opaque root element. Thus \l sourceItem itself can be + invisible, but still appear in the texture. + + ShaderEffectSource can be used as: + \list + \o a texture source in a \l ShaderEffect. + This allows you to apply custom shader effects to any QML element. + \o a cache for a complex element. + The complex element can be rendered once into the texture, which can + then be animated freely without the need to render the complex element + again every frame. + \o an opacity layer. + ShaderEffectSource allows you to apply an opacity to elements as a group + rather than each element individually. + \endlist + + \table + \row + \o \image declarative-shadereffectsource.png + \o \qml + import QtQuick 2.0 + + Rectangle { + width: 200 + height: 100 + gradient: Gradient { + GradientStop { position: 0; color: "white" } + GradientStop { position: 1; color: "black" } + } + Row { + opacity: 0.5 + Item { + id: foo + width: 100; height: 100 + Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" } + Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" } + Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" } + } + ShaderEffectSource { + width: 100; height: 100 + sourceItem: foo + } + } + } + \endqml + \endrow + \endtable + + The ShaderEffectSource element does not redirect any mouse or keyboard + input to \l sourceItem. If you hide the \l sourceItem by setting + \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero, + it will no longer react to input. In cases where the ShaderEffectSource is + meant to replace the \l sourceItem, you typically want to hide the + \l sourceItem while still handling input. For this, you can use + the \l hideSource property. + + \note If \l sourceItem is a \l Rectangle with border, by default half the + border width falls outside the texture. To get the whole border, you can + extend the \l sourceRect. + + \warning In most cases, using a ShaderEffectSource will decrease + performance, and in all cases, it will increase video memory usage. + Rendering through a ShaderEffectSource might also lead to lower quality + since some OpenGL implementations support multisampled backbuffer, + but not multisampled framebuffer objects. +*/ + +QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent) + : QQuickItem(parent) + , m_provider(0) + , m_texture(0) + , m_wrapMode(ClampToEdge) + , m_sourceItem(0) + , m_textureSize(0, 0) + , m_format(RGBA) + , m_live(true) + , m_hideSource(false) + , m_mipmap(false) + , m_recursive(false) + , m_grab(true) +{ + setFlag(ItemHasContents); +} + +QQuickShaderEffectSource::~QQuickShaderEffectSource() +{ + if (m_texture) + m_texture->deleteLater(); + + if (m_provider) + m_provider->deleteLater(); + + if (m_sourceItem) + QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource); +} + +void QQuickShaderEffectSource::ensureTexture() +{ + if (m_texture) + return; + + Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas + && QQuickItemPrivate::get(this)->sceneGraphContext() + && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(), + "QQuickShaderEffectSource::ensureTexture", + "Cannot be used outside the rendering thread"); + + m_texture = new QQuickShaderEffectTexture(this); + connect(m_texture, SIGNAL(textureChanged()), this, SLOT(update())); +} + +QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const +{ + if (!m_provider) { + // Make sure it gets thread affinity on the rendering thread so deletion works properly.. + Q_ASSERT_X(QQuickItemPrivate::get(this)->canvas + && QQuickItemPrivate::get(this)->sceneGraphContext() + && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphContext()->thread(), + "QQuickShaderEffectSource::textureProvider", + "Cannot be used outside the rendering thread"); + const_cast(this)->m_provider = new QQuickShaderEffectSourceTextureProvider(); + + const_cast(this)->ensureTexture(); + connect(m_texture, SIGNAL(textureChanged()), m_provider, SIGNAL(textureChanged()), Qt::DirectConnection); + m_provider->sourceTexture = m_texture; + } + return m_provider; +} + +/*! + \qmlproperty enumeration ShaderEffectSource::wrapMode + + This property defines the OpenGL wrap modes associated with the texture. + Modifying this property makes most sense when the element is used as a + source texture of a \l ShaderEffect. + + \list + \o ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically + \o ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically + \o ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically + \o ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically + \endlist + + \note Some OpenGL ES 2 implementations do not support the GL_REPEAT + wrap mode with non-power-of-two textures. +*/ + +QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const +{ + return m_wrapMode; +} + +void QQuickShaderEffectSource::setWrapMode(WrapMode mode) +{ + if (mode == m_wrapMode) + return; + m_wrapMode = mode; + update(); + emit wrapModeChanged(); +} + +/*! + \qmlproperty Item ShaderEffectSource::sourceItem + + This property holds the element to be rendered into the texture. +*/ + +QQuickItem *QQuickShaderEffectSource::sourceItem() const +{ + return m_sourceItem; +} + +void QQuickShaderEffectSource::setSourceItem(QQuickItem *item) +{ + if (item == m_sourceItem) + return; + if (m_sourceItem) + QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource); + m_sourceItem = item; + if (m_sourceItem) { + // TODO: Find better solution. + // 'm_sourceItem' needs a canvas to get a scenegraph node. + // The easiest way to make sure it gets a canvas is to + // make it a part of the same item tree as 'this'. + if (m_sourceItem->parentItem() == 0) { + m_sourceItem->setParentItem(this); + m_sourceItem->setVisible(false); + } + QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(m_hideSource); + } + update(); + emit sourceItemChanged(); +} + +/*! + \qmlproperty rect ShaderEffectSource::sourceRect + + This property defines which rectangular area of the \l sourceItem to + render into the texture. The source rectangle can be larger than + \l sourceItem itself. If the rectangle is null, which is the default, + the whole \l sourceItem is rendered to texture. +*/ + +QRectF QQuickShaderEffectSource::sourceRect() const +{ + return m_sourceRect; +} + +void QQuickShaderEffectSource::setSourceRect(const QRectF &rect) +{ + if (rect == m_sourceRect) + return; + m_sourceRect = rect; + update(); + emit sourceRectChanged(); +} + +/*! + \qmlproperty size ShaderEffectSource::textureSize + + This property holds the requested size of the texture. If it is empty, + which is the default, the size of the source rectangle is used. + + \note Some platforms have a limit on how small framebuffer objects can be, + which means the actual texture size might be larger than the requested + size. +*/ + +QSize QQuickShaderEffectSource::textureSize() const +{ + return m_textureSize; +} + +void QQuickShaderEffectSource::setTextureSize(const QSize &size) +{ + if (size == m_textureSize) + return; + m_textureSize = size; + update(); + emit textureSizeChanged(); +} + +/*! + \qmlproperty enumeration ShaderEffectSource::format + + This property defines the internal OpenGL format of the texture. + Modifying this property makes most sense when the element is used as a + source texture of a \l ShaderEffect. Depending on the OpenGL + implementation, this property might allow you to save some texture memory. + + \list + \o ShaderEffectSource.Alpha - GL_ALPHA + \o ShaderEffectSource.RGB - GL_RGB + \o ShaderEffectSource.RGBA - GL_RGBA + \endlist + + \note Some OpenGL implementations do not support the GL_ALPHA format. +*/ + +QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const +{ + return m_format; +} + +void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format) +{ + if (format == m_format) + return; + m_format = format; + update(); + emit formatChanged(); +} + +/*! + \qmlproperty bool ShaderEffectSource::live + + If this property is true, the texture is updated whenever the + \l sourceItem changes. Otherwise, it will be a frozen image of the + \l sourceItem. The property is true by default. +*/ + +bool QQuickShaderEffectSource::live() const +{ + return m_live; +} + +void QQuickShaderEffectSource::setLive(bool live) +{ + if (live == m_live) + return; + m_live = live; + update(); + emit liveChanged(); +} + +/*! + \qmlproperty bool ShaderEffectSource::hideSource + + If this property is true, the \l sourceItem is hidden, though it will still + be rendered into the texture. As opposed to hiding the \l sourceItem by + setting \l{Item::visible}{visible} to false, setting this property to true + will not prevent mouse or keyboard input from reaching \l sourceItem. + The property is useful when the ShaderEffectSource is anchored on top of, + and meant to replace the \l sourceItem. +*/ + +bool QQuickShaderEffectSource::hideSource() const +{ + return m_hideSource; +} + +void QQuickShaderEffectSource::setHideSource(bool hide) +{ + if (hide == m_hideSource) + return; + if (m_sourceItem) { + QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide); + QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource); + } + m_hideSource = hide; + update(); + emit hideSourceChanged(); +} + +/*! + \qmlproperty bool ShaderEffectSource::mipmap + + If this property is true, mipmaps are generated for the texture. + + \note Some OpenGL ES 2 implementations do not support mipmapping of + non-power-of-two textures. +*/ + +bool QQuickShaderEffectSource::mipmap() const +{ + return m_mipmap; +} + +void QQuickShaderEffectSource::setMipmap(bool enabled) +{ + if (enabled == m_mipmap) + return; + m_mipmap = enabled; + update(); + emit mipmapChanged(); +} + +/*! + \qmlproperty bool ShaderEffectSource::recursive + + Set this property to true if the ShaderEffectSource has a dependency on + itself. ShaderEffectSources form a dependency chain, where one + ShaderEffectSource can be part of the \l sourceItem of another. + If there is a loop in this chain, a ShaderEffectSource could end up trying + to render into the same texture it is using as source, which is not allowed + by OpenGL. When this property is set to true, an extra texture is allocated + so that ShaderEffectSource can keep a copy of the texture from the previous + frame. It can then render into one texture and use the texture from the + previous frame as source. + + Setting both this property and \l live to true will cause the scene graph + to render continuously. Since the ShaderEffectSource depends on itself, + updating it means that it immediately becomes dirty again. +*/ + +bool QQuickShaderEffectSource::recursive() const +{ + return m_recursive; +} + +void QQuickShaderEffectSource::setRecursive(bool enabled) +{ + if (enabled == m_recursive) + return; + m_recursive = enabled; + emit recursiveChanged(); +} + +/*! + \qmlmethod ShaderEffectSource::scheduleUpdate() + + Schedules a re-rendering of the texture for the next frame. + Use this to update the texture when \l live is false. +*/ + +void QQuickShaderEffectSource::scheduleUpdate() +{ + if (m_grab) + return; + m_grab = true; + update(); +} + +static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap) +{ + switch (mode) { + case QQuickShaderEffectSource::RepeatHorizontally: + *hWrap = QSGTexture::Repeat; + *vWrap = QSGTexture::ClampToEdge; + break; + case QQuickShaderEffectSource::RepeatVertically: + *vWrap = QSGTexture::Repeat; + *hWrap = QSGTexture::ClampToEdge; + break; + case QQuickShaderEffectSource::Repeat: + *hWrap = *vWrap = QSGTexture::Repeat; + break; + default: + // QQuickShaderEffectSource::ClampToEdge + *hWrap = *vWrap = QSGTexture::ClampToEdge; + break; + } +} + + +QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) { + delete oldNode; + return 0; + } + + ensureTexture(); + + QQuickShaderEffectTexture *tex = qobject_cast(m_texture); + tex->setLive(m_live); + tex->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode()); + QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0 + ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height()) + : m_sourceRect; + tex->setRect(sourceRect); + QSize textureSize = m_textureSize.isEmpty() + ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height()))) + : m_textureSize; + Q_ASSERT(!textureSize.isEmpty()); + QQuickItemPrivate *d = static_cast(QObjectPrivate::get(this)); + const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize(); + // Keep power-of-two by doubling the size. + while (textureSize.width() < minTextureSize.width()) + textureSize.rwidth() *= 2; + while (textureSize.height() < minTextureSize.height()) + textureSize.rheight() *= 2; + + tex->setSize(textureSize); + tex->setRecursive(m_recursive); + tex->setFormat(GLenum(m_format)); + tex->setHasMipmaps(m_mipmap); + + if (m_grab) + tex->scheduleUpdate(); + m_grab = false; + + QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth + ? QSGTexture::Linear + : QSGTexture::Nearest; + QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None; + QSGTexture::WrapMode hWrap, vWrap; + get_wrap_mode(m_wrapMode, &hWrap, &vWrap); + + if (m_provider) { + m_provider->mipmapFiltering = mmFiltering; + m_provider->filtering = filtering; + m_provider->horizontalWrap = hWrap; + m_provider->verticalWrap = vWrap; + } + + // Don't create the paint node if we're not spanning any area + if (width() == 0 || height() == 0) { + delete oldNode; + return 0; + } + + QQuickShaderEffectSourceNode *node = static_cast(oldNode); + if (!node) { + node = new QQuickShaderEffectSourceNode; + node->setTexture(m_texture); + connect(m_texture, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); + } + + // If live and recursive, update continuously. + if (m_live && m_recursive) + node->markDirty(QSGNode::DirtyMaterial); + + node->setMipmapFiltering(mmFiltering); + node->setFiltering(filtering); + node->setHorizontalWrapMode(hWrap); + node->setVerticalWrapMode(vWrap); + node->setTargetRect(QRectF(0, 0, width(), height())); + node->setSourceRect(QRectF(0, 0, 1, 1)); + node->update(); + + return node; +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickshadereffectsource_p.h b/src/declarative/items/qquickshadereffectsource_p.h new file mode 100644 index 0000000000..8117c06b9c --- /dev/null +++ b/src/declarative/items/qquickshadereffectsource_p.h @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHADEREFFECTSOURCE_P_H +#define QQUICKSHADEREFFECTSOURCE_P_H + +#include "qquickitem.h" +#include +#include +#include +#include + +#include "qpointer.h" +#include "qsize.h" +#include "qrect.h" + +#define QSG_DEBUG_FBO_OVERLAY + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGNode; +class UpdatePaintNodeData; +class QOpenGLFramebufferObject; + +class QQuickShaderEffectSourceTextureProvider; + +class QQuickShaderEffectSourceNode : public QObject, public QSGDefaultImageNode +{ + Q_OBJECT + +public: + QQuickShaderEffectSourceNode(); + +private Q_SLOTS: + void markDirtyTexture(); +}; + +class Q_DECLARATIVE_EXPORT QQuickShaderEffectTexture : public QSGDynamicTexture +{ + Q_OBJECT +public: + QQuickShaderEffectTexture(QQuickItem *shaderSource); + ~QQuickShaderEffectTexture(); + + virtual bool updateTexture(); + + // The item's "paint node", not effect node. + QSGNode *item() const { return m_item; } + void setItem(QSGNode *item); + + QRectF rect() const { return m_rect; } + void setRect(const QRectF &rect); + + QSize size() const { return m_size; } + void setSize(const QSize &size); + + void setHasMipmaps(bool mipmap); + + void bind(); + + bool hasAlphaChannel() const; + bool hasMipmaps() const; + int textureId() const; + QSize textureSize() const { return m_size; } + + GLenum format() const { return m_format; } + void setFormat(GLenum format); + + bool live() const { return bool(m_live); } + void setLive(bool live); + + bool recursive() const { return bool(m_recursive); } + void setRecursive(bool recursive); + + void scheduleUpdate(); + + QImage toImage() const; + +Q_SIGNALS: + void textureChanged(); + +public Q_SLOTS: + void markDirtyTexture(); + +private: + void grab(); + + QSGNode *m_item; + QRectF m_rect; + QSize m_size; + GLenum m_format; + + QQuickItem *m_shaderSource; + QSGRenderer *m_renderer; + QOpenGLFramebufferObject *m_fbo; + QOpenGLFramebufferObject *m_secondaryFbo; + +#ifdef QSG_DEBUG_FBO_OVERLAY + QSGRectangleNode *m_debugOverlay; +#endif + + QSGContext *m_context; + + uint m_mipmap : 1; + uint m_live : 1; + uint m_recursive : 1; + uint m_dirtyTexture : 1; + uint m_multisamplingSupportChecked : 1; + uint m_multisampling : 1; + uint m_grab : 1; +}; + +class Q_DECLARATIVE_EXPORT QQuickShaderEffectSource : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) + Q_PROPERTY(QQuickItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged) + Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged) + Q_PROPERTY(QSize textureSize READ textureSize WRITE setTextureSize NOTIFY textureSizeChanged) + Q_PROPERTY(Format format READ format WRITE setFormat NOTIFY formatChanged) + Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged) + Q_PROPERTY(bool hideSource READ hideSource WRITE setHideSource NOTIFY hideSourceChanged) + Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged) + Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged) + + Q_ENUMS(Format WrapMode) +public: + enum WrapMode { + ClampToEdge, + RepeatHorizontally, + RepeatVertically, + Repeat + }; + + enum Format { + Alpha = GL_ALPHA, + RGB = GL_RGB, + RGBA = GL_RGBA + }; + + QQuickShaderEffectSource(QQuickItem *parent = 0); + ~QQuickShaderEffectSource(); + + WrapMode wrapMode() const; + void setWrapMode(WrapMode mode); + + QQuickItem *sourceItem() const; + void setSourceItem(QQuickItem *item); + + QRectF sourceRect() const; + void setSourceRect(const QRectF &rect); + + QSize textureSize() const; + void setTextureSize(const QSize &size); + + Format format() const; + void setFormat(Format format); + + bool live() const; + void setLive(bool live); + + bool hideSource() const; + void setHideSource(bool hide); + + bool mipmap() const; + void setMipmap(bool enabled); + + bool recursive() const; + void setRecursive(bool enabled); + + bool isTextureProvider() const { return true; } + QSGTextureProvider *textureProvider() const; + + Q_INVOKABLE void scheduleUpdate(); + +Q_SIGNALS: + void wrapModeChanged(); + void sourceItemChanged(); + void sourceRectChanged(); + void textureSizeChanged(); + void formatChanged(); + void liveChanged(); + void hideSourceChanged(); + void mipmapChanged(); + void recursiveChanged(); + + void textureChanged(); + +protected: + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private: + void ensureTexture(); + + QQuickShaderEffectSourceTextureProvider *m_provider; + QQuickShaderEffectTexture *m_texture; + WrapMode m_wrapMode; + QPointer m_sourceItem; + QRectF m_sourceRect; + QSize m_textureSize; + Format m_format; + uint m_live : 1; + uint m_hideSource : 1; + uint m_mipmap : 1; + uint m_recursive : 1; + uint m_grab : 1; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKSHADEREFFECTSOURCE_P_H diff --git a/src/declarative/items/qquicksprite.cpp b/src/declarative/items/qquicksprite.cpp new file mode 100644 index 0000000000..b476905269 --- /dev/null +++ b/src/declarative/items/qquicksprite.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicksprite_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Sprite QQuickSprite + \inqmlmodule QtQuick 2 + \brief The Sprite element represents a sprite animation + +*/ +/*! + \qmlproperty int QtQuick2::Sprite::duration + + Time between frames. +*/ +/*! + \qmlproperty int QtQuick2::Sprite::durationVariation + + The time between frames can vary by up to this amount. + + Default is 0. +*/ + +/*! + \qmlproperty string QtQuick2::Sprite::name + + The name of this sprite, for use in the to property of other sprites. +*/ +/*! + \qmlproperty QVariantMap QtQuick2::Sprite::to + + A list of other sprites and weighted transitions to them, + for example {"a":1, "b":2, "c":0} would specify that one-third should + transition to sprite "a" when this sprite is done, and two-thirds should + transition to sprite "b" when this sprite is done. As the transitions are + chosen randomly, these proportions will not be exact. With "c":0 in the list, + no sprites will randomly transition to "c", but it wll be a valid path if a sprite + goal is set. + + If no list is specified, or the sum of weights in the list is zero, then the sprite + will repeat itself after completing. +*/ +/*! + \qmlproperty int QtQuick2::Sprite::frames + + Number of frames in this sprite. +*/ +/*! + \qmlproperty int QtQuick2::Sprite::frameHeight + + Height of a single frame in this sprite. +*/ +/*! + \qmlproperty int QtQuick2::Sprite::frameWidth + + Width of a single frame in this sprite. +*/ +/*! + \qmlproperty url QtQuick2::Sprite::source + + The image source for the animation. + + If frameHeight and frameWidth are not specified, it is assumed to be a single long row of square frames. + Otherwise, it can be multiple contiguous rows or rectangluar frames, when one row runs out the next will be used. +*/ + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged) + Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged) + Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + //If frame height or width is not specified, it is assumed to be a single long row of square frames. + //Otherwise, it can be multiple contiguous rows, when one row runs out the next will be used. + Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged) + Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged) + +QQuickSprite::QQuickSprite(QObject *parent) : + QQuickStochasticState(parent) + , m_generatedCount(0) + , m_framesPerRow(0) + , m_frameHeight(0) + , m_frameWidth(0) + , m_rowY(0) +{ +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquicksprite_p.h b/src/declarative/items/qquicksprite_p.h new file mode 100644 index 0000000000..e7f3bbce80 --- /dev/null +++ b/src/declarative/items/qquicksprite_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSPRITE_P_H +#define QQUICKSPRITE_P_H + +#include +#include +#include +#include +#include "qquickspriteengine_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QQuickSprite : public QQuickStochasticState +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + //If frame height or width is not specified, it is assumed to be a single long row of square frames. + //Otherwise, it can be multiple contiguous rows, when one row runs out the next will be used. + Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged) + Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged) + +public: + explicit QQuickSprite(QObject *parent = 0); + + QUrl source() const + { + return m_source; + } + + int frameHeight() const + { + return m_frameHeight; + } + + int frameWidth() const + { + return m_frameWidth; + } + + +signals: + + void sourceChanged(QUrl arg); + + void frameHeightChanged(int arg); + + void frameWidthChanged(int arg); + +public slots: + + void setSource(QUrl arg) + { + if (m_source != arg) { + m_source = arg; + emit sourceChanged(arg); + } + } + + void setFrameHeight(int arg) + { + if (m_frameHeight != arg) { + m_frameHeight = arg; + emit frameHeightChanged(arg); + } + } + + void setFrameWidth(int arg) + { + if (m_frameWidth != arg) { + m_frameWidth = arg; + emit frameWidthChanged(arg); + } + } + + +private: + friend class QSGImageParticle; + friend class QQuickSpriteEngine; + friend class QQuickStochasticEngine; + int m_generatedCount; + int m_framesPerRow; + QUrl m_source; + int m_frameHeight; + int m_frameWidth; + int m_rowY; + +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // QQUICKSPRITE_P_H diff --git a/src/declarative/items/qquickspriteengine.cpp b/src/declarative/items/qquickspriteengine.cpp new file mode 100644 index 0000000000..9cf21eb2d9 --- /dev/null +++ b/src/declarative/items/qquickspriteengine.cpp @@ -0,0 +1,500 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickspriteengine_p.h" +#include "qquicksprite_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* TODO: Split out image logic from stochastic state logic + Also make sharable + Also solve the state data initialization/transfer issue so as to not need to make friends +*/ + +QQuickStochasticEngine::QQuickStochasticEngine(QObject *parent) : + QObject(parent), m_timeOffset(0) +{ + //Default size 1 + setCount(1); + m_advanceTime.start(); +} + +QQuickStochasticEngine::QQuickStochasticEngine(QList states, QObject *parent) : + QObject(parent), m_states(states), m_timeOffset(0) +{ + //Default size 1 + setCount(1); + m_advanceTime.start(); +} + +QQuickStochasticEngine::~QQuickStochasticEngine() +{ +} + +QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent) + : QQuickStochasticEngine(parent) +{ +} + +QQuickSpriteEngine::QQuickSpriteEngine(QList sprites, QObject *parent) + : QQuickStochasticEngine(parent) +{ + foreach (QQuickSprite* sprite, sprites) + m_states << (QQuickStochasticState*)sprite; +} + +QQuickSpriteEngine::~QQuickSpriteEngine() +{ +} + + +int QQuickSpriteEngine::maxFrames() +{ + return m_maxFrames; +} + +/* States too large to fit in one row are split into multiple rows + This is more efficient for the implementation, but should remain an implementation detail (invisible from QML) + Therefore the below functions abstract sprite from the viewpoint of classes that pass the details onto shaders + But States maintain their listed index for internal structures +TODO: All these calculations should be pre-calculated and cached during initialization for a significant performance boost +TODO: Above idea needs to have the varying duration offset added to it +*/ +int QQuickSpriteEngine::spriteState(int sprite) +{ + int state = m_things[sprite]; + if (!m_sprites[state]->m_generatedCount) + return state; + int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; + int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; + return state + extra; +} + +int QQuickSpriteEngine::spriteStart(int sprite) +{ + int state = m_things[sprite]; + if (!m_sprites[state]->m_generatedCount) + return m_startTimes[sprite]; + int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; + int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; + return state + extra*rowDuration; +} + +int QQuickSpriteEngine::spriteFrames(int sprite) +{ + int state = m_things[sprite]; + if (!m_sprites[state]->m_generatedCount) + return m_sprites[state]->frames(); + int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; + int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; + if (extra == m_sprites[state]->m_generatedCount - 1)//last state + return m_sprites[state]->frames() % m_sprites[state]->m_framesPerRow; + else + return m_sprites[state]->m_framesPerRow; +} + +int QQuickSpriteEngine::spriteDuration(int sprite) +{ + int state = m_things[sprite]; + if (!m_sprites[state]->m_generatedCount) + return m_duration[sprite]; + int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; + int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; + if (extra == m_sprites[state]->m_generatedCount - 1)//last state + return (m_duration[sprite] * m_sprites[state]->frames()) % rowDuration; + else + return rowDuration; +} + +int QQuickSpriteEngine::spriteY(int sprite) +{ + int state = m_things[sprite]; + if (!m_sprites[state]->m_generatedCount) + return m_sprites[state]->m_rowY; + int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; + int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; + return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra; +} + +int QQuickSpriteEngine::spriteWidth(int sprite) +{ + int state = m_things[sprite]; + return m_sprites[state]->m_frameWidth; +} + +int QQuickSpriteEngine::spriteHeight(int sprite) +{ + int state = m_things[sprite]; + return m_sprites[state]->m_frameHeight; +} + +int QQuickSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together +{ + return m_imageStateCount; +} + +void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump) +{ + if (sprite >= m_things.count() || state >= m_states.count()) + return; + if (!jump){ + m_goals[sprite] = state; + return; + } + + if (m_things[sprite] == state) + return;//Already there + m_things[sprite] = state; + m_duration[sprite] = m_states[state]->variedDuration(); + m_goals[sprite] = -1; + restart(sprite); + emit stateChanged(sprite); + emit m_states[state]->entered(); + return; +} + +QImage QQuickSpriteEngine::assembledImage() +{ + int h = 0; + int w = 0; + m_maxFrames = 0; + m_imageStateCount = 0; + int maxSize = 0; + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); + foreach (QQuickStochasticState* s, m_states){ + QQuickSprite* sprite = qobject_cast(s); + if (sprite) + m_sprites << sprite; + else + qDebug() << "Error: Non-sprite in QQuickSpriteEngine"; + } + + foreach (QQuickSprite* state, m_sprites){ + if (state->frames() > m_maxFrames) + m_maxFrames = state->frames(); + + QImage img(state->source().toLocalFile()); + if (img.isNull()) { + qWarning() << "SpriteEngine: loading image failed..." << state->source().toLocalFile(); + return QImage(); + } + + //Check that the frame sizes are the same within one engine + if (!state->m_frameWidth) + state->m_frameWidth = img.width() / state->frames(); + + if (!state->m_frameHeight) + state->m_frameHeight = img.height(); + + if (state->frames() * state->frameWidth() > maxSize){ + struct helper{ + static int divRoundUp(int a, int b){return (a+b-1)/b;} + }; + int rowsNeeded = helper::divRoundUp(state->frames(), helper::divRoundUp(maxSize, state->frameWidth())); + if (rowsNeeded * state->frameHeight() > maxSize){ + qWarning() << "SpriteEngine: Animation too large to fit in one texture..." << state->source().toLocalFile(); + qWarning() << "SpriteEngine: Your texture max size today is " << maxSize; + } + state->m_generatedCount = rowsNeeded; + h += state->frameHeight() * rowsNeeded; + w = qMax(w, helper::divRoundUp(maxSize, state->frameWidth())); + m_imageStateCount += rowsNeeded; + }else{ + h += state->frameHeight(); + w = qMax(w, state->frameWidth() * state->frames()); + m_imageStateCount++; + } + } + + //maxFrames is max number in a line of the texture + QImage image(w, h, QImage::Format_ARGB32); + image.fill(0); + QPainter p(&image); + int y = 0; + foreach (QQuickSprite* state, m_sprites){ + QImage img(state->source().toLocalFile()); + int frameWidth = state->m_frameWidth; + int frameHeight = state->m_frameHeight; + if (img.height() == frameHeight && img.width() < maxSize){//Simple case + p.drawImage(0,y,img); + state->m_rowY = y; + y += frameHeight; + }else{//Chopping up image case + state->m_framesPerRow = image.width()/frameWidth; + state->m_rowY = y; + int x = 0; + int curX = 0; + int curY = 0; + int framesLeft = state->frames(); + while (framesLeft > 0){ + if (image.width() - x + curX <= img.width()){//finish a row in image (dest) + int copied = image.width() - x; + Q_ASSERT(!(copied % frameWidth));//XXX: Just checking + framesLeft -= copied/frameWidth; + p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight)); + y += frameHeight; + curX += copied; + x = 0; + if (curX == img.width()){ + curX = 0; + curY += frameHeight; + } + }else{//finish a row in img (src) + int copied = img.width() - curX; + Q_ASSERT(!(copied % frameWidth));//XXX: Just checking + framesLeft -= copied/frameWidth; + p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight)); + curY += frameHeight; + x += copied; + curX = 0; + } + } + if (x) + y += frameHeight; + } + } + + if (image.height() > maxSize){ + qWarning() << "SpriteEngine: Too many animations to fit in one texture..."; + qWarning() << "SpriteEngine: Your texture max size today is " << maxSize; + return QImage(); + } + return image; +} + +void QQuickStochasticEngine::setCount(int c) +{ + m_things.resize(c); + m_goals.resize(c); + m_duration.resize(c); + m_startTimes.resize(c); +} + +void QQuickStochasticEngine::start(int index, int state) +{ + if (index >= m_things.count()) + return; + m_things[index] = state; + m_duration[index] = m_states[state]->variedDuration(); + m_goals[index] = -1; + restart(index); +} + +void QQuickStochasticEngine::stop(int index) +{ + if (index >= m_things.count()) + return; + //Will never change until start is called again with a new state - this is not a 'pause' + for (int i=0; iframes() + m_startTimes[index]; + for (int i=0; i changedIndexes; + while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){ + foreach (int idx, m_stateUpdates.first().second){ + if (idx >= m_things.count()) + continue;//TODO: Proper fix(because this does happen and I'm just ignoring it) + int stateIdx = m_things[idx]; + int nextIdx = -1; + int goalPath = goalSeek(stateIdx, idx); + if (goalPath == -1){//Random + qreal r =(qreal) qrand() / (qreal) RAND_MAX; + qreal total = 0.0; + for (QVariantMap::const_iterator iter=m_states[stateIdx]->m_to.constBegin(); + iter!=m_states[stateIdx]->m_to.constEnd(); iter++) + total += (*iter).toReal(); + r*=total; + for (QVariantMap::const_iterator iter= m_states[stateIdx]->m_to.constBegin(); + iter!=m_states[stateIdx]->m_to.constEnd(); iter++){ + if (r < (*iter).toReal()){ + bool superBreak = false; + for (int i=0; iname() == iter.key()){ + nextIdx = i; + superBreak = true; + break; + } + } + if (superBreak) + break; + } + r -= (*iter).toReal(); + } + }else{//Random out of shortest paths to goal + nextIdx = goalPath; + } + if (nextIdx == -1)//No to states means stay here + nextIdx = stateIdx; + + m_things[idx] = nextIdx; + m_duration[idx] = m_states[nextIdx]->variedDuration(); + m_startTimes[idx] = time; + if (nextIdx != stateIdx){ + changedIndexes << idx; + emit m_states[nextIdx]->entered(); + } + addToUpdateList((m_duration[idx] * m_states[nextIdx]->frames()) + time, idx); + } + m_stateUpdates.pop_front(); + } + + m_timeOffset = time; + m_advanceTime.start(); + //TODO: emit this when a psuedostate changes too + foreach (int idx, changedIndexes){//Batched so that update list doesn't change midway + emit stateChanged(idx); + } + if (m_stateUpdates.isEmpty()) + return -1; + return m_stateUpdates.first().first; +} + +int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) +{ + QString goalName; + if (m_goals[spriteIdx] != -1) + goalName = m_states[m_goals[spriteIdx]]->name(); + else + goalName = m_globalGoal; + if (goalName.isEmpty()) + return -1; + //TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitarily anyways) + // Paraphrased - implement in an *efficient* manner + for (int i=0; iname() == goalName) + return curIdx; + if (dist < 0) + dist = m_states.count(); + QQuickStochasticState* curState = m_states[curIdx]; + for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); + iter!=curState->m_to.constEnd(); iter++){ + if (iter.key() == goalName) + for (int i=0; iname() == goalName) + return i; + } + QSet options; + for (int i=1; im_to.constBegin(); + iter!=curState->m_to.constEnd(); iter++){ + int option = -1; + for (int j=0; jname() == iter.key()) + if (goalSeek(j, spriteIdx, i) != -1) + option = j; + if (option != -1) + options << option; + } + if (!options.isEmpty()){ + if (options.count()==1) + return *(options.begin()); + int option = -1; + qreal r =(qreal) qrand() / (qreal) RAND_MAX; + qreal total = 0; + for (QSet::const_iterator iter=options.constBegin(); + iter!=options.constEnd(); iter++) + total += curState->m_to.value(m_states[(*iter)]->name()).toReal(); + r *= total; + for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); + iter!=curState->m_to.constEnd(); iter++){ + bool superContinue = true; + for (int j=0; jname() == iter.key()) + if (options.contains(j)) + superContinue = false; + if (superContinue) + continue; + if (r < (*iter).toReal()){ + bool superBreak = false; + for (int j=0; jname() == iter.key()){ + option = j; + superBreak = true; + break; + } + } + if (superBreak) + break; + } + r-=(*iter).toReal(); + } + return option; + } + } + return -1; +} + +void QQuickStochasticEngine::addToUpdateList(uint t, int idx) +{ + for (int i=0; i t){ + QList tmpList; + tmpList << idx; + m_stateUpdates.insert(i, qMakePair(t, tmpList)); + return; + } + } + QList tmpList; + tmpList << idx; + m_stateUpdates << qMakePair(t, tmpList); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickspriteengine_p.h b/src/declarative/items/qquickspriteengine_p.h new file mode 100644 index 0000000000..6d527d12dc --- /dev/null +++ b/src/declarative/items/qquickspriteengine_p.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSPRITEENGINE_P_H +#define QQUICKSPRITEENGINE_P_H + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickSprite; +class Q_AUTOTEST_EXPORT QQuickStochasticState : public QObject //For internal use +{ + Q_OBJECT + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged) + Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged) + Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged) + +public: + QQuickStochasticState(QObject* parent = 0) + : QObject(parent) + , m_frames(1) + , m_duration(1000) + { + } + + int duration() const + { + return m_duration; + } + + QString name() const + { + return m_name; + } + + QVariantMap to() const + { + return m_to; + } + + qreal speedModifer() const + { + return m_speedModifier; + } + + int durationVariance() const + { + return m_durationVariance; + } + + + int variedDuration() const + { + return m_duration + + (m_durationVariance * ((qreal)qrand()/RAND_MAX) * 2) + - m_durationVariance; + } + + int frames() const + { + return m_frames; + } + +signals: + void durationChanged(int arg); + + void nameChanged(QString arg); + + void toChanged(QVariantMap arg); + + void speedModifierChanged(qreal arg); + + void durationVarianceChanged(int arg); + + void entered();//### Just playing around - don't expect full state API + void framesChanged(int arg); + +public slots: + void setDuration(int arg) + { + if (m_duration != arg) { + m_duration = arg; + emit durationChanged(arg); + } + } + + void setName(QString arg) + { + if (m_name != arg) { + m_name = arg; + emit nameChanged(arg); + } + } + + void setTo(QVariantMap arg) + { + if (m_to != arg) { + m_to = arg; + emit toChanged(arg); + } + } + + void setSpeedModifier(qreal arg) + { + if (m_speedModifier != arg) { + m_speedModifier = arg; + emit speedModifierChanged(arg); + } + } + + void setDurationVariance(int arg) + { + if (m_durationVariance != arg) { + m_durationVariance = arg; + emit durationVarianceChanged(arg); + } + } + + void setFrames(int arg) + { + if (m_frames != arg) { + m_frames = arg; + emit framesChanged(arg); + } + } + +private: + QString m_name; + int m_frames; + QVariantMap m_to; + int m_duration; + qreal m_speedModifier; + int m_durationVariance; + + friend class QQuickStochasticEngine; +}; + +class Q_AUTOTEST_EXPORT QQuickStochasticEngine : public QObject +{ + Q_OBJECT + //TODO: Optimize single state case? + Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged) + Q_PROPERTY(QDeclarativeListProperty states READ states) +public: + explicit QQuickStochasticEngine(QObject *parent = 0); + QQuickStochasticEngine(QList states, QObject *parent=0); + ~QQuickStochasticEngine(); + + QDeclarativeListProperty states() + { + return QDeclarativeListProperty(this, m_states); + } + + QString globalGoal() const + { + return m_globalGoal; + } + + int count() const {return m_things.count();} + void setCount(int c); + + + + void setGoal(int state, int sprite=0, bool jump=false); + void start(int index=0, int state=0); + void stop(int index=0); + int curState(int index=0) {return m_things[index];} + + QQuickStochasticState* state(int idx){return m_states[idx];} + int stateIndex(QQuickStochasticState* s){return m_states.indexOf(s);} + int stateCount() {return m_states.count();} +private: +signals: + + void globalGoalChanged(QString arg); + void stateChanged(int idx); + +public slots: + void setGlobalGoal(QString arg) + { + if (m_globalGoal != arg) { + m_globalGoal = arg; + emit globalGoalChanged(arg); + } + } + + uint updateSprites(uint time); + +protected: + friend class QSGParticleSystem; + void restart(int index); + void addToUpdateList(uint t, int idx); + int goalSeek(int curState, int idx, int dist=-1); + QList m_states; + //### Consider struct or class for the four data variables? + QVector m_things;//int is the index in m_states of the current state + QVector m_goals; + QVector m_duration; + QVector m_startTimes; + QList > > m_stateUpdates;//### This could be done faster - priority queue? + + QTime m_advanceTime; + uint m_timeOffset; + QString m_globalGoal; + int m_maxFrames; + int m_imageStateCount; +}; + +class QQuickSpriteEngine : public QQuickStochasticEngine +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) +public: + explicit QQuickSpriteEngine(QObject *parent = 0); + QQuickSpriteEngine(QList sprites, QObject *parent=0); + ~QQuickSpriteEngine(); + QDeclarativeListProperty sprites() + { + return QDeclarativeListProperty(this, m_sprites); + } + + + int spriteState(int sprite=0); + int spriteStart(int sprite=0); + int spriteFrames(int sprite=0); + int spriteDuration(int sprite=0); + int spriteX(int /* sprite */ = 0) { return 0; }//Currently all rows are 0 aligned, if we get more space efficient we might change this + int spriteY(int sprite=0); + int spriteWidth(int sprite=0); + int spriteHeight(int sprite=0); + int spriteCount();//Like state count, but for the image states + int maxFrames(); + QImage assembledImage(); +private: + QList m_sprites; +}; + +//Common use is to have your own list property which is transparently an engine +inline void spriteAppend(QDeclarativeListProperty *p, QQuickSprite* s) +{ + reinterpret_cast *>(p->data)->append(s); + p->object->metaObject()->invokeMethod(p->object, "createEngine"); +} + +inline QQuickSprite* spriteAt(QDeclarativeListProperty *p, int idx) +{ + return reinterpret_cast *>(p->data)->at(idx); +} + +inline void spriteClear(QDeclarativeListProperty *p) +{ + reinterpret_cast *>(p->data)->clear(); + p->object->metaObject()->invokeMethod(p->object, "createEngine"); +} + +inline int spriteCount(QDeclarativeListProperty *p) +{ + return reinterpret_cast *>(p->data)->count(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKSPRITEENGINE_P_H diff --git a/src/declarative/items/qquickspriteimage.cpp b/src/declarative/items/qquickspriteimage.cpp new file mode 100644 index 0000000000..36ab73490c --- /dev/null +++ b/src/declarative/items/qquickspriteimage.cpp @@ -0,0 +1,421 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickspriteimage_p.h" +#include "qquicksprite_p.h" +#include "qquickspriteengine_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char vertexShaderCode[] = + "attribute highp vec2 vTex;\n" + "uniform highp vec4 animData;// interpolate(bool), duration, frameCount (this anim), timestamp (this anim)\n" + "uniform highp vec4 animPos;//sheet x,y, width/height of this anim\n" + "uniform highp vec4 animSheetSize; //width/height of whole sheet, width/height of element\n" + "\n" + "uniform highp mat4 qt_Matrix;\n" + "uniform highp float timestamp;\n" + "\n" + "varying highp vec4 fTexS;\n" + "varying lowp float progress;\n" + "\n" + "\n" + "void main() {\n" + " //Calculate frame location in texture\n" + " highp float frameIndex = mod((((timestamp - animData.w)*1000.)/animData.y),animData.z);\n" + " progress = mod((timestamp - animData.w)*1000., animData.y) / animData.y;\n" + "\n" + " frameIndex = floor(frameIndex);\n" + " fTexS.xy = vec2(((frameIndex + vTex.x) * animPos.z / animSheetSize.x), ((animPos.y + vTex.y * animPos.w) / animSheetSize.y));\n" + "\n" + " //Next frame is also passed, for interpolation\n" + " //### Should the next anim be precalculated to allow for interpolation there?\n" + " if (animData.x == 1.0 && frameIndex != animData.z - 1.)//Can't do it for the last frame though, this anim may not loop\n" + " frameIndex = mod(frameIndex+1., animData.z);\n" + " fTexS.zw = vec2(((frameIndex + vTex.x) * animPos.z / animSheetSize.x), ((animPos.y + vTex.y * animPos.w) / animSheetSize.y));\n" + "\n" + " gl_Position = qt_Matrix * vec4(animSheetSize.z * vTex.x, animSheetSize.w * vTex.y, 0, 1);\n" + "}\n"; + +static const char fragmentShaderCode[] = + "uniform sampler2D texture;\n" + "uniform lowp float qt_Opacity;\n" + "\n" + "varying highp vec4 fTexS;\n" + "varying lowp float progress;\n" + "\n" + "void main() {\n" + " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), progress) * qt_Opacity;\n" + "}\n"; + +class QQuickSpriteMaterial : public QSGMaterial +{ +public: + QQuickSpriteMaterial(); + virtual ~QQuickSpriteMaterial(); + virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } + virtual QSGMaterialShader *createShader() const; + virtual int compare(const QSGMaterial *other) const + { + return this - static_cast(other); + } + + QSGTexture *texture; + + qreal timestamp; + float interpolate; + float frameDuration; + float frameCount; + float animT; + float animX; + float animY; + float animWidth; + float animHeight; + float sheetWidth; + float sheetHeight; + float elementWidth; + float elementHeight; +}; + +QQuickSpriteMaterial::QQuickSpriteMaterial() + : timestamp(0) + , interpolate(1.0f) + , frameDuration(1.0f) + , frameCount(1.0f) + , animT(0.0f) + , animX(0.0f) + , animY(0.0f) + , animWidth(1.0f) + , animHeight(1.0f) + , sheetWidth(1.0f) + , sheetHeight(1.0f) + , elementWidth(1.0f) + , elementHeight(1.0f) +{ + setFlag(Blending, true); +} + +QQuickSpriteMaterial::~QQuickSpriteMaterial() +{ + delete texture; +} + +class SpriteMaterialData : public QSGMaterialShader +{ +public: + SpriteMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) + { + } + + void deactivate() { + QSGMaterialShader::deactivate(); + + for (int i=0; i<8; ++i) { + program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); + } + } + + virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) + { + QQuickSpriteMaterial *m = static_cast(newEffect); + m->texture->bind(); + + program()->setUniformValue(m_opacity_id, state.opacity()); + program()->setUniformValue(m_timestamp_id, (float) m->timestamp); + program()->setUniformValue(m_animData_id, m->interpolate, m->frameDuration, m->frameCount, m->animT); + program()->setUniformValue(m_animPos_id, m->animX, m->animY, m->animWidth, m->animHeight); + program()->setUniformValue(m_animSheetSize_id, m->sheetWidth, m->sheetHeight, m->elementWidth, m->elementHeight); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + + virtual void initialize() { + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_opacity_id = program()->uniformLocation("qt_Opacity"); + m_timestamp_id = program()->uniformLocation("timestamp"); + m_animData_id = program()->uniformLocation("animData"); + m_animPos_id = program()->uniformLocation("animPos"); + m_animSheetSize_id = program()->uniformLocation("animSheetSize"); + } + + virtual const char *vertexShader() const { return vertexShaderCode; } + virtual const char *fragmentShader() const { return fragmentShaderCode; } + + virtual char const *const *attributeNames() const { + static const char *attr[] = { + "vTex", + 0 + }; + return attr; + } + + int m_matrix_id; + int m_opacity_id; + int m_timestamp_id; + int m_animData_id; + int m_animPos_id; + int m_animSheetSize_id; + + static float chunkOfBytes[1024]; +}; + +float SpriteMaterialData::chunkOfBytes[1024]; + +QSGMaterialShader *QQuickSpriteMaterial::createShader() const +{ + return new SpriteMaterialData; +} + +struct SpriteVertex { + float tx; + float ty; +}; + +struct SpriteVertices { + SpriteVertex v1; + SpriteVertex v2; + SpriteVertex v3; + SpriteVertex v4; +}; + +/*! + \qmlclass SpriteImage QQuickSpriteImage + \inqmlmodule QtQuick 2 + \inherits Item + \brief The SpriteImage element draws a sprite animation + +*/ +/*! + \qmlproperty bool QtQuick2::SpriteImage::running + + Whether the sprite is animating or not. + + Default is true +*/ +/*! + \qmlproperty bool QtQuick2::SpriteImage::interpolate + + If true, interpolation will occur between sprite frames to make the + animation appear smoother. + + Default is true. +*/ +/*! + \qmlproperty list QtQuick2::SpriteImage::sprites + + The sprite or sprites to draw. Sprites will be scaled to the size of this element. +*/ + +//TODO: Implicitly size element to size of first sprite? +QQuickSpriteImage::QQuickSpriteImage(QQuickItem *parent) : + QQuickItem(parent) + , m_node(0) + , m_material(0) + , m_spriteEngine(0) + , m_pleaseReset(false) + , m_running(true) + , m_interpolate(true) +{ + setFlag(ItemHasContents); + connect(this, SIGNAL(runningChanged(bool)), + this, SLOT(update())); +} + +QDeclarativeListProperty QQuickSpriteImage::sprites() +{ + return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); +} + +void QQuickSpriteImage::createEngine() +{ + //TODO: delay until component complete + if (m_spriteEngine) + delete m_spriteEngine; + if (m_sprites.count()) + m_spriteEngine = new QQuickSpriteEngine(m_sprites, this); + else + m_spriteEngine = 0; + reset(); +} + +static QSGGeometry::Attribute SpriteImage_Attributes[] = { + QSGGeometry::Attribute::create(0, 2, GL_FLOAT), // tex +}; + +static QSGGeometry::AttributeSet SpriteImage_AttributeSet = +{ + 1, // Attribute Count + 2 * sizeof(float), + SpriteImage_Attributes +}; + +QSGGeometryNode* QQuickSpriteImage::buildNode() +{ + if (!m_spriteEngine) { + qWarning() << "SpriteImage: No sprite engine..."; + return 0; + } + + m_material = new QQuickSpriteMaterial(); + + QImage image = m_spriteEngine->assembledImage(); + if (image.isNull()) + return 0; + m_material->texture = sceneGraphEngine()->createTextureFromImage(image); + m_material->texture->setFiltering(QSGTexture::Linear); + m_spriteEngine->start(0); + m_material->interpolate = m_interpolate ? 1.0 : 0.0; + m_material->frameCount = m_spriteEngine->spriteFrames(); + m_material->frameDuration = m_spriteEngine->spriteDuration(); + m_material->animT = 0; + m_material->animX = m_spriteEngine->spriteX(); + m_material->animY = m_spriteEngine->spriteY(); + m_material->animWidth = m_spriteEngine->spriteWidth(); + m_material->animHeight = m_spriteEngine->spriteHeight(); + m_material->sheetWidth = image.width(); + m_material->sheetHeight = image.height(); + m_material->elementWidth = width(); + m_material->elementHeight = height(); + + int vCount = 4; + int iCount = 6; + QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount); + g->setDrawingMode(GL_TRIANGLES); + + SpriteVertices *p = (SpriteVertices *) g->vertexData(); + + p->v1.tx = 0; + p->v1.ty = 0; + + p->v2.tx = 1.0; + p->v2.ty = 0; + + p->v3.tx = 0; + p->v3.ty = 1.0; + + p->v4.tx = 1.0; + p->v4.ty = 1.0; + + quint16 *indices = g->indexDataAsUShort(); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 1; + indices[4] = 3; + indices[5] = 2; + + + m_timestamp.start(); + m_node = new QSGGeometryNode(); + m_node->setGeometry(g); + m_node->setMaterial(m_material); + m_node->setFlag(QSGGeometryNode::OwnsMaterial); + return m_node; +} + +void QQuickSpriteImage::reset() +{ + m_pleaseReset = true; +} + +QSGNode *QQuickSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *) +{ + if (m_pleaseReset) { + delete m_node; + delete m_material; + + m_node = 0; + m_material = 0; + m_pleaseReset = false; + } + + prepareNextFrame(); + + if (m_running) { + update(); + if (m_node) + m_node->markDirty(QSGNode::DirtyMaterial); + } + + return m_node; +} + +void QQuickSpriteImage::prepareNextFrame() +{ + if (m_node == 0) + m_node = buildNode(); + if (m_node == 0) //error creating node + return; + + uint timeInt = m_timestamp.elapsed(); + qreal time = timeInt / 1000.; + m_material->timestamp = time; + m_material->elementHeight = height(); + m_material->elementWidth = width(); + m_material->interpolate = m_interpolate; + + //Advance State + SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData(); + m_spriteEngine->updateSprites(timeInt); + int curY = m_spriteEngine->spriteY(); + if (curY != m_material->animY){ + m_material->animT = m_spriteEngine->spriteStart()/1000.0; + m_material->frameCount = m_spriteEngine->spriteFrames(); + m_material->frameDuration = m_spriteEngine->spriteDuration(); + m_material->animX = m_spriteEngine->spriteX(); + m_material->animY = m_spriteEngine->spriteY(); + m_material->animWidth = m_spriteEngine->spriteWidth(); + m_material->animHeight = m_spriteEngine->spriteHeight(); + } +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickspriteimage_p.h b/src/declarative/items/qquickspriteimage_p.h new file mode 100644 index 0000000000..1ffc95d952 --- /dev/null +++ b/src/declarative/items/qquickspriteimage_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Declarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSPRITEIMAGE_P_H +#define QQUICKSPRITEIMAGE_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QSGContext; +class QQuickSprite; +class QQuickSpriteEngine; +class QSGGeometryNode; +class QQuickSpriteMaterial; +class QQuickSpriteImage : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate NOTIFY interpolateChanged) + //###try to share similar spriteEngines for less overhead? + Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) + Q_CLASSINFO("DefaultProperty", "sprites") + +public: + explicit QQuickSpriteImage(QQuickItem *parent = 0); + + QDeclarativeListProperty sprites(); + + bool running() const + { + return m_running; + } + + bool interpolate() const + { + return m_interpolate; + } + +signals: + + void runningChanged(bool arg); + void interpolateChanged(bool arg); + +public slots: + +void setRunning(bool arg) +{ + if (m_running != arg) { + m_running = arg; + emit runningChanged(arg); + } +} + +void setInterpolate(bool arg) +{ + if (m_interpolate != arg) { + m_interpolate = arg; + emit interpolateChanged(arg); + } +} + +private slots: + void createEngine(); +protected: + void reset(); + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); +private: + void prepareNextFrame(); + QSGGeometryNode* buildNode(); + QSGGeometryNode *m_node; + QQuickSpriteMaterial *m_material; + QList m_sprites; + QQuickSpriteEngine* m_spriteEngine; + QTime m_timestamp; + int m_maxFrames; + bool m_pleaseReset; + bool m_running; + bool m_interpolate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKSPRITEIMAGE_P_H diff --git a/src/declarative/items/qquickstateoperations.cpp b/src/declarative/items/qquickstateoperations.cpp new file mode 100644 index 0000000000..8c53e0a63f --- /dev/null +++ b/src/declarative/items/qquickstateoperations.cpp @@ -0,0 +1,1346 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstateoperations_p.h" +#include "qquickitem_p.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickParentChangePrivate : public QDeclarativeStateOperationPrivate +{ + Q_DECLARE_PUBLIC(QQuickParentChange) +public: + QQuickParentChangePrivate() : target(0), parent(0), origParent(0), origStackBefore(0), + rewindParent(0), rewindStackBefore(0) {} + + QQuickItem *target; + QDeclarativeGuard parent; + QDeclarativeGuard origParent; + QDeclarativeGuard origStackBefore; + QQuickItem *rewindParent; + QQuickItem *rewindStackBefore; + + QDeclarativeNullableValue xString; + QDeclarativeNullableValue yString; + QDeclarativeNullableValue widthString; + QDeclarativeNullableValue heightString; + QDeclarativeNullableValue scaleString; + QDeclarativeNullableValue rotationString; + + void doChange(QQuickItem *targetParent, QQuickItem *stackBefore = 0); +}; + +void QQuickParentChangePrivate::doChange(QQuickItem *targetParent, QQuickItem *stackBefore) +{ + if (targetParent && target && target->parentItem()) { + Q_Q(QQuickParentChange); + bool ok; + const QTransform &transform = target->parentItem()->itemTransform(targetParent, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(q) << QQuickParentChange::tr("Unable to preserve appearance under complex transform"); + ok = false; + } + + qreal scale = 1; + qreal rotation = 0; + bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); + if (ok && !isRotate) { + if (transform.m11() == transform.m22()) + scale = transform.m11(); + else { + qmlInfo(q) << QQuickParentChange::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + } else if (ok && isRotate) { + if (transform.m11() == transform.m22()) + scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); + else { + qmlInfo(q) << QQuickParentChange::tr("Unable to preserve appearance under non-uniform scale"); + ok = false; + } + + if (scale != 0) + rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; + else { + qmlInfo(q) << QQuickParentChange::tr("Unable to preserve appearance under scale of 0"); + ok = false; + } + } + + const QPointF &point = transform.map(QPointF(target->x(),target->y())); + qreal x = point.x(); + qreal y = point.y(); + + // setParentItem will update the transformOriginPoint if needed + target->setParentItem(targetParent); + + if (ok && target->transformOrigin() != QQuickItem::TopLeft) { + qreal tempxt = target->transformOriginPoint().x(); + qreal tempyt = target->transformOriginPoint().y(); + QTransform t; + t.translate(-tempxt, -tempyt); + t.rotate(rotation); + t.scale(scale, scale); + t.translate(tempxt, tempyt); + const QPointF &offset = t.map(QPointF(0,0)); + x += offset.x(); + y += offset.y(); + } + + if (ok) { + //qDebug() << x << y << rotation << scale; + target->setX(x); + target->setY(y); + target->setRotation(target->rotation() + rotation); + target->setScale(target->scale() * scale); + } + } else if (target) { + target->setParentItem(targetParent); + } + + //restore the original stack position. + //### if stackBefore has also been reparented this won't work + if (stackBefore) + target->stackBefore(stackBefore); +} + +QQuickParentChange::QQuickParentChange(QObject *parent) + : QDeclarativeStateOperation(*(new QQuickParentChangePrivate), parent) +{ +} + +QQuickParentChange::~QQuickParentChange() +{ +} + +QDeclarativeScriptString QQuickParentChange::x() const +{ + Q_D(const QQuickParentChange); + return d->xString.value; +} + +void QQuickParentChange::setX(QDeclarativeScriptString x) +{ + Q_D(QQuickParentChange); + d->xString = x; +} + +bool QQuickParentChange::xIsSet() const +{ + Q_D(const QQuickParentChange); + return d->xString.isValid(); +} + +QDeclarativeScriptString QQuickParentChange::y() const +{ + Q_D(const QQuickParentChange); + return d->yString.value; +} + +void QQuickParentChange::setY(QDeclarativeScriptString y) +{ + Q_D(QQuickParentChange); + d->yString = y; +} + +bool QQuickParentChange::yIsSet() const +{ + Q_D(const QQuickParentChange); + return d->yString.isValid(); +} + +QDeclarativeScriptString QQuickParentChange::width() const +{ + Q_D(const QQuickParentChange); + return d->widthString.value; +} + +void QQuickParentChange::setWidth(QDeclarativeScriptString width) +{ + Q_D(QQuickParentChange); + d->widthString = width; +} + +bool QQuickParentChange::widthIsSet() const +{ + Q_D(const QQuickParentChange); + return d->widthString.isValid(); +} + +QDeclarativeScriptString QQuickParentChange::height() const +{ + Q_D(const QQuickParentChange); + return d->heightString.value; +} + +void QQuickParentChange::setHeight(QDeclarativeScriptString height) +{ + Q_D(QQuickParentChange); + d->heightString = height; +} + +bool QQuickParentChange::heightIsSet() const +{ + Q_D(const QQuickParentChange); + return d->heightString.isValid(); +} + +QDeclarativeScriptString QQuickParentChange::scale() const +{ + Q_D(const QQuickParentChange); + return d->scaleString.value; +} + +void QQuickParentChange::setScale(QDeclarativeScriptString scale) +{ + Q_D(QQuickParentChange); + d->scaleString = scale; +} + +bool QQuickParentChange::scaleIsSet() const +{ + Q_D(const QQuickParentChange); + return d->scaleString.isValid(); +} + +QDeclarativeScriptString QQuickParentChange::rotation() const +{ + Q_D(const QQuickParentChange); + return d->rotationString.value; +} + +void QQuickParentChange::setRotation(QDeclarativeScriptString rotation) +{ + Q_D(QQuickParentChange); + d->rotationString = rotation; +} + +bool QQuickParentChange::rotationIsSet() const +{ + Q_D(const QQuickParentChange); + return d->rotationString.isValid(); +} + +QQuickItem *QQuickParentChange::originalParent() const +{ + Q_D(const QQuickParentChange); + return d->origParent; +} + +QQuickItem *QQuickParentChange::object() const +{ + Q_D(const QQuickParentChange); + return d->target; +} + +void QQuickParentChange::setObject(QQuickItem *target) +{ + Q_D(QQuickParentChange); + d->target = target; +} + +QQuickItem *QQuickParentChange::parent() const +{ + Q_D(const QQuickParentChange); + return d->parent; +} + +void QQuickParentChange::setParent(QQuickItem *parent) +{ + Q_D(QQuickParentChange); + d->parent = parent; +} + +QDeclarativeStateOperation::ActionList QQuickParentChange::actions() +{ + Q_D(QQuickParentChange); + if (!d->target || !d->parent) + return ActionList(); + + ActionList actions; + + QDeclarativeAction a; + a.event = this; + actions << a; + + if (d->xString.isValid()) { + bool ok = false; + QString script = d->xString.value.script(); + qreal x = script.toFloat(&ok); + if (ok) { + QDeclarativeAction xa(d->target, QLatin1String("x"), x); + actions << xa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("x"))); + QDeclarativeAction xa; + xa.property = newBinding->property(); + xa.toBinding = newBinding; + xa.fromValue = xa.property.read(); + xa.deletableToBinding = true; + actions << xa; + } + } + + if (d->yString.isValid()) { + bool ok = false; + QString script = d->yString.value.script(); + qreal y = script.toFloat(&ok); + if (ok) { + QDeclarativeAction ya(d->target, QLatin1String("y"), y); + actions << ya; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("y"))); + QDeclarativeAction ya; + ya.property = newBinding->property(); + ya.toBinding = newBinding; + ya.fromValue = ya.property.read(); + ya.deletableToBinding = true; + actions << ya; + } + } + + if (d->scaleString.isValid()) { + bool ok = false; + QString script = d->scaleString.value.script(); + qreal scale = script.toFloat(&ok); + if (ok) { + QDeclarativeAction sa(d->target, QLatin1String("scale"), scale); + actions << sa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("scale"))); + QDeclarativeAction sa; + sa.property = newBinding->property(); + sa.toBinding = newBinding; + sa.fromValue = sa.property.read(); + sa.deletableToBinding = true; + actions << sa; + } + } + + if (d->rotationString.isValid()) { + bool ok = false; + QString script = d->rotationString.value.script(); + qreal rotation = script.toFloat(&ok); + if (ok) { + QDeclarativeAction ra(d->target, QLatin1String("rotation"), rotation); + actions << ra; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("rotation"))); + QDeclarativeAction ra; + ra.property = newBinding->property(); + ra.toBinding = newBinding; + ra.fromValue = ra.property.read(); + ra.deletableToBinding = true; + actions << ra; + } + } + + if (d->widthString.isValid()) { + bool ok = false; + QString script = d->widthString.value.script(); + qreal width = script.toFloat(&ok); + if (ok) { + QDeclarativeAction wa(d->target, QLatin1String("width"), width); + actions << wa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("width"))); + QDeclarativeAction wa; + wa.property = newBinding->property(); + wa.toBinding = newBinding; + wa.fromValue = wa.property.read(); + wa.deletableToBinding = true; + actions << wa; + } + } + + if (d->heightString.isValid()) { + bool ok = false; + QString script = d->heightString.value.script(); + qreal height = script.toFloat(&ok); + if (ok) { + QDeclarativeAction ha(d->target, QLatin1String("height"), height); + actions << ha; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("height"))); + QDeclarativeAction ha; + ha.property = newBinding->property(); + ha.toBinding = newBinding; + ha.fromValue = ha.property.read(); + ha.deletableToBinding = true; + actions << ha; + } + } + + return actions; +} + +void QQuickParentChange::saveOriginals() +{ + Q_D(QQuickParentChange); + saveCurrentValues(); + d->origParent = d->rewindParent; + d->origStackBefore = d->rewindStackBefore; +} + +/*void QQuickParentChange::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QQuickParentChange); + QQuickParentChange *pc = static_cast(other); + + d->origParent = pc->d_func()->rewindParent; + d->origStackBefore = pc->d_func()->rewindStackBefore; + + saveCurrentValues(); +}*/ + +void QQuickParentChange::execute(Reason) +{ + Q_D(QQuickParentChange); + d->doChange(d->parent); +} + +bool QQuickParentChange::isReversable() +{ + return true; +} + +void QQuickParentChange::reverse(Reason) +{ + Q_D(QQuickParentChange); + d->doChange(d->origParent, d->origStackBefore); +} + +QString QQuickParentChange::typeName() const +{ + return QLatin1String("ParentChange"); +} + +bool QQuickParentChange::override(QDeclarativeActionEvent*other) +{ + Q_D(QQuickParentChange); + if (other->typeName() != QLatin1String("ParentChange")) + return false; + if (QQuickParentChange *otherPC = static_cast(other)) + return (d->target == otherPC->object()); + return false; +} + +void QQuickParentChange::saveCurrentValues() +{ + Q_D(QQuickParentChange); + if (!d->target) { + d->rewindParent = 0; + d->rewindStackBefore = 0; + return; + } + + d->rewindParent = d->target->parentItem(); + d->rewindStackBefore = 0; + + if (!d->rewindParent) + return; + + QList children = d->rewindParent->childItems(); + for (int ii = 0; ii < children.count() - 1; ++ii) { + if (children.at(ii) == d->target) { + d->rewindStackBefore = children.at(ii + 1); + break; + } + } +} + +void QQuickParentChange::rewind() +{ + Q_D(QQuickParentChange); + d->doChange(d->rewindParent, d->rewindStackBefore); +} + +class QQuickAnchorSetPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickAnchorSet) +public: + QQuickAnchorSetPrivate() + : usedAnchors(0), resetAnchors(0), fill(0), + centerIn(0)/*, leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), + margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)*/ + { + } + + QQuickAnchors::Anchors usedAnchors; + QQuickAnchors::Anchors resetAnchors; + + QQuickItem *fill; + QQuickItem *centerIn; + + QDeclarativeScriptString leftScript; + QDeclarativeScriptString rightScript; + QDeclarativeScriptString topScript; + QDeclarativeScriptString bottomScript; + QDeclarativeScriptString hCenterScript; + QDeclarativeScriptString vCenterScript; + QDeclarativeScriptString baselineScript; + + /*qreal leftMargin; + qreal rightMargin; + qreal topMargin; + qreal bottomMargin; + qreal margins; + qreal vCenterOffset; + qreal hCenterOffset; + qreal baselineOffset;*/ +}; + +QQuickAnchorSet::QQuickAnchorSet(QObject *parent) + : QObject(*new QQuickAnchorSetPrivate, parent) +{ +} + +QQuickAnchorSet::~QQuickAnchorSet() +{ +} + +QDeclarativeScriptString QQuickAnchorSet::top() const +{ + Q_D(const QQuickAnchorSet); + return d->topScript; +} + +void QQuickAnchorSet::setTop(const QDeclarativeScriptString &edge) +{ + Q_D(QQuickAnchorSet); + d->usedAnchors |= QQuickAnchors::TopAnchor; + d->topScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetTop(); +} + +void QQuickAnchorSet::resetTop() +{ + Q_D(QQuickAnchorSet); + d->usedAnchors &= ~QQuickAnchors::TopAnchor; + d->topScript = QDeclarativeScriptString(); + d->resetAnchors |= QQuickAnchors::TopAnchor; +} + +QDeclarativeScriptString QQuickAnchorSet::bottom() const +{ + Q_D(const QQuickAnchorSet); + return d->bottomScript; +} + +void QQuickAnchorSet::setBottom(const QDeclarativeScriptString &edge) +{ + Q_D(QQuickAnchorSet); + d->usedAnchors |= QQuickAnchors::BottomAnchor; + d->bottomScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBottom(); +} + +void QQuickAnchorSet::resetBottom() +{ + Q_D(QQuickAnchorSet); + d->usedAnchors &= ~QQuickAnchors::BottomAnchor; + d->bottomScript = QDeclarativeScriptString(); + d->resetAnchors |= QQuickAnchors::BottomAnchor; +} + +QDeclarativeScriptString QQuickAnchorSet::verticalCenter() const +{ + Q_D(const QQuickAnchorSet); + return d->vCenterScript; +} + +void QQuickAnchorSet::setVerticalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QQuickAnchorSet); + d->usedAnchors |= QQuickAnchors::VCenterAnchor; + d->vCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetVerticalCenter(); +} + +void QQuickAnchorSet::resetVerticalCenter() +{ + Q_D(QQuickAnchorSet); + d->usedAnchors &= ~QQuickAnchors::VCenterAnchor; + d->vCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QQuickAnchors::VCenterAnchor; +} + +QDeclarativeScriptString QQuickAnchorSet::baseline() const +{ + Q_D(const QQuickAnchorSet); + return d->baselineScript; +} + +void QQuickAnchorSet::setBaseline(const QDeclarativeScriptString &edge) +{ + Q_D(QQuickAnchorSet); + d->usedAnchors |= QQuickAnchors::BaselineAnchor; + d->baselineScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBaseline(); +} + +void QQuickAnchorSet::resetBaseline() +{ + Q_D(QQuickAnchorSet); + d->usedAnchors &= ~QQuickAnchors::BaselineAnchor; + d->baselineScript = QDeclarativeScriptString(); + d->resetAnchors |= QQuickAnchors::BaselineAnchor; +} + +QDeclarativeScriptString QQuickAnchorSet::left() const +{ + Q_D(const QQuickAnchorSet); + return d->leftScript; +} + +void QQuickAnchorSet::setLeft(const QDeclarativeScriptString &edge) +{ + Q_D(QQuickAnchorSet); + d->usedAnchors |= QQuickAnchors::LeftAnchor; + d->leftScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetLeft(); +} + +void QQuickAnchorSet::resetLeft() +{ + Q_D(QQuickAnchorSet); + d->usedAnchors &= ~QQuickAnchors::LeftAnchor; + d->leftScript = QDeclarativeScriptString(); + d->resetAnchors |= QQuickAnchors::LeftAnchor; +} + +QDeclarativeScriptString QQuickAnchorSet::right() const +{ + Q_D(const QQuickAnchorSet); + return d->rightScript; +} + +void QQuickAnchorSet::setRight(const QDeclarativeScriptString &edge) +{ + Q_D(QQuickAnchorSet); + d->usedAnchors |= QQuickAnchors::RightAnchor; + d->rightScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetRight(); +} + +void QQuickAnchorSet::resetRight() +{ + Q_D(QQuickAnchorSet); + d->usedAnchors &= ~QQuickAnchors::RightAnchor; + d->rightScript = QDeclarativeScriptString(); + d->resetAnchors |= QQuickAnchors::RightAnchor; +} + +QDeclarativeScriptString QQuickAnchorSet::horizontalCenter() const +{ + Q_D(const QQuickAnchorSet); + return d->hCenterScript; +} + +void QQuickAnchorSet::setHorizontalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QQuickAnchorSet); + d->usedAnchors |= QQuickAnchors::HCenterAnchor; + d->hCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetHorizontalCenter(); +} + +void QQuickAnchorSet::resetHorizontalCenter() +{ + Q_D(QQuickAnchorSet); + d->usedAnchors &= ~QQuickAnchors::HCenterAnchor; + d->hCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QQuickAnchors::HCenterAnchor; +} + +QQuickItem *QQuickAnchorSet::fill() const +{ + Q_D(const QQuickAnchorSet); + return d->fill; +} + +void QQuickAnchorSet::setFill(QQuickItem *f) +{ + Q_D(QQuickAnchorSet); + d->fill = f; +} + +void QQuickAnchorSet::resetFill() +{ + setFill(0); +} + +QQuickItem *QQuickAnchorSet::centerIn() const +{ + Q_D(const QQuickAnchorSet); + return d->centerIn; +} + +void QQuickAnchorSet::setCenterIn(QQuickItem* c) +{ + Q_D(QQuickAnchorSet); + d->centerIn = c; +} + +void QQuickAnchorSet::resetCenterIn() +{ + setCenterIn(0); +} + + +class QQuickAnchorChangesPrivate : public QDeclarativeStateOperationPrivate +{ +public: + QQuickAnchorChangesPrivate() + : target(0), anchorSet(new QQuickAnchorSet), + leftBinding(0), rightBinding(0), hCenterBinding(0), + topBinding(0), bottomBinding(0), vCenterBinding(0), baselineBinding(0), + origLeftBinding(0), origRightBinding(0), origHCenterBinding(0), + origTopBinding(0), origBottomBinding(0), origVCenterBinding(0), + origBaselineBinding(0) + { + + } + ~QQuickAnchorChangesPrivate() { delete anchorSet; } + + QQuickItem *target; + QQuickAnchorSet *anchorSet; + + QDeclarativeBinding *leftBinding; + QDeclarativeBinding *rightBinding; + QDeclarativeBinding *hCenterBinding; + QDeclarativeBinding *topBinding; + QDeclarativeBinding *bottomBinding; + QDeclarativeBinding *vCenterBinding; + QDeclarativeBinding *baselineBinding; + + QDeclarativeAbstractBinding *origLeftBinding; + QDeclarativeAbstractBinding *origRightBinding; + QDeclarativeAbstractBinding *origHCenterBinding; + QDeclarativeAbstractBinding *origTopBinding; + QDeclarativeAbstractBinding *origBottomBinding; + QDeclarativeAbstractBinding *origVCenterBinding; + QDeclarativeAbstractBinding *origBaselineBinding; + + QQuickAnchorLine rewindLeft; + QQuickAnchorLine rewindRight; + QQuickAnchorLine rewindHCenter; + QQuickAnchorLine rewindTop; + QQuickAnchorLine rewindBottom; + QQuickAnchorLine rewindVCenter; + QQuickAnchorLine rewindBaseline; + + qreal fromX; + qreal fromY; + qreal fromWidth; + qreal fromHeight; + + qreal toX; + qreal toY; + qreal toWidth; + qreal toHeight; + + qreal rewindX; + qreal rewindY; + qreal rewindWidth; + qreal rewindHeight; + + bool applyOrigLeft; + bool applyOrigRight; + bool applyOrigHCenter; + bool applyOrigTop; + bool applyOrigBottom; + bool applyOrigVCenter; + bool applyOrigBaseline; + + QDeclarativeNullableValue origWidth; + QDeclarativeNullableValue origHeight; + qreal origX; + qreal origY; + + QList oldBindings; + + QDeclarativeProperty leftProp; + QDeclarativeProperty rightProp; + QDeclarativeProperty hCenterProp; + QDeclarativeProperty topProp; + QDeclarativeProperty bottomProp; + QDeclarativeProperty vCenterProp; + QDeclarativeProperty baselineProp; +}; + +QQuickAnchorChanges::QQuickAnchorChanges(QObject *parent) + : QDeclarativeStateOperation(*(new QQuickAnchorChangesPrivate), parent) +{ +} + +QQuickAnchorChanges::~QQuickAnchorChanges() +{ +} + +QQuickAnchorChanges::ActionList QQuickAnchorChanges::actions() +{ + Q_D(QQuickAnchorChanges); + d->leftBinding = d->rightBinding = d->hCenterBinding = d->topBinding + = d->bottomBinding = d->vCenterBinding = d->baselineBinding = 0; + + d->leftProp = QDeclarativeProperty(d->target, QLatin1String("anchors.left")); + d->rightProp = QDeclarativeProperty(d->target, QLatin1String("anchors.right")); + d->hCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.horizontalCenter")); + d->topProp = QDeclarativeProperty(d->target, QLatin1String("anchors.top")); + d->bottomProp = QDeclarativeProperty(d->target, QLatin1String("anchors.bottom")); + d->vCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.verticalCenter")); + d->baselineProp = QDeclarativeProperty(d->target, QLatin1String("anchors.baseline")); + + if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::LeftAnchor) { + d->leftBinding = new QDeclarativeBinding(d->anchorSet->d_func()->leftScript.script(), d->target, qmlContext(this)); + d->leftBinding->setTarget(d->leftProp); + } + if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::RightAnchor) { + d->rightBinding = new QDeclarativeBinding(d->anchorSet->d_func()->rightScript.script(), d->target, qmlContext(this)); + d->rightBinding->setTarget(d->rightProp); + } + if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::HCenterAnchor) { + d->hCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->hCenterScript.script(), d->target, qmlContext(this)); + d->hCenterBinding->setTarget(d->hCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::TopAnchor) { + d->topBinding = new QDeclarativeBinding(d->anchorSet->d_func()->topScript.script(), d->target, qmlContext(this)); + d->topBinding->setTarget(d->topProp); + } + if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BottomAnchor) { + d->bottomBinding = new QDeclarativeBinding(d->anchorSet->d_func()->bottomScript.script(), d->target, qmlContext(this)); + d->bottomBinding->setTarget(d->bottomProp); + } + if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::VCenterAnchor) { + d->vCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->vCenterScript.script(), d->target, qmlContext(this)); + d->vCenterBinding->setTarget(d->vCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BaselineAnchor) { + d->baselineBinding = new QDeclarativeBinding(d->anchorSet->d_func()->baselineScript.script(), d->target, qmlContext(this)); + d->baselineBinding->setTarget(d->baselineProp); + } + + QDeclarativeAction a; + a.event = this; + return ActionList() << a; +} + +QQuickAnchorSet *QQuickAnchorChanges::anchors() +{ + Q_D(QQuickAnchorChanges); + return d->anchorSet; +} + +QQuickItem *QQuickAnchorChanges::object() const +{ + Q_D(const QQuickAnchorChanges); + return d->target; +} + +void QQuickAnchorChanges::setObject(QQuickItem *target) +{ + Q_D(QQuickAnchorChanges); + d->target = target; +} + +void QQuickAnchorChanges::execute(Reason reason) +{ + Q_D(QQuickAnchorChanges); + if (!d->target) + return; + + QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); + //incorporate any needed "reverts" + if (d->applyOrigLeft) { + if (!d->origLeftBinding) + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + } + if (d->applyOrigRight) { + if (!d->origRightBinding) + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + } + if (d->applyOrigHCenter) { + if (!d->origHCenterBinding) + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + } + if (d->applyOrigTop) { + if (!d->origTopBinding) + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); + } + if (d->applyOrigBottom) { + if (!d->origBottomBinding) + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + } + if (d->applyOrigVCenter) { + if (!d->origVCenterBinding) + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + } + if (d->applyOrigBaseline) { + if (!d->origBaselineBinding) + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + } + + //destroy old bindings + if (reason == ActualChange) { + for (int i = 0; i < d->oldBindings.size(); ++i) { + QDeclarativeAbstractBinding *binding = d->oldBindings.at(i); + if (binding) + binding->destroy(); + } + d->oldBindings.clear(); + } + + //reset any anchors that have been specified as "undefined" + if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::LeftAnchor) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::RightAnchor) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::HCenterAnchor) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::TopAnchor) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::BottomAnchor) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::VCenterAnchor) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::BaselineAnchor) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); + } + + //set any anchors that have been specified + if (d->leftBinding) + QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), d->leftBinding); + if (d->rightBinding) + QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), d->rightBinding); + if (d->hCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), d->hCenterBinding); + if (d->topBinding) + QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), d->topBinding); + if (d->bottomBinding) + QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), d->bottomBinding); + if (d->vCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), d->vCenterBinding); + if (d->baselineBinding) + QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), d->baselineBinding); +} + +bool QQuickAnchorChanges::isReversable() +{ + return true; +} + +void QQuickAnchorChanges::reverse(Reason reason) +{ + Q_D(QQuickAnchorChanges); + if (!d->target) + return; + + QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); + //reset any anchors set by the state + if (d->leftBinding) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), 0); + if (reason == ActualChange) { + d->leftBinding->destroy(); d->leftBinding = 0; + } + } + if (d->rightBinding) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), 0); + if (reason == ActualChange) { + d->rightBinding->destroy(); d->rightBinding = 0; + } + } + if (d->hCenterBinding) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), 0); + if (reason == ActualChange) { + d->hCenterBinding->destroy(); d->hCenterBinding = 0; + } + } + if (d->topBinding) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), 0); + if (reason == ActualChange) { + d->topBinding->destroy(); d->topBinding = 0; + } + } + if (d->bottomBinding) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), 0); + if (reason == ActualChange) { + d->bottomBinding->destroy(); d->bottomBinding = 0; + } + } + if (d->vCenterBinding) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), 0); + if (reason == ActualChange) { + d->vCenterBinding->destroy(); d->vCenterBinding = 0; + } + } + if (d->baselineBinding) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), 0); + if (reason == ActualChange) { + d->baselineBinding->destroy(); d->baselineBinding = 0; + } + } + + //restore previous anchors + if (d->origLeftBinding) + QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + if (d->origRightBinding) + QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + if (d->origHCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + if (d->origTopBinding) + QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); + if (d->origBottomBinding) + QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + if (d->origVCenterBinding) + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + if (d->origBaselineBinding) + QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + + //restore any absolute geometry changed by the state's anchors + QQuickAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Vertical_Mask; + QQuickAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Vertical_Mask; + QQuickAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Horizontal_Mask; + QQuickAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Horizontal_Mask; + + bool stateSetWidth = (stateHAnchors && + stateHAnchors != QQuickAnchors::LeftAnchor && + stateHAnchors != QQuickAnchors::RightAnchor && + stateHAnchors != QQuickAnchors::HCenterAnchor); + bool origSetWidth = (origHAnchors && + origHAnchors != QQuickAnchors::LeftAnchor && + origHAnchors != QQuickAnchors::RightAnchor && + origHAnchors != QQuickAnchors::HCenterAnchor); + if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) + d->target->setWidth(d->origWidth.value); + + bool stateSetHeight = (stateVAnchors && + stateVAnchors != QQuickAnchors::TopAnchor && + stateVAnchors != QQuickAnchors::BottomAnchor && + stateVAnchors != QQuickAnchors::VCenterAnchor && + stateVAnchors != QQuickAnchors::BaselineAnchor); + bool origSetHeight = (origVAnchors && + origVAnchors != QQuickAnchors::TopAnchor && + origVAnchors != QQuickAnchors::BottomAnchor && + origVAnchors != QQuickAnchors::VCenterAnchor && + origVAnchors != QQuickAnchors::BaselineAnchor); + if (d->origHeight.isValid() && stateSetHeight && !origSetHeight) + d->target->setHeight(d->origHeight.value); + + if (stateHAnchors && !origHAnchors) + d->target->setX(d->origX); + + if (stateVAnchors && !origVAnchors) + d->target->setY(d->origY); +} + +QString QQuickAnchorChanges::typeName() const +{ + return QLatin1String("AnchorChanges"); +} + +QList QQuickAnchorChanges::additionalActions() +{ + Q_D(QQuickAnchorChanges); + QList extra; + + QQuickAnchors::Anchors combined = d->anchorSet->d_func()->usedAnchors | d->anchorSet->d_func()->resetAnchors; + bool hChange = combined & QQuickAnchors::Horizontal_Mask; + bool vChange = combined & QQuickAnchors::Vertical_Mask; + + if (d->target) { + QDeclarativeAction a; + if (hChange && d->fromX != d->toX) { + a.property = QDeclarativeProperty(d->target, QLatin1String("x")); + a.toValue = d->toX; + extra << a; + } + if (vChange && d->fromY != d->toY) { + a.property = QDeclarativeProperty(d->target, QLatin1String("y")); + a.toValue = d->toY; + extra << a; + } + if (hChange && d->fromWidth != d->toWidth) { + a.property = QDeclarativeProperty(d->target, QLatin1String("width")); + a.toValue = d->toWidth; + extra << a; + } + if (vChange && d->fromHeight != d->toHeight) { + a.property = QDeclarativeProperty(d->target, QLatin1String("height")); + a.toValue = d->toHeight; + extra << a; + } + } + + return extra; +} + +bool QQuickAnchorChanges::changesBindings() +{ + return true; +} + +void QQuickAnchorChanges::saveOriginals() +{ + Q_D(QQuickAnchorChanges); + if (!d->target) + return; + + d->origLeftBinding = QDeclarativePropertyPrivate::binding(d->leftProp); + d->origRightBinding = QDeclarativePropertyPrivate::binding(d->rightProp); + d->origHCenterBinding = QDeclarativePropertyPrivate::binding(d->hCenterProp); + d->origTopBinding = QDeclarativePropertyPrivate::binding(d->topProp); + d->origBottomBinding = QDeclarativePropertyPrivate::binding(d->bottomProp); + d->origVCenterBinding = QDeclarativePropertyPrivate::binding(d->vCenterProp); + d->origBaselineBinding = QDeclarativePropertyPrivate::binding(d->baselineProp); + + QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); + if (targetPrivate->widthValid) + d->origWidth = d->target->width(); + if (targetPrivate->heightValid) + d->origHeight = d->target->height(); + d->origX = d->target->x(); + d->origY = d->target->y(); + + d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop + = d->applyOrigBottom = d->applyOrigVCenter = d->applyOrigBaseline = false; + + saveCurrentValues(); +} + +void QQuickAnchorChanges::copyOriginals(QDeclarativeActionEvent *other) +{ + Q_D(QQuickAnchorChanges); + QQuickAnchorChanges *ac = static_cast(other); + QQuickAnchorChangesPrivate *acp = ac->d_func(); + + QQuickAnchors::Anchors combined = acp->anchorSet->d_func()->usedAnchors | + acp->anchorSet->d_func()->resetAnchors; + + //probably also need to revert some things + d->applyOrigLeft = (combined & QQuickAnchors::LeftAnchor); + d->applyOrigRight = (combined & QQuickAnchors::RightAnchor); + d->applyOrigHCenter = (combined & QQuickAnchors::HCenterAnchor); + d->applyOrigTop = (combined & QQuickAnchors::TopAnchor); + d->applyOrigBottom = (combined & QQuickAnchors::BottomAnchor); + d->applyOrigVCenter = (combined & QQuickAnchors::VCenterAnchor); + d->applyOrigBaseline = (combined & QQuickAnchors::BaselineAnchor); + + d->origLeftBinding = acp->origLeftBinding; + d->origRightBinding = acp->origRightBinding; + d->origHCenterBinding = acp->origHCenterBinding; + d->origTopBinding = acp->origTopBinding; + d->origBottomBinding = acp->origBottomBinding; + d->origVCenterBinding = acp->origVCenterBinding; + d->origBaselineBinding = acp->origBaselineBinding; + + d->origWidth = acp->origWidth; + d->origHeight = acp->origHeight; + d->origX = acp->origX; + d->origY = acp->origY; + + d->oldBindings.clear(); + d->oldBindings << acp->leftBinding << acp->rightBinding << acp->hCenterBinding + << acp->topBinding << acp->bottomBinding << acp->baselineBinding; + + saveCurrentValues(); +} + +void QQuickAnchorChanges::clearBindings() +{ + Q_D(QQuickAnchorChanges); + if (!d->target) + return; + + //### should this (saving "from" values) be moved to saveCurrentValues()? + d->fromX = d->target->x(); + d->fromY = d->target->y(); + d->fromWidth = d->target->width(); + d->fromHeight = d->target->height(); + + QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); + //reset any anchors with corresponding reverts + //reset any anchors that have been specified as "undefined" + //reset any anchors that we'll be setting in the state + QQuickAnchors::Anchors combined = d->anchorSet->d_func()->resetAnchors | + d->anchorSet->d_func()->usedAnchors; + if (d->applyOrigLeft || (combined & QQuickAnchors::LeftAnchor)) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->applyOrigRight || (combined & QQuickAnchors::RightAnchor)) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->applyOrigHCenter || (combined & QQuickAnchors::HCenterAnchor)) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->applyOrigTop || (combined & QQuickAnchors::TopAnchor)) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->applyOrigBottom || (combined & QQuickAnchors::BottomAnchor)) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->applyOrigVCenter || (combined & QQuickAnchors::VCenterAnchor)) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->applyOrigBaseline || (combined & QQuickAnchors::BaselineAnchor)) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); + } +} + +bool QQuickAnchorChanges::override(QDeclarativeActionEvent*other) +{ + if (other->typeName() != QLatin1String("AnchorChanges")) + return false; + if (static_cast(this) == other) + return true; + if (static_cast(other)->object() == object()) + return true; + return false; +} + +void QQuickAnchorChanges::rewind() +{ + Q_D(QQuickAnchorChanges); + if (!d->target) + return; + + QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); + + //restore previous values (but not previous bindings, i.e. anchors) + d->target->setX(d->rewindX); + d->target->setY(d->rewindY); + if (targetPrivate->widthValid) { + d->target->setWidth(d->rewindWidth); + } + if (targetPrivate->heightValid) { + d->target->setHeight(d->rewindHeight); + } +} + +void QQuickAnchorChanges::saveCurrentValues() +{ + Q_D(QQuickAnchorChanges); + if (!d->target) + return; + + QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); + d->rewindLeft = targetPrivate->anchors()->left(); + d->rewindRight = targetPrivate->anchors()->right(); + d->rewindHCenter = targetPrivate->anchors()->horizontalCenter(); + d->rewindTop = targetPrivate->anchors()->top(); + d->rewindBottom = targetPrivate->anchors()->bottom(); + d->rewindVCenter = targetPrivate->anchors()->verticalCenter(); + d->rewindBaseline = targetPrivate->anchors()->baseline(); + + d->rewindX = d->target->x(); + d->rewindY = d->target->y(); + d->rewindWidth = d->target->width(); + d->rewindHeight = d->target->height(); +} + +void QQuickAnchorChanges::saveTargetValues() +{ + Q_D(QQuickAnchorChanges); + if (!d->target) + return; + + d->toX = d->target->x(); + d->toY = d->target->y(); + d->toWidth = d->target->width(); + d->toHeight = d->target->height(); +} + +#include + +QT_END_NAMESPACE + diff --git a/src/declarative/items/qquickstateoperations_p.h b/src/declarative/items/qquickstateoperations_p.h new file mode 100644 index 0000000000..7844c6d7dd --- /dev/null +++ b/src/declarative/items/qquickstateoperations_p.h @@ -0,0 +1,275 @@ +// Commit: 84c47bbb133304d7ef35642fa1fbb17619d4a43d +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTATEOPERATIONS_P_H +#define QQUICKSTATEOPERATIONS_P_H + +#include "qquickitem.h" +#include "qquickanchors_p.h" + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickParentChangePrivate; +class Q_AUTOTEST_EXPORT QQuickParentChange : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickParentChange) + + Q_PROPERTY(QQuickItem *target READ object WRITE setObject) + Q_PROPERTY(QQuickItem *parent READ parent WRITE setParent) + Q_PROPERTY(QDeclarativeScriptString x READ x WRITE setX) + Q_PROPERTY(QDeclarativeScriptString y READ y WRITE setY) + Q_PROPERTY(QDeclarativeScriptString width READ width WRITE setWidth) + Q_PROPERTY(QDeclarativeScriptString height READ height WRITE setHeight) + Q_PROPERTY(QDeclarativeScriptString scale READ scale WRITE setScale) + Q_PROPERTY(QDeclarativeScriptString rotation READ rotation WRITE setRotation) +public: + QQuickParentChange(QObject *parent=0); + ~QQuickParentChange(); + + QQuickItem *object() const; + void setObject(QQuickItem *); + + QQuickItem *parent() const; + void setParent(QQuickItem *); + + QQuickItem *originalParent() const; + + QDeclarativeScriptString x() const; + void setX(QDeclarativeScriptString x); + bool xIsSet() const; + + QDeclarativeScriptString y() const; + void setY(QDeclarativeScriptString y); + bool yIsSet() const; + + QDeclarativeScriptString width() const; + void setWidth(QDeclarativeScriptString width); + bool widthIsSet() const; + + QDeclarativeScriptString height() const; + void setHeight(QDeclarativeScriptString height); + bool heightIsSet() const; + + QDeclarativeScriptString scale() const; + void setScale(QDeclarativeScriptString scale); + bool scaleIsSet() const; + + QDeclarativeScriptString rotation() const; + void setRotation(QDeclarativeScriptString rotation); + bool rotationIsSet() const; + + virtual ActionList actions(); + + virtual void saveOriginals(); + //virtual void copyOriginals(QDeclarativeActionEvent*); + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarativeActionEvent*other); + virtual void rewind(); + virtual void saveCurrentValues(); +}; + +class QQuickAnchorChanges; +class QQuickAnchorSetPrivate; +class Q_AUTOTEST_EXPORT QQuickAnchorSet : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeScriptString left READ left WRITE setLeft RESET resetLeft) + Q_PROPERTY(QDeclarativeScriptString right READ right WRITE setRight RESET resetRight) + Q_PROPERTY(QDeclarativeScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter) + Q_PROPERTY(QDeclarativeScriptString top READ top WRITE setTop RESET resetTop) + Q_PROPERTY(QDeclarativeScriptString bottom READ bottom WRITE setBottom RESET resetBottom) + Q_PROPERTY(QDeclarativeScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter) + Q_PROPERTY(QDeclarativeScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline) + //Q_PROPERTY(QQuickItem *fill READ fill WRITE setFill RESET resetFill) + //Q_PROPERTY(QQuickItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn) + + /*Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged()) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) + Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged()) + Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged())*/ + +public: + QQuickAnchorSet(QObject *parent=0); + virtual ~QQuickAnchorSet(); + + QDeclarativeScriptString left() const; + void setLeft(const QDeclarativeScriptString &edge); + void resetLeft(); + + QDeclarativeScriptString right() const; + void setRight(const QDeclarativeScriptString &edge); + void resetRight(); + + QDeclarativeScriptString horizontalCenter() const; + void setHorizontalCenter(const QDeclarativeScriptString &edge); + void resetHorizontalCenter(); + + QDeclarativeScriptString top() const; + void setTop(const QDeclarativeScriptString &edge); + void resetTop(); + + QDeclarativeScriptString bottom() const; + void setBottom(const QDeclarativeScriptString &edge); + void resetBottom(); + + QDeclarativeScriptString verticalCenter() const; + void setVerticalCenter(const QDeclarativeScriptString &edge); + void resetVerticalCenter(); + + QDeclarativeScriptString baseline() const; + void setBaseline(const QDeclarativeScriptString &edge); + void resetBaseline(); + + QQuickItem *fill() const; + void setFill(QQuickItem *); + void resetFill(); + + QQuickItem *centerIn() const; + void setCenterIn(QQuickItem *); + void resetCenterIn(); + + /*qreal leftMargin() const; + void setLeftMargin(qreal); + + qreal rightMargin() const; + void setRightMargin(qreal); + + qreal horizontalCenterOffset() const; + void setHorizontalCenterOffset(qreal); + + qreal topMargin() const; + void setTopMargin(qreal); + + qreal bottomMargin() const; + void setBottomMargin(qreal); + + qreal margins() const; + void setMargins(qreal); + + qreal verticalCenterOffset() const; + void setVerticalCenterOffset(qreal); + + qreal baselineOffset() const; + void setBaselineOffset(qreal);*/ + + QQuickAnchors::Anchors usedAnchors() const; + +/*Q_SIGNALS: + void leftMarginChanged(); + void rightMarginChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void marginsChanged(); + void verticalCenterOffsetChanged(); + void horizontalCenterOffsetChanged(); + void baselineOffsetChanged();*/ + +private: + friend class QQuickAnchorChanges; + Q_DISABLE_COPY(QQuickAnchorSet) + Q_DECLARE_PRIVATE(QQuickAnchorSet) +}; + +class QQuickAnchorChangesPrivate; +class Q_AUTOTEST_EXPORT QQuickAnchorChanges : public QDeclarativeStateOperation, public QDeclarativeActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickAnchorChanges) + + Q_PROPERTY(QQuickItem *target READ object WRITE setObject) + Q_PROPERTY(QQuickAnchorSet *anchors READ anchors CONSTANT) + +public: + QQuickAnchorChanges(QObject *parent=0); + ~QQuickAnchorChanges(); + + virtual ActionList actions(); + + QQuickAnchorSet *anchors(); + + QQuickItem *object() const; + void setObject(QQuickItem *); + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarativeActionEvent*other); + virtual bool changesBindings(); + virtual void saveOriginals(); + virtual bool needsCopy() { return true; } + virtual void copyOriginals(QDeclarativeActionEvent*); + virtual void clearBindings(); + virtual void rewind(); + virtual void saveCurrentValues(); + + QList additionalActions(); + virtual void saveTargetValues(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickParentChange) +QML_DECLARE_TYPE(QQuickAnchorSet) +QML_DECLARE_TYPE(QQuickAnchorChanges) + +QT_END_HEADER + +#endif // QQUICKSTATEOPERATIONS_P_H + diff --git a/src/declarative/items/qquicktext.cpp b/src/declarative/items/qquicktext.cpp new file mode 100644 index 0000000000..bf736e6a10 --- /dev/null +++ b/src/declarative/items/qquicktext.cpp @@ -0,0 +1,1938 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktext_p.h" +#include "qquicktext_p_p.h" + +#include +#include +#include +#include "qquicktextnode_p.h" +#include "qquickimage_p_p.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + +class QQuickTextDocumentWithImageResources : public QTextDocument { + Q_OBJECT + +public: + QQuickTextDocumentWithImageResources(QQuickText *parent); + virtual ~QQuickTextDocumentWithImageResources(); + + void setText(const QString &); + int resourcesLoading() const { return outstanding; } + +protected: + QVariant loadResource(int type, const QUrl &name); + +private slots: + void requestFinished(); + +private: + QHash m_resources; + + int outstanding; + static QSet errors; +}; + +DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) +DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE); + +QString QQuickTextPrivate::elideChar = QString(0x2026); + +QQuickTextPrivate::QQuickTextPrivate() +: color((QRgb)0), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft), + vAlign(QQuickText::AlignTop), elideMode(QQuickText::ElideNone), + format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap), lineHeight(1), + lineHeightMode(QQuickText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX), + maximumLineCountValid(false), + texture(0), + imageCacheDirty(false), updateOnComponentComplete(true), + richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false), + requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false), + layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true), + naturalWidth(0), doc(0), textLine(0), nodeType(NodeIsNull) + +#if defined(Q_OS_MAC) +, layoutThread(0), paintingThread(0) +#endif + +{ + cacheAllTextAsImage = enableImageCache(); +} + +void QQuickTextPrivate::init() +{ + Q_Q(QQuickText); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QQuickItem::ItemHasContents); +} + +QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickText *parent) +: QTextDocument(parent), outstanding(0) +{ + setUndoRedoEnabled(false); +} + +QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources() +{ + if (!m_resources.isEmpty()) + qDeleteAll(m_resources); +} + +QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name) +{ + QDeclarativeContext *context = qmlContext(parent()); + QUrl url = context->resolvedUrl(name); + + if (type == QTextDocument::ImageResource) { + QHash::Iterator iter = m_resources.find(url); + + if (iter == m_resources.end()) { + QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url); + iter = m_resources.insert(name, p); + + if (p->isLoading()) { + p->connectFinished(this, SLOT(requestFinished())); + outstanding++; + } + } + + QDeclarativePixmap *p = *iter; + if (p->isReady()) { + return p->pixmap(); + } else if (p->isError()) { + if (!errors.contains(url)) { + errors.insert(url); + qmlInfo(parent()) << p->error(); + } + } + } + + return QTextDocument::loadResource(type,url); // The *resolved* URL +} + +void QQuickTextDocumentWithImageResources::requestFinished() +{ + outstanding--; + if (outstanding == 0) { + QQuickText *textItem = static_cast(parent()); + QString text = textItem->text(); +#ifndef QT_NO_TEXTHTMLPARSER + setHtml(text); +#else + setPlainText(text); +#endif + QQuickTextPrivate *d = QQuickTextPrivate::get(textItem); + d->updateLayout(); + } +} + +void QQuickTextDocumentWithImageResources::setText(const QString &text) +{ + if (!m_resources.isEmpty()) { + qDeleteAll(m_resources); + m_resources.clear(); + outstanding = 0; + } + +#ifndef QT_NO_TEXTHTMLPARSER + setHtml(text); +#else + setPlainText(text); +#endif +} + +QSet QQuickTextDocumentWithImageResources::errors; + +QQuickTextPrivate::~QQuickTextPrivate() +{ + delete textLine; textLine = 0; +} + +qreal QQuickTextPrivate::getImplicitWidth() const +{ + if (!requireImplicitWidth) { + // We don't calculate implicitWidth unless it is required. + // We need to force a size update now to ensure implicitWidth is calculated + QQuickTextPrivate *me = const_cast(this); + me->requireImplicitWidth = true; + me->updateSize(); + } + return implicitWidth; +} + +void QQuickTextPrivate::updateLayout() +{ + Q_Q(QQuickText); + if (!q->isComponentComplete()) { + updateOnComponentComplete = true; + return; + } + + layoutTextElided = false; + // Setup instance of QTextLayout for all cases other than richtext + if (!richText) { + layout.clearLayout(); + layout.setFont(font); + if (!styledText) { + QString tmp = text; + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + singleline = !tmp.contains(QChar::LineSeparator); + if (singleline && !maximumLineCountValid && elideMode != QQuickText::ElideNone && q->widthValid()) { + QFontMetrics fm(font); + tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); + if (tmp != text) { + layoutTextElided = true; + if (!truncated) { + truncated = true; + emit q->truncatedChanged(); + } + } + } + layout.setText(tmp); + } else { + singleline = false; + if (textHasChanged) { + QDeclarativeStyledText::parse(text, layout); + textHasChanged = false; + } + } + } else { + ensureDoc(); + QTextBlockFormat::LineHeightTypes type; + type = lineHeightMode == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight; + QTextBlockFormat blockFormat; + blockFormat.setLineHeight((lineHeightMode == QQuickText::FixedHeight ? lineHeight : lineHeight * 100), type); + for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) { + QTextCursor cursor(it); + cursor.mergeBlockFormat(blockFormat); + } + } + + updateSize(); +} + +void QQuickTextPrivate::updateSize() +{ + Q_Q(QQuickText); + + if (!q->isComponentComplete()) { + updateOnComponentComplete = true; + return; + } + + if (!requireImplicitWidth) { + emit q->implicitWidthChanged(); + // if the implicitWidth is used, then updateSize() has already been called (recursively) + if (requireImplicitWidth) + return; + } + + invalidateImageCache(); + + QFontMetrics fm(font); + if (text.isEmpty()) { + q->setImplicitWidth(0); + q->setImplicitHeight(fm.height()); + paintedSize = QSize(0, fm.height()); + emit q->paintedSizeChanged(); + q->update(); + return; + } + + int dy = q->height(); + QSize size(0, 0); + +#if defined(Q_OS_MAC) + layoutThread = QThread::currentThread(); +#endif + + //setup instance of QTextLayout for all cases other than richtext + if (!richText) { + QRect textRect = setupTextLayout(); + layedOutTextRect = textRect; + size = textRect.size(); + dy -= size.height(); + } else { + singleline = false; // richtext can't elide or be optimized for single-line case + ensureDoc(); + doc->setDefaultFont(font); + QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QQuickText::AlignLeft) + horizontalAlignment = QQuickText::AlignRight; + else if (horizontalAlignment == QQuickText::AlignRight) + horizontalAlignment = QQuickText::AlignLeft; + } + QTextOption option; + option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign)); + option.setWrapMode(QTextOption::WrapMode(wrapMode)); + if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField()) + option.setUseDesignMetrics(true); + doc->setDefaultTextOption(option); + if (requireImplicitWidth && q->widthValid()) { + doc->setTextWidth(-1); + naturalWidth = doc->idealWidth(); + } + if (wrapMode != QQuickText::NoWrap && q->widthValid()) + doc->setTextWidth(q->width()); + else + doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug) + dy -= (int)doc->size().height(); + QSize dsize = doc->size().toSize(); + layedOutTextRect = QRect(QPoint(0,0), dsize); + size = QSize(int(doc->idealWidth()),dsize.height()); + } + int yoff = 0; + + if (q->heightValid()) { + if (vAlign == QQuickText::AlignBottom) + yoff = dy; + else if (vAlign == QQuickText::AlignVCenter) + yoff = dy/2; + } + q->setBaselineOffset(fm.ascent() + yoff); + + //### need to comfirm cost of always setting these for richText + internalWidthUpdate = true; + if (!q->widthValid()) + q->setImplicitWidth(size.width()); + else if (requireImplicitWidth) + q->setImplicitWidth(naturalWidth); + internalWidthUpdate = false; + + q->setImplicitHeight(size.height()); + if (paintedSize != size) { + paintedSize = size; + emit q->paintedSizeChanged(); + } + q->update(); +} + +QQuickTextLine::QQuickTextLine() + : QObject(), m_line(0), m_height(0) +{ +} + +void QQuickTextLine::setLine(QTextLine *line) +{ + m_line = line; +} + +int QQuickTextLine::number() const +{ + if (m_line) + return m_line->lineNumber(); + return 0; +} + +qreal QQuickTextLine::width() const +{ + if (m_line) + return m_line->width(); + return 0; +} + +void QQuickTextLine::setWidth(qreal width) +{ + if (m_line) + m_line->setLineWidth(width); +} + +qreal QQuickTextLine::height() const +{ + if (m_height) + return m_height; + if (m_line) + return m_line->height(); + return 0; +} + +void QQuickTextLine::setHeight(qreal height) +{ + if (m_line) + m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height)); + m_height = height; +} + +qreal QQuickTextLine::x() const +{ + if (m_line) + return m_line->x(); + return 0; +} + +void QQuickTextLine::setX(qreal x) +{ + if (m_line) + m_line->setPosition(QPointF(x, m_line->y())); +} + +qreal QQuickTextLine::y() const +{ + if (m_line) + return m_line->y(); + return 0; +} + +void QQuickTextLine::setY(qreal y) +{ + if (m_line) + m_line->setPosition(QPointF(m_line->x(), y)); +} + +void QQuickText::doLayout() +{ + Q_D(QQuickText); + d->updateSize(); +} + +/*! + \qmlsignal QtQuick2::Text::onLineLaidOut(line) + + This handler is called for every line during the layout process. + This gives the opportunity to position and resize a line as it is being laid out. + It can for example be used to create columns or lay out text around objects. + + The properties of a line are: + \list + \o number (read-only) + \o x + \o y + \o width + \o height + \endlist + + For example, this will move the first 5 lines of a text element by 100 pixels to the right: + \code + onLineLaidOut: { + if (line.number < 5) { + line.x = line.x + 100 + line.width = line.width - 100 + } + } + \endcode +*/ + +bool QQuickTextPrivate::isLineLaidOutConnected() +{ + static int idx = this->signalIndex("lineLaidOut(QQuickTextLine*)"); + return this->isSignalConnected(idx); +} + +void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, qreal elideWidth = 0) +{ + Q_Q(QQuickText); + +#if defined(Q_OS_MAC) + if (QThread::currentThread() != paintingThread) { +#endif + if (!line.lineNumber()) + linesRects.clear(); + + if (!textLine) + textLine = new QQuickTextLine; + textLine->setLine(&line); + textLine->setY(height); + textLine->setHeight(0); + + // use the text item's width by default if it has one and wrap is on + if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap) + textLine->setWidth(q->width() - elideWidth); + else + textLine->setWidth(INT_MAX); + if (lineHeight != 1.0) + textLine->setHeight((lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight); + + emit q->lineLaidOut(textLine); + + linesRects << QRectF(textLine->x(), textLine->y(), textLine->width(), textLine->height()); + height += textLine->height(); + +#if defined(Q_OS_MAC) + } else { + if (line.lineNumber() < linesRects.count()) { + QRectF r = linesRects.at(line.lineNumber()); + line.setLineWidth(r.width()); + line.setPosition(r.topLeft()); + } + } +#endif +} + +/*! + Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText. + + Returns the size of the final text. This can be used to position the text vertically (the text is + already absolutely positioned horizontally). +*/ +QRect QQuickTextPrivate::setupTextLayout() +{ + // ### text layout handling should be profiled and optimized as needed + // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine); + Q_Q(QQuickText); + layout.setCacheEnabled(true); + + qreal lineWidth = 0; + int visibleCount = 0; + + //set manual width + if (q->widthValid()) + lineWidth = q->width(); + + QTextOption textOption = layout.textOption(); + textOption.setAlignment(Qt::Alignment(q->effectiveHAlign())); + textOption.setWrapMode(QTextOption::WrapMode(wrapMode)); + if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField()) + textOption.setUseDesignMetrics(true); + layout.setTextOption(textOption); + + bool elideText = false; + bool truncate = false; + + QFontMetrics fm(layout.font()); + elidePos = QPointF(); + + if (requireImplicitWidth && q->widthValid()) { + // requires an extra layout + QString elidedText; + if (layoutTextElided) { + // We have provided elided text to the layout, but we must calculate unelided width. + elidedText = layout.text(); + layout.setText(text); + } + layout.beginLayout(); + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + } + layout.endLayout(); + QRectF br; + for (int i = 0; i < layout.lineCount(); ++i) { + QTextLine line = layout.lineAt(i); + br = br.united(line.naturalTextRect()); + } + naturalWidth = br.width(); + if (layoutTextElided) + layout.setText(elidedText); + } + + qreal height = 0; + bool customLayout = isLineLaidOutConnected(); + + if (maximumLineCountValid) { + layout.beginLayout(); + if (!lineWidth) + lineWidth = INT_MAX; + int linesLeft = maximumLineCount; + int visibleTextLength = 0; + while (linesLeft > 0) { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + + visibleCount++; + + if (customLayout) + setupCustomLineGeometry(line, height); + else if (lineWidth) + line.setLineWidth(lineWidth); + visibleTextLength += line.textLength(); + + if (--linesLeft == 0) { + if (visibleTextLength < text.length()) { + truncate = true; + if (elideMode == QQuickText::ElideRight && q->widthValid()) { + qreal elideWidth = fm.width(elideChar); + // Need to correct for alignment + if (customLayout) + setupCustomLineGeometry(line, height, elideWidth); + else + line.setLineWidth(lineWidth - elideWidth); + if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) { + line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y())); + elidePos.setX(line.naturalTextRect().left() - elideWidth); + } else { + elidePos.setX(line.naturalTextRect().right()); + } + elideText = true; + } + } + } + } + layout.endLayout(); + + //Update truncated + if (truncated != truncate) { + truncated = truncate; + emit q->truncatedChanged(); + } + } else { + layout.beginLayout(); + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; + visibleCount++; + if (customLayout) + setupCustomLineGeometry(line, height); + else { + if (lineWidth) + line.setLineWidth(lineWidth); + } + } + layout.endLayout(); + } + + height = 0; + QRectF br; + for (int i = 0; i < layout.lineCount(); ++i) { + QTextLine line = layout.lineAt(i); + // set line spacing + if (!customLayout) + line.setPosition(QPointF(line.position().x(), height)); + if (elideText && i == layout.lineCount()-1) { + elidePos.setY(height + fm.ascent()); + br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent()))); + } + br = br.united(line.naturalTextRect()); + height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight; + } + if (!customLayout) + br.setHeight(height); + + if (!q->widthValid()) + naturalWidth = br.width(); + + //Update the number of visible lines + if (lineCount != visibleCount) { + lineCount = visibleCount; + emit q->lineCountChanged(); + } + + return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height())); +} + +/*! + Returns a painted version of the QQuickTextPrivate::layout QTextLayout. + If \a drawStyle is true, the style color overrides all colors in the document. +*/ +QPixmap QQuickTextPrivate::textLayoutImage(bool drawStyle) +{ + QSize size = layedOutTextRect.size(); + + //paint text + QPixmap img(size); + if (!size.isEmpty()) { + img.fill(Qt::transparent); +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter p(&img); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle); + } + return img; +} + +/*! + Paints the QQuickTextPrivate::layout QTextLayout into \a painter at \a pos. If + \a drawStyle is true, the style color overrides all colors in the document. +*/ +void QQuickTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle) +{ + if (drawStyle) + painter->setPen(styleColor); + else + painter->setPen(color); + painter->setFont(font); + layout.draw(painter, pos); + if (!elidePos.isNull()) + painter->drawText(pos + elidePos, elideChar); +} + +/*! + Returns a painted version of the QQuickTextPrivate::doc QTextDocument. + If \a drawStyle is true, the style color overrides all colors in the document. +*/ +QPixmap QQuickTextPrivate::textDocumentImage(bool drawStyle) +{ + QSize size = doc->size().toSize(); + + //paint text + QPixmap img(size); + img.fill(Qt::transparent); +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter p(&img); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + + QAbstractTextDocumentLayout::PaintContext context; + + QTextOption oldOption(doc->defaultTextOption()); + if (drawStyle) { + context.palette.setColor(QPalette::Text, styleColor); + QTextOption colorOption(doc->defaultTextOption()); + colorOption.setFlags(QTextOption::SuppressColors); + doc->setDefaultTextOption(colorOption); + } else { + context.palette.setColor(QPalette::Text, color); + } + doc->documentLayout()->draw(&p, context); + if (drawStyle) + doc->setDefaultTextOption(oldOption); + return img; +} + +/*! + Mark the image cache as dirty. +*/ +void QQuickTextPrivate::invalidateImageCache() +{ + Q_Q(QQuickText); + + if (richTextAsImage || cacheAllTextAsImage || (qmlDisableDistanceField() && style != QQuickText::Normal)) { // If actually using the image cache + if (imageCacheDirty) + return; + + imageCacheDirty = true; + + if (q->isComponentComplete()) + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } else if (q->isComponentComplete()) + q->update(); +} + +/*! + Tests if the image cache is dirty, and repaints it if it is. +*/ +void QQuickTextPrivate::checkImageCache() +{ + Q_Q(QQuickText); + + if (!imageCacheDirty) + return; + + if (text.isEmpty()) { + + imageCache = QPixmap(); + + } else { + + QPixmap textImage; + QPixmap styledImage; + + if (richText) { + textImage = textDocumentImage(false); + if (style != QQuickText::Normal) + styledImage = textDocumentImage(true); //### should use styleColor + } else { + textImage = textLayoutImage(false); + if (style != QQuickText::Normal) + styledImage = textLayoutImage(true); //### should use styleColor + } + + switch (style) { + case QQuickText::Outline: + imageCache = drawOutline(textImage, styledImage); + break; + case QQuickText::Sunken: + imageCache = drawOutline(textImage, styledImage, -1); + break; + case QQuickText::Raised: + imageCache = drawOutline(textImage, styledImage, 1); + break; + default: + imageCache = textImage; + break; + } + + } + + imageCacheDirty = false; + textureImageCacheDirty = true; + q->update(); +} + +/*! + Ensures the QQuickTextPrivate::doc variable is set to a valid text document +*/ +void QQuickTextPrivate::ensureDoc() +{ + if (!doc) { + Q_Q(QQuickText); + doc = new QQuickTextDocumentWithImageResources(q); + doc->setDocumentMargin(0); + } +} + +/*! + Draw \a styleSource as an outline around \a source and return the new image. +*/ +QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource) +{ + QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); + img.fill(Qt::transparent); + + QPainter ppm(&img); + + QPoint pos(0, 0); + pos += QPoint(-1, 0); + ppm.drawPixmap(pos, styleSource); + pos += QPoint(2, 0); + ppm.drawPixmap(pos, styleSource); + pos += QPoint(-1, -1); + ppm.drawPixmap(pos, styleSource); + pos += QPoint(0, 2); + ppm.drawPixmap(pos, styleSource); + + pos += QPoint(0, -1); + ppm.drawPixmap(pos, source); + ppm.end(); + + return img; +} + +/*! + Draw \a styleSource below \a source at \a yOffset and return the new image. +*/ +QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset) +{ + QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); + img.fill(Qt::transparent); + + QPainter ppm(&img); + + ppm.drawPixmap(QPoint(0, yOffset), styleSource); + ppm.drawPixmap(0, 0, source); + + ppm.end(); + + return img; +} + +/*! + \qmlclass Text QQuickText + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The Text item allows you to add formatted text to a scene. + \inherits Item + + Text items can display both plain and rich text. For example, red text with + a specific font and size can be defined like this: + + \qml + Text { + text: "Hello World!" + font.family: "Helvetica" + font.pointSize: 24 + color: "red" + } + \endqml + + Rich text is defined using HTML-style markup: + + \qml + Text { + text: "Hello World!" + } + \endqml + + \image declarative-text.png + + If height and width are not explicitly set, Text will attempt to determine how + much room is needed and set it accordingly. Unless \l wrapMode is set, it will always + prefer width to height (all text will be placed on a single line). + + The \l elide property can alternatively be used to fit a single line of + plain text to a set width. + + Note that the \l{Supported HTML Subset} is limited. Also, if the text contains + HTML img tags that load remote images, the text is reloaded. + + Text provides read-only text. For editable text, see \l TextEdit. + + \sa {declarative/text/fonts}{Fonts example} +*/ +QQuickText::QQuickText(QQuickItem *parent) +: QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent) +{ + Q_D(QQuickText); + d->init(); +} + +QQuickText::~QQuickText() +{ +} + +/*! + \qmlproperty bool QtQuick2::Text::clip + This property holds whether the text is clipped. + + Note that if the text does not fit in the bounding rectangle it will be abruptly chopped. + + If you want to display potentially long text in a limited space, you probably want to use \c elide instead. +*/ + +/*! + \qmlproperty bool QtQuick2::Text::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlsignal QtQuick2::Text::onLinkActivated(string link) + + This handler is called when the user clicks on a link embedded in the text. + The link must be in rich text or HTML format and the + \a link string provides access to the particular link. + + \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0 + + The example code will display the text + "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}." + + Clicking on the highlighted link will output + \tt{http://qt.nokia.com link activated} to the console. +*/ + +/*! + \qmlproperty string QtQuick2::Text::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool QtQuick2::Text::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration QtQuick2::Text::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + Text { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool QtQuick2::Text::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool QtQuick2::Text::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool QtQuick2::Text::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real QtQuick2::Text::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int QtQuick2::Text::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. + Use \c pointSize to set the size of the font in a device independent manner. +*/ + +/*! + \qmlproperty real QtQuick2::Text::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real QtQuick2::Text::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration QtQuick2::Text::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + Text { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ +QFont QQuickText::font() const +{ + Q_D(const QQuickText); + return d->sourceFont; +} + +void QQuickText::setFont(const QFont &font) +{ + Q_D(QQuickText); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; + d->font = font; + + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + + if (oldFont != d->font) + d->updateLayout(); + + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty string QtQuick2::Text::text + + The text to display. Text supports both plain and rich text strings. + + The item will try to automatically determine whether the text should + be treated as styled text. This determination is made using Qt::mightBeRichText(). +*/ +QString QQuickText::text() const +{ + Q_D(const QQuickText); + return d->text; +} + +void QQuickText::setText(const QString &n) +{ + Q_D(QQuickText); + if (d->text == n) + return; + + d->richText = d->format == RichText; + d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n)); + d->text = n; + if (isComponentComplete()) { + if (d->richText) { + d->ensureDoc(); + d->doc->setText(n); + d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + d->richTextAsImage = enableImageCache(); + } else { + d->rightToLeftText = d->text.isRightToLeft(); + } + d->determineHorizontalAlignment(); + } + d->textHasChanged = true; + d->updateLayout(); + emit textChanged(d->text); +} + +/*! + \qmlproperty color QtQuick2::Text::color + + The text color. + + An example of green text defined using hexadecimal notation: + \qml + Text { + color: "#00FF00" + text: "green text" + } + \endqml + + An example of steel blue text defined using an SVG color name: + \qml + Text { + color: "steelblue" + text: "blue text" + } + \endqml +*/ +QColor QQuickText::color() const +{ + Q_D(const QQuickText); + return d->color; +} + +void QQuickText::setColor(const QColor &color) +{ + Q_D(QQuickText); + if (d->color == color) + return; + + d->color = color; + d->invalidateImageCache(); + emit colorChanged(d->color); +} +/*! + \qmlproperty enumeration QtQuick2::Text::style + + Set an additional text style. + + Supported text styles are: + \list + \o Text.Normal - the default + \o Text.Outline + \o Text.Raised + \o Text.Sunken + \endlist + + \qml + Row { + Text { font.pointSize: 24; text: "Normal" } + Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" } + Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" } + Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" } + } + \endqml + + \image declarative-textstyle.png +*/ +QQuickText::TextStyle QQuickText::style() const +{ + Q_D(const QQuickText); + return d->style; +} + +void QQuickText::setStyle(QQuickText::TextStyle style) +{ + Q_D(QQuickText); + if (d->style == style) + return; + + // changing to/from Normal requires the boundingRect() to change + if (isComponentComplete() && (d->style == Normal || style == Normal)) + update(); + d->style = style; + d->invalidateImageCache(); + emit styleChanged(d->style); +} + +/*! + \qmlproperty color QtQuick2::Text::styleColor + + Defines the secondary color used by text styles. + + \c styleColor is used as the outline color for outlined text, and as the + shadow color for raised or sunken text. If no style has been set, it is not + used at all. + + \qml + Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" } + \endqml + + \sa style + */ +QColor QQuickText::styleColor() const +{ + Q_D(const QQuickText); + return d->styleColor; +} + +void QQuickText::setStyleColor(const QColor &color) +{ + Q_D(QQuickText); + if (d->styleColor == color) + return; + + d->styleColor = color; + d->invalidateImageCache(); + emit styleColorChanged(d->styleColor); +} + +/*! + \qmlproperty enumeration QtQuick2::Text::horizontalAlignment + \qmlproperty enumeration QtQuick2::Text::verticalAlignment + \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment + + Sets the horizontal and vertical alignment of the text within the Text items + width and height. By default, the text is vertically aligned to the top. Horizontal + alignment follows the natural alignment of the text, for example text that is read + from left to right will be aligned to the left. + + The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and + \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom + and \c Text.AlignVCenter. + + Note that for a single line of text, the size of the text is the area of the text. In this common case, + all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will + need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to + that of the parent. + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of Text, use the read-only property \c effectiveHorizontalAlignment. +*/ +QQuickText::HAlignment QQuickText::hAlign() const +{ + Q_D(const QQuickText); + return d->hAlign; +} + +void QQuickText::setHAlign(HAlignment align) +{ + Q_D(QQuickText); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) + d->updateLayout(); +} + +void QQuickText::resetHAlign() +{ + Q_D(QQuickText); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) + d->updateLayout(); +} + +QQuickText::HAlignment QQuickText::effectiveHAlign() const +{ + Q_D(const QQuickText); + QQuickText::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QQuickText::AlignLeft: + effectiveAlignment = QQuickText::AlignRight; + break; + case QQuickText::AlignRight: + effectiveAlignment = QQuickText::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign) +{ + Q_Q(QQuickText); + if (hAlign != alignment || forceAlign) { + QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + + emit q->horizontalAlignmentChanged(hAlign); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QQuickTextPrivate::determineHorizontalAlignment() +{ + Q_Q(QQuickText); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; + return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft); + } + return false; +} + +void QQuickTextPrivate::mirrorChange() +{ + Q_Q(QQuickText); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) { + updateLayout(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +QTextDocument *QQuickTextPrivate::textDocument() +{ + return doc; +} + +QQuickText::VAlignment QQuickText::vAlign() const +{ + Q_D(const QQuickText); + return d->vAlign; +} + +void QQuickText::setVAlign(VAlignment align) +{ + Q_D(QQuickText); + if (d->vAlign == align) + return; + + d->vAlign = align; + emit verticalAlignmentChanged(align); +} + +/*! + \qmlproperty enumeration QtQuick2::Text::wrapMode + + Set this property to wrap the text to the Text item's width. The text will only + wrap if an explicit width has been set. wrapMode can be one of: + + \list + \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width. + \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width. + \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. + \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. + \endlist +*/ +QQuickText::WrapMode QQuickText::wrapMode() const +{ + Q_D(const QQuickText); + return d->wrapMode; +} + +void QQuickText::setWrapMode(WrapMode mode) +{ + Q_D(QQuickText); + if (mode == d->wrapMode) + return; + + d->wrapMode = mode; + d->updateLayout(); + + emit wrapModeChanged(); +} + +/*! + \qmlproperty int QtQuick2::Text::lineCount + + Returns the number of lines visible in the text item. + + This property is not supported for rich text. + + \sa maximumLineCount +*/ +int QQuickText::lineCount() const +{ + Q_D(const QQuickText); + return d->lineCount; +} + +/*! + \qmlproperty bool QtQuick2::Text::truncated + + Returns true if the text has been truncated due to \l maximumLineCount + or \l elide. + + This property is not supported for rich text. + + \sa maximumLineCount, elide +*/ +bool QQuickText::truncated() const +{ + Q_D(const QQuickText); + return d->truncated; +} + +/*! + \qmlproperty int QtQuick2::Text::maximumLineCount + + Set this property to limit the number of lines that the text item will show. + If elide is set to Text.ElideRight, the text will be elided appropriately. + By default, this is the value of the largest possible integer. + + This property is not supported for rich text. + + \sa lineCount, elide +*/ +int QQuickText::maximumLineCount() const +{ + Q_D(const QQuickText); + return d->maximumLineCount; +} + +void QQuickText::setMaximumLineCount(int lines) +{ + Q_D(QQuickText); + + d->maximumLineCountValid = lines==INT_MAX ? false : true; + if (d->maximumLineCount != lines) { + d->maximumLineCount = lines; + d->updateLayout(); + emit maximumLineCountChanged(); + } +} + +void QQuickText::resetMaximumLineCount() +{ + Q_D(QQuickText); + setMaximumLineCount(INT_MAX); + d->elidePos = QPointF(); + if (d->truncated != false) { + d->truncated = false; + emit truncatedChanged(); + } +} + +/*! + \qmlproperty enumeration QtQuick2::Text::textFormat + + The way the text property should be displayed. + + Supported text formats are: + + \list + \o Text.AutoText (default) + \o Text.PlainText + \o Text.StyledText + \o Text.RichText + \endlist + + If the text format is \c Text.AutoText the text element + will automatically determine whether the text should be treated as + styled text. This determination is made using Qt::mightBeRichText(). + + Text.StyledText is an optimized format supporting some basic text + styling markup, in the style of html 3.2: + + \code + - bold + - italic +
- new line +

- paragraph + - underlined text + +

to
- headers + - anchor +
    ,
      and
    • - ordered and unordered lists + > < & + \endcode + + \c Text.StyledText parser is strict, requiring tags to be correctly nested. + + \table + \row + \o + \qml +Column { + Text { + font.pointSize: 24 + text: "Hello World!" + } + Text { + font.pointSize: 24 + textFormat: Text.RichText + text: "Hello World!" + } + Text { + font.pointSize: 24 + textFormat: Text.PlainText + text: "Hello World!" + } +} + \endqml + \o \image declarative-textformat.png + \endtable +*/ +QQuickText::TextFormat QQuickText::textFormat() const +{ + Q_D(const QQuickText); + return d->format; +} + +void QQuickText::setTextFormat(TextFormat format) +{ + Q_D(QQuickText); + if (format == d->format) + return; + d->format = format; + bool wasRich = d->richText; + d->richText = format == RichText; + d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text)); + + if (!wasRich && d->richText && isComponentComplete()) { + d->ensureDoc(); + d->doc->setText(d->text); + d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + d->richTextAsImage = enableImageCache(); + } else { + d->rightToLeftText = d->text.isRightToLeft(); + } + d->determineHorizontalAlignment(); + d->updateLayout(); + + emit textFormatChanged(d->format); +} + +/*! + \qmlproperty enumeration QtQuick2::Text::elide + + Set this property to elide parts of the text fit to the Text item's width. + The text will only elide if an explicit width has been set. + + This property cannot be used with rich text. + + Eliding can be: + \list + \o Text.ElideNone - the default + \o Text.ElideLeft + \o Text.ElideMiddle + \o Text.ElideRight + \endlist + + If this property is set to Text.ElideRight, it can be used with multiline + text. The text will only elide if maximumLineCount has been set. + + If the text is a multi-length string, and the mode is not \c Text.ElideNone, + the first string that fits will be used, otherwise the last will be elided. + + Multi-length strings are ordered from longest to shortest, separated by the + Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}). +*/ +QQuickText::TextElideMode QQuickText::elideMode() const +{ + Q_D(const QQuickText); + return d->elideMode; +} + +void QQuickText::setElideMode(QQuickText::TextElideMode mode) +{ + Q_D(QQuickText); + if (mode == d->elideMode) + return; + + d->elideMode = mode; + d->updateLayout(); + + emit elideModeChanged(d->elideMode); +} + +/*! \internal */ +QRectF QQuickText::boundingRect() const +{ + Q_D(const QQuickText); + + QRect rect = d->layedOutTextRect; + if (d->style != Normal) + rect.adjust(-1, 0, 1, 2); + + // Could include font max left/right bearings to either side of rectangle. + + int h = height(); + switch (d->vAlign) { + case AlignTop: + break; + case AlignBottom: + rect.moveTop(h - rect.height()); + break; + case AlignVCenter: + rect.moveTop((h - rect.height()) / 2); + break; + } + + return QRectF(rect); +} + +/*! \internal */ +void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickText); + if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width()) + && (d->wrapMode != QQuickText::NoWrap + || d->elideMode != QQuickText::ElideNone + || d->hAlign != QQuickText::AlignLeft)) { + if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QQuickText::ElideNone && widthValid()) { + // We need to re-elide + d->updateLayout(); + } else { + // We just need to re-layout + d->updateSize(); + } + } + + QQuickItem::geometryChanged(newGeometry, oldGeometry); +} + +QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + Q_D(QQuickText); + + if (d->text.isEmpty()) { + delete oldNode; + return 0; + } + + QRectF bounds = boundingRect(); + + // We need to make sure the layout is done in the current thread +#if defined(Q_OS_MAC) + d->paintingThread = QThread::currentThread(); + if (d->layoutThread != d->paintingThread) + d->updateLayout(); +#endif + + // XXX todo - some styled text can be done by the QQuickTextNode + if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) { + bool wasDirty = d->textureImageCacheDirty; + d->textureImageCacheDirty = false; + + if (d->imageCache.isNull()) { + delete oldNode; + return 0; + } + + QSGImageNode *node = 0; + if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsTexture) { + delete oldNode; + node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode(); + d->texture = new QSGPlainTexture(); + wasDirty = true; + d->nodeType = QQuickTextPrivate::NodeIsTexture; + } else { + node = static_cast(oldNode); + Q_ASSERT(d->texture); + } + + if (wasDirty) { + qobject_cast(d->texture)->setImage(d->imageCache.toImage()); + node->setTexture(0); + node->setTexture(d->texture); + } + + node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height())); + node->setSourceRect(QRectF(0, 0, 1, 1)); + node->setHorizontalWrapMode(QSGTexture::ClampToEdge); + node->setVerticalWrapMode(QSGTexture::ClampToEdge); + node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that.. + node->update(); + + return node; + + } else { + QQuickTextNode *node = 0; + if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsText) { + delete oldNode; + node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext()); + d->nodeType = QQuickTextPrivate::NodeIsText; + } else { + node = static_cast(oldNode); + } + + node->deleteContent(); + node->setMatrix(QMatrix4x4()); + + if (d->richText) { + d->ensureDoc(); + node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor); + + } else { + node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor); + } + + return node; + } +} + +bool QQuickText::event(QEvent *e) +{ + Q_D(QQuickText); + if (e->type() == QEvent::User) { + d->checkImageCache(); + return true; + } else { + return QQuickImplicitSizeItem::event(e); + } +} + +/*! + \qmlproperty real QtQuick2::Text::paintedWidth + + Returns the width of the text, including width past the width + which is covered due to insufficient wrapping if WrapMode is set. +*/ +qreal QQuickText::paintedWidth() const +{ + Q_D(const QQuickText); + return d->paintedSize.width(); +} + +/*! + \qmlproperty real QtQuick2::Text::paintedHeight + + Returns the height of the text, including height past the height + which is covered due to there being more text than fits in the set height. +*/ +qreal QQuickText::paintedHeight() const +{ + Q_D(const QQuickText); + return d->paintedSize.height(); +} + +/*! + \qmlproperty real QtQuick2::Text::lineHeight + + Sets the line height for the text. + The value can be in pixels or a multiplier depending on lineHeightMode. + + The default value is a multiplier of 1.0. + The line height must be a positive value. +*/ +qreal QQuickText::lineHeight() const +{ + Q_D(const QQuickText); + return d->lineHeight; +} + +void QQuickText::setLineHeight(qreal lineHeight) +{ + Q_D(QQuickText); + + if ((d->lineHeight == lineHeight) || (lineHeight < 0.0)) + return; + + d->lineHeight = lineHeight; + d->updateLayout(); + emit lineHeightChanged(lineHeight); +} + +/*! + \qmlproperty enumeration QtQuick2::Text::lineHeightMode + + This property determines how the line height is specified. + The possible values are: + + \list + \o Text.ProportionalHeight (default) - this sets the spacing proportional to the + line (as a multiplier). For example, set to 2 for double spacing. + \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels). + \endlist +*/ +QQuickText::LineHeightMode QQuickText::lineHeightMode() const +{ + Q_D(const QQuickText); + return d->lineHeightMode; +} + +void QQuickText::setLineHeightMode(LineHeightMode mode) +{ + Q_D(QQuickText); + if (mode == d->lineHeightMode) + return; + + d->lineHeightMode = mode; + d->updateLayout(); + + emit lineHeightModeChanged(mode); +} + +/*! + Returns the number of resources (images) that are being loaded asynchronously. +*/ +int QQuickText::resourcesLoading() const +{ + Q_D(const QQuickText); + return d->doc ? d->doc->resourcesLoading() : 0; +} + +/*! \internal */ +void QQuickText::componentComplete() +{ + Q_D(QQuickText); + QQuickItem::componentComplete(); + if (d->updateOnComponentComplete) { + d->updateOnComponentComplete = false; + if (d->richText) { + d->ensureDoc(); + d->doc->setText(d->text); + d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + d->richTextAsImage = enableImageCache(); + } else { + d->rightToLeftText = d->text.isRightToLeft(); + } + d->determineHorizontalAlignment(); + d->updateLayout(); + } +} + + +QString QQuickTextPrivate::anchorAt(const QPointF &mousePos) +{ + if (styledText) { + for (int i = 0; i < layout.lineCount(); ++i) { + QTextLine line = layout.lineAt(i); + if (line.naturalTextRect().contains(mousePos)) { + int charPos = line.xToCursor(mousePos.x()); + foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) { + if (formatRange.format.isAnchor() + && charPos >= formatRange.start + && charPos <= formatRange.start + formatRange.length) { + return formatRange.format.anchorHref(); + } + } + break; + } + } + } + return QString(); +} + +bool QQuickTextPrivate::isLinkActivatedConnected() +{ + static int idx = this->signalIndex("linkActivated(QString)"); + return this->isSignalConnected(idx); +} + +/*! \internal */ +void QQuickText::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickText); + + if (d->isLinkActivatedConnected()) { + if (d->styledText) + d->activeLink = d->anchorAt(event->localPos()); + else if (d->richText && d->doc) + d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos()); + } + + if (d->activeLink.isEmpty()) + event->setAccepted(false); + + // ### may malfunction if two of the same links are clicked & dragged onto each other) + + if (!event->isAccepted()) + QQuickItem::mousePressEvent(event); + +} + +/*! \internal */ +void QQuickText::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickText); + + // ### confirm the link, and send a signal out + + QString link; + if (d->isLinkActivatedConnected()) { + if (d->styledText) + link = d->anchorAt(event->localPos()); + else if (d->richText && d->doc) + link = d->doc->documentLayout()->anchorAt(event->localPos()); + } + + if (!link.isEmpty() && d->activeLink == link) + emit linkActivated(d->activeLink); + else + event->setAccepted(false); + + if (!event->isAccepted()) + QQuickItem::mouseReleaseEvent(event); +} + +QT_END_NAMESPACE + +#include "qquicktext.moc" diff --git a/src/declarative/items/qquicktext_p.h b/src/declarative/items/qquicktext_p.h new file mode 100644 index 0000000000..be77f2eb45 --- /dev/null +++ b/src/declarative/items/qquicktext_p.h @@ -0,0 +1,252 @@ +// Commit: 27e4302b7f45f22180693d26747f419177c81e27 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXT_P_H +#define QQUICKTEXT_P_H + +#include "qquickimplicitsizeitem_p.h" + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QQuickTextPrivate; +class QQuickTextLine; +class Q_DECLARATIVE_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(HAlignment) + Q_ENUMS(VAlignment) + Q_ENUMS(TextStyle) + Q_ENUMS(TextFormat) + Q_ENUMS(TextElideMode) + Q_ENUMS(WrapMode) + Q_ENUMS(LineHeightMode) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(TextStyle style READ style WRITE setStyle NOTIFY styleChanged) + Q_PROPERTY(QColor styleColor READ styleColor WRITE setStyleColor NOTIFY styleColorChanged) + Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) + Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged) + Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged) + Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) + Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged) + Q_PROPERTY(bool truncated READ truncated NOTIFY truncatedChanged) + Q_PROPERTY(int maximumLineCount READ maximumLineCount WRITE setMaximumLineCount NOTIFY maximumLineCountChanged RESET resetMaximumLineCount) + + Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged) + Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode? + Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) + Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) + Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged) + Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged) + +public: + QQuickText(QQuickItem *parent=0); + ~QQuickText(); + + enum HAlignment { AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter, + AlignJustify = Qt::AlignJustify }; + enum VAlignment { AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter }; + enum TextStyle { Normal, + Outline, + Raised, + Sunken }; + enum TextFormat { PlainText = Qt::PlainText, + RichText = Qt::RichText, + AutoText = Qt::AutoText, + StyledText = 4 }; + enum TextElideMode { ElideLeft = Qt::ElideLeft, + ElideRight = Qt::ElideRight, + ElideMiddle = Qt::ElideMiddle, + ElideNone = Qt::ElideNone }; + + enum WrapMode { NoWrap = QTextOption::NoWrap, + WordWrap = QTextOption::WordWrap, + WrapAnywhere = QTextOption::WrapAnywhere, + WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT + Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere + }; + + enum LineHeightMode { ProportionalHeight, FixedHeight }; + + QString text() const; + void setText(const QString &); + + QFont font() const; + void setFont(const QFont &font); + + QColor color() const; + void setColor(const QColor &c); + + TextStyle style() const; + void setStyle(TextStyle style); + + QColor styleColor() const; + void setStyleColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; + + VAlignment vAlign() const; + void setVAlign(VAlignment align); + + WrapMode wrapMode() const; + void setWrapMode(WrapMode w); + + int lineCount() const; + bool truncated() const; + + int maximumLineCount() const; + void setMaximumLineCount(int lines); + void resetMaximumLineCount(); + + TextFormat textFormat() const; + void setTextFormat(TextFormat format); + + TextElideMode elideMode() const; + void setElideMode(TextElideMode); + + qreal lineHeight() const; + void setLineHeight(qreal lineHeight); + + LineHeightMode lineHeightMode() const; + void setLineHeightMode(LineHeightMode); + + virtual void componentComplete(); + + int resourcesLoading() const; // mainly for testing + + qreal paintedWidth() const; + qreal paintedHeight() const; + + QRectF boundingRect() const; + Q_INVOKABLE void doLayout(); + +Q_SIGNALS: + void textChanged(const QString &text); + void linkActivated(const QString &link); + void fontChanged(const QFont &font); + void colorChanged(const QColor &color); + void styleChanged(TextStyle style); + void styleColorChanged(const QColor &color); + void horizontalAlignmentChanged(HAlignment alignment); + void verticalAlignmentChanged(VAlignment alignment); + void wrapModeChanged(); + void lineCountChanged(); + void truncatedChanged(); + void maximumLineCountChanged(); + void textFormatChanged(TextFormat textFormat); + void elideModeChanged(TextElideMode mode); + void paintedSizeChanged(); + void lineHeightChanged(qreal lineHeight); + void lineHeightModeChanged(LineHeightMode mode); + void effectiveHorizontalAlignmentChanged(); + void lineLaidOut(QQuickTextLine *line); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual bool event(QEvent *); + +private: + Q_DISABLE_COPY(QQuickText) + Q_DECLARE_PRIVATE(QQuickText) +}; + +class QTextLine; +class Q_AUTOTEST_EXPORT QQuickTextLine : public QObject +{ + Q_OBJECT + Q_PROPERTY(int number READ number) + Q_PROPERTY(qreal width READ width WRITE setWidth) + Q_PROPERTY(qreal height READ height WRITE setHeight) + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) + +public: + QQuickTextLine(); + + void setLine(QTextLine* line); + int number() const; + + qreal width() const; + void setWidth(qreal width); + + qreal height() const; + void setHeight(qreal height); + + qreal x() const; + void setX(qreal x); + + qreal y() const; + void setY(qreal y); + +private: + QTextLine *m_line; + qreal m_height; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickText) +QML_DECLARE_TYPE(QQuickTextLine) + +QT_END_HEADER + +#endif // QQUICKTEXT_P_H diff --git a/src/declarative/items/qquicktext_p_p.h b/src/declarative/items/qquicktext_p_p.h new file mode 100644 index 0000000000..16cc29ac13 --- /dev/null +++ b/src/declarative/items/qquicktext_p_p.h @@ -0,0 +1,168 @@ +// Commit: 6e5a642c9484536fc173714f560f739944368cf5 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXT_P_P_H +#define QQUICKTEXT_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickitem.h" +#include "qquickimplicitsizeitem_p_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QTextLayout; +class QQuickTextDocumentWithImageResources; +class QSGPlainTexture; + +class Q_AUTOTEST_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickText) +public: + QQuickTextPrivate(); + ~QQuickTextPrivate(); + void init(); + + void updateSize(); + void updateLayout(); + bool determineHorizontalAlignment(); + bool setHAlign(QQuickText::HAlignment, bool forceAlign = false); + void mirrorChange(); + QTextDocument *textDocument(); + bool isLineLaidOutConnected(); + + QString text; + QFont font; + QFont sourceFont; + QColor color; + QQuickText::TextStyle style; + QColor styleColor; + QString activeLink; + QQuickText::HAlignment hAlign; + QQuickText::VAlignment vAlign; + QQuickText::TextElideMode elideMode; + QQuickText::TextFormat format; + QQuickText::WrapMode wrapMode; + qreal lineHeight; + QQuickText::LineHeightMode lineHeightMode; + int lineCount; + int maximumLineCount; + int maximumLineCountValid; + QPointF elidePos; + + static QString elideChar; + + void invalidateImageCache(); + void checkImageCache(); + QPixmap imageCache; + QSGTexture *texture; + + bool imageCacheDirty:1; + bool updateOnComponentComplete:1; + bool richText:1; + bool styledText:1; + bool singleline:1; + bool cacheAllTextAsImage:1; + bool internalWidthUpdate:1; + bool requireImplicitWidth:1; + bool truncated:1; + bool hAlignImplicit:1; + bool rightToLeftText:1; + bool layoutTextElided:1; + bool richTextAsImage:1; + bool textureImageCacheDirty:1; + bool textHasChanged:1; + + QRect layedOutTextRect; + QSize paintedSize; + qreal naturalWidth; + virtual qreal getImplicitWidth() const; + + void ensureDoc(); + QPixmap textDocumentImage(bool drawStyle); + QQuickTextDocumentWithImageResources *doc; + + QRect setupTextLayout(); + void setupCustomLineGeometry(QTextLine &line, qreal &height, qreal elideWidth); + QPixmap textLayoutImage(bool drawStyle); + void drawTextLayout(QPainter *p, const QPointF &pos, bool drawStyle); + bool isLinkActivatedConnected(); + QString anchorAt(const QPointF &pos); + QTextLayout layout; + QList linesRects; + QQuickTextLine *textLine; + + static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource); + static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset); + + static inline QQuickTextPrivate *get(QQuickText *t) { + return t->d_func(); + } + + enum NodeType { + NodeIsNull, + NodeIsTexture, + NodeIsText + }; + NodeType nodeType; + +#if defined(Q_OS_MAC) + QThread *layoutThread; + QThread *paintingThread; +#endif +}; + +QT_END_NAMESPACE + +#endif // QQUICKTEXT_P_P_H diff --git a/src/declarative/items/qquicktextedit.cpp b/src/declarative/items/qquicktextedit.cpp new file mode 100644 index 0000000000..75f60bc08c --- /dev/null +++ b/src/declarative/items/qquicktextedit.cpp @@ -0,0 +1,1976 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktextedit_p.h" +#include "qquicktextedit_p_p.h" +#include "qquickevents_p_p.h" +#include "qquickcanvas.h" +#include "qquicktextnode_p.h" +#include "qsgsimplerectnode.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) +DEFINE_BOOL_CONFIG_OPTION(qmlEnableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE) + +/*! + \qmlclass TextEdit QQuickTextEdit + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The TextEdit item displays multiple lines of editable formatted text. + \inherits Item + + The TextEdit item displays a block of editable, formatted text. + + It can display both plain and rich text. For example: + + \qml +TextEdit { + width: 240 + text: "Hello World!" + font.family: "Helvetica" + font.pointSize: 20 + color: "blue" + focus: true +} + \endqml + + \image declarative-textedit.gif + + Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus. + + Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific + to a look-and-feel. For example, to add flickable scrolling that follows the cursor: + + \snippet snippets/declarative/texteditor.qml 0 + + A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible + scrollbar, or a scrollbar that fades in to show location, etc. + + Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can + be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely + from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord(). + + You can translate between cursor positions (characters from the start of the document) and pixel + points using positionAt() and positionToRectangle(). + + \sa Text, TextInput, {declarative/text/textselection}{Text Selection example} +*/ + +/*! + \qmlsignal QtQuick2::TextEdit::onLinkActivated(string link) + + This handler is called when the user clicks on a link embedded in the text. + The link must be in rich text or HTML format and the + \a link string provides access to the particular link. +*/ +QQuickTextEdit::QQuickTextEdit(QQuickItem *parent) +: QQuickImplicitSizeItem(*(new QQuickTextEditPrivate), parent) +{ + Q_D(QQuickTextEdit); + d->init(); +} + +QString QQuickTextEdit::text() const +{ + Q_D(const QQuickTextEdit); + +#ifndef QT_NO_TEXTHTMLPARSER + if (d->richText) + return d->document->toHtml(); + else +#endif + return d->document->toPlainText(); +} + +/*! + \qmlproperty string QtQuick2::TextEdit::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool QtQuick2::TextEdit::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration QtQuick2::TextEdit::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + TextEdit { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool QtQuick2::TextEdit::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool QtQuick2::TextEdit::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool QtQuick2::TextEdit::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real QtQuick2::TextEdit::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int QtQuick2::TextEdit::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. Use + \l{TextEdit::font.pointSize} to set the size of the font in a + device independent manner. +*/ + +/*! + \qmlproperty real QtQuick2::TextEdit::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real QtQuick2::TextEdit::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration QtQuick2::TextEdit::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ + +/*! + \qmlproperty string QtQuick2::TextEdit::text + + The text to display. If the text format is AutoText the text edit will + automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). +*/ +void QQuickTextEdit::setText(const QString &text) +{ + Q_D(QQuickTextEdit); + if (QQuickTextEdit::text() == text) + return; + + d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text)); + if (d->richText) { +#ifndef QT_NO_TEXTHTMLPARSER + d->control->setHtml(text); +#else + d->control->setPlainText(text); +#endif + d->useImageFallback = qmlEnableImageCache(); + } else { + d->control->setPlainText(text); + } + q_textChanged(); +} + +/*! + \qmlproperty enumeration QtQuick2::TextEdit::textFormat + + The way the text property should be displayed. + + \list + \o TextEdit.AutoText + \o TextEdit.PlainText + \o TextEdit.RichText + \endlist + + The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit + will automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + + \table + \row + \o + \qml +Column { + TextEdit { + font.pointSize: 24 + text: "Hello World!" + } + TextEdit { + font.pointSize: 24 + textFormat: TextEdit.RichText + text: "Hello World!" + } + TextEdit { + font.pointSize: 24 + textFormat: TextEdit.PlainText + text: "Hello World!" + } +} + \endqml + \o \image declarative-textformat.png + \endtable +*/ +QQuickTextEdit::TextFormat QQuickTextEdit::textFormat() const +{ + Q_D(const QQuickTextEdit); + return d->format; +} + +void QQuickTextEdit::setTextFormat(TextFormat format) +{ + Q_D(QQuickTextEdit); + if (format == d->format) + return; + bool wasRich = d->richText; + d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); + + if (wasRich && !d->richText) { + d->control->setPlainText(d->text); + updateSize(); + } else if (!wasRich && d->richText) { +#ifndef QT_NO_TEXTHTMLPARSER + d->control->setHtml(d->text); +#else + d->control->setPlainText(d->text); +#endif + updateSize(); + d->useImageFallback = qmlEnableImageCache(); + } + d->format = format; + d->control->setAcceptRichText(d->format != PlainText); + emit textFormatChanged(d->format); +} + +QFont QQuickTextEdit::font() const +{ + Q_D(const QQuickTextEdit); + return d->sourceFont; +} + +void QQuickTextEdit::setFont(const QFont &font) +{ + Q_D(QQuickTextEdit); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; + d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + + if (oldFont != d->font) { + d->document->setDefaultFont(d->font); + if (d->cursor) { + d->cursor->setHeight(QFontMetrics(d->font).height()); + moveCursorDelegate(); + } + updateSize(); + updateDocument(); + } + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty color QtQuick2::TextEdit::color + + The text color. + + \qml + // green text using hexadecimal notation + TextEdit { color: "#00FF00" } + \endqml + + \qml + // steelblue text using SVG color name + TextEdit { color: "steelblue" } + \endqml +*/ +QColor QQuickTextEdit::color() const +{ + Q_D(const QQuickTextEdit); + return d->color; +} + +void QQuickTextEdit::setColor(const QColor &color) +{ + Q_D(QQuickTextEdit); + if (d->color == color) + return; + + d->color = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::Text, color); + d->control->setPalette(pal); + updateDocument(); + emit colorChanged(d->color); +} + +/*! + \qmlproperty color QtQuick2::TextEdit::selectionColor + + The text highlight color, used behind selections. +*/ +QColor QQuickTextEdit::selectionColor() const +{ + Q_D(const QQuickTextEdit); + return d->selectionColor; +} + +void QQuickTextEdit::setSelectionColor(const QColor &color) +{ + Q_D(QQuickTextEdit); + if (d->selectionColor == color) + return; + + d->selectionColor = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::Highlight, color); + d->control->setPalette(pal); + updateDocument(); + emit selectionColorChanged(d->selectionColor); +} + +/*! + \qmlproperty color QtQuick2::TextEdit::selectedTextColor + + The selected text color, used in selections. +*/ +QColor QQuickTextEdit::selectedTextColor() const +{ + Q_D(const QQuickTextEdit); + return d->selectedTextColor; +} + +void QQuickTextEdit::setSelectedTextColor(const QColor &color) +{ + Q_D(QQuickTextEdit); + if (d->selectedTextColor == color) + return; + + d->selectedTextColor = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::HighlightedText, color); + d->control->setPalette(pal); + updateDocument(); + emit selectedTextColorChanged(d->selectedTextColor); +} + +/*! + \qmlproperty enumeration QtQuick2::TextEdit::horizontalAlignment + \qmlproperty enumeration QtQuick2::TextEdit::verticalAlignment + \qmlproperty enumeration QtQuick2::TextEdit::effectiveHorizontalAlignment + + Sets the horizontal and vertical alignment of the text within the TextEdit item's + width and height. By default, the text alignment follows the natural alignment + of the text, for example text that is read from left to right will be aligned to + the left. + + Valid values for \c horizontalAlignment are: + \list + \o TextEdit.AlignLeft (default) + \o TextEdit.AlignRight + \o TextEdit.AlignHCenter + \o TextEdit.AlignJustify + \endlist + + Valid values for \c verticalAlignment are: + \list + \o TextEdit.AlignTop (default) + \o TextEdit.AlignBottom + \o TextEdit.AlignVCenter + \endlist + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextEdit, use the read-only property \c effectiveHorizontalAlignment. +*/ +QQuickTextEdit::HAlignment QQuickTextEdit::hAlign() const +{ + Q_D(const QQuickTextEdit); + return d->hAlign; +} + +void QQuickTextEdit::setHAlign(HAlignment align) +{ + Q_D(QQuickTextEdit); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +void QQuickTextEdit::resetHAlign() +{ + Q_D(QQuickTextEdit); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign() const +{ + Q_D(const QQuickTextEdit); + QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QQuickTextEdit::AlignLeft: + effectiveAlignment = QQuickTextEdit::AlignRight; + break; + case QQuickTextEdit::AlignRight: + effectiveAlignment = QQuickTextEdit::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment alignment, bool forceAlign) +{ + Q_Q(QQuickTextEdit); + if (hAlign != alignment || forceAlign) { + QQuickTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QQuickTextEditPrivate::determineHorizontalAlignment() +{ + Q_Q(QQuickTextEdit); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; + return setHAlign(alignToRight ? QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft); + } + return false; +} + +void QQuickTextEditPrivate::mirrorChange() +{ + Q_Q(QQuickTextEdit); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QQuickTextEdit::AlignRight || hAlign == QQuickTextEdit::AlignLeft)) { + updateDefaultTextOption(); + q->updateSize(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +QQuickTextEdit::VAlignment QQuickTextEdit::vAlign() const +{ + Q_D(const QQuickTextEdit); + return d->vAlign; +} + +void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment) +{ + Q_D(QQuickTextEdit); + if (alignment == d->vAlign) + return; + d->vAlign = alignment; + d->updateDefaultTextOption(); + updateSize(); + moveCursorDelegate(); + emit verticalAlignmentChanged(d->vAlign); +} +/*! + \qmlproperty enumeration QtQuick2::TextEdit::wrapMode + + Set this property to wrap the text to the TextEdit item's width. + The text will only wrap if an explicit width has been set. + + \list + \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width. + \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width. + \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. + \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. + \endlist + + The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap. +*/ +QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode() const +{ + Q_D(const QQuickTextEdit); + return d->wrapMode; +} + +void QQuickTextEdit::setWrapMode(WrapMode mode) +{ + Q_D(QQuickTextEdit); + if (mode == d->wrapMode) + return; + d->wrapMode = mode; + d->updateDefaultTextOption(); + updateSize(); + emit wrapModeChanged(); +} + +/*! + \qmlproperty int QtQuick2::TextEdit::lineCount + + Returns the total number of lines in the textEdit item. +*/ +int QQuickTextEdit::lineCount() const +{ + Q_D(const QQuickTextEdit); + return d->lineCount; +} + +/*! + \qmlproperty real QtQuick2::TextEdit::paintedWidth + + Returns the width of the text, including the width past the width + which is covered due to insufficient wrapping if \l wrapMode is set. +*/ +qreal QQuickTextEdit::paintedWidth() const +{ + Q_D(const QQuickTextEdit); + return d->paintedSize.width(); +} + +/*! + \qmlproperty real QtQuick2::TextEdit::paintedHeight + + Returns the height of the text, including the height past the height + that is covered if the text does not fit within the set height. +*/ +qreal QQuickTextEdit::paintedHeight() const +{ + Q_D(const QQuickTextEdit); + return d->paintedSize.height(); +} + +/*! + \qmlmethod rectangle QtQuick2::TextEdit::positionToRectangle(position) + + Returns the rectangle at the given \a position in the text. The x, y, + and height properties correspond to the cursor that would describe + that position. +*/ +QRectF QQuickTextEdit::positionToRectangle(int pos) const +{ + Q_D(const QQuickTextEdit); + QTextCursor c(d->document); + c.setPosition(pos); + return d->control->cursorRect(c); + +} + +/*! + \qmlmethod int QtQuick2::TextEdit::positionAt(int x, int y) + + Returns the text position closest to pixel position (\a x, \a y). + + Position 0 is before the first character, position 1 is after the first character + but before the second, and so on until position \l {text}.length, which is after all characters. +*/ +int QQuickTextEdit::positionAt(int x, int y) const +{ + Q_D(const QQuickTextEdit); + int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit); + QTextCursor cursor = d->control->textCursor(); + if (r > cursor.position()) { + // The cursor position includes positions within the preedit text, but only positions in the + // same text block are offset so it is possible to get a position that is either part of the + // preedit or the next text block. + QTextLayout *layout = cursor.block().layout(); + const int preeditLength = layout + ? layout->preeditAreaText().length() + : 0; + if (preeditLength > 0 + && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) { + r = r > cursor.position() + preeditLength + ? r - preeditLength + : cursor.position(); + } + } + return r; +} + +/*! + \qmlmethod void QtQuick2::TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters) + + Moves the cursor to \a position and updates the selection according to the optional \a mode + parameter. (To only move the cursor, set the \l cursorPosition property.) + + When this method is called it additionally sets either the + selectionStart or the selectionEnd (whichever was at the previous cursor position) + to the specified position. This allows you to easily extend and contract the selected + text range. + + The selection mode specifies whether the selection is updated on a per character or a per word + basis. If not specified the selection mode will default to TextEdit.SelectCharacters. + + \list + \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at + the previous cursor position) to the specified position. + \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all + words between the specified postion and the previous cursor position. Words partially in the + range are included. + \endlist + + For example, take this sequence of calls: + + \code + cursorPosition = 5 + moveCursorSelection(9, TextEdit.SelectCharacters) + moveCursorSelection(7, TextEdit.SelectCharacters) + \endcode + + This moves the cursor to position 5, extend the selection end from 5 to 9 + and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 + selected (the 6th and 7th characters). + + The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary + before or on position 5 and extend the selection end to a word boundary on or past position 9. +*/ +void QQuickTextEdit::moveCursorSelection(int pos) +{ + //Note that this is the same as setCursorPosition but with the KeepAnchor flag set + Q_D(QQuickTextEdit); + QTextCursor cursor = d->control->textCursor(); + if (cursor.position() == pos) + return; + cursor.setPosition(pos, QTextCursor::KeepAnchor); + d->control->setTextCursor(cursor); +} + +void QQuickTextEdit::moveCursorSelection(int pos, SelectionMode mode) +{ + Q_D(QQuickTextEdit); + QTextCursor cursor = d->control->textCursor(); + if (cursor.position() == pos) + return; + if (mode == SelectCharacters) { + cursor.setPosition(pos, QTextCursor::KeepAnchor); + } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) { + if (cursor.anchor() > cursor.position()) { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); + if (cursor.position() == cursor.anchor()) + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor); + else + cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor); + } else { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); + } + + cursor.setPosition(pos, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); + if (cursor.position() != pos) + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) { + if (cursor.anchor() < cursor.position()) { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); + } else { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + if (cursor.position() != cursor.anchor()) { + cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); + } + } + + cursor.setPosition(pos, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + if (cursor.position() != pos) { + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); + } + } + d->control->setTextCursor(cursor); +} + +/*! + \qmlproperty bool QtQuick2::TextEdit::cursorVisible + If true the text edit shows a cursor. + + This property is set and unset when the text edit gets active focus, but it can also + be set directly (useful, for example, if a KeyProxy might forward keys to it). +*/ +bool QQuickTextEdit::isCursorVisible() const +{ + Q_D(const QQuickTextEdit); + return d->cursorVisible; +} + +void QQuickTextEdit::setCursorVisible(bool on) +{ + Q_D(QQuickTextEdit); + if (d->cursorVisible == on) + return; + d->cursorVisible = on; + QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut); + if (!on && !d->persistentSelection) + d->control->setCursorIsFocusIndicator(true); + d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); + emit cursorVisibleChanged(d->cursorVisible); +} + +/*! + \qmlproperty int QtQuick2::TextEdit::cursorPosition + The position of the cursor in the TextEdit. +*/ +int QQuickTextEdit::cursorPosition() const +{ + Q_D(const QQuickTextEdit); + return d->control->textCursor().position(); +} + +void QQuickTextEdit::setCursorPosition(int pos) +{ + Q_D(QQuickTextEdit); + if (pos < 0 || pos > d->text.length()) + return; + QTextCursor cursor = d->control->textCursor(); + if (cursor.position() == pos && cursor.anchor() == pos) + return; + cursor.setPosition(pos); + d->control->setTextCursor(cursor); +} + +/*! + \qmlproperty Component QtQuick2::TextEdit::cursorDelegate + The delegate for the cursor in the TextEdit. + + If you set a cursorDelegate for a TextEdit, this delegate will be used for + drawing the cursor instead of the standard cursor. An instance of the + delegate will be created and managed by the text edit when a cursor is + needed, and the x and y properties of delegate instance will be set so as + to be one pixel before the top left of the current character. + + Note that the root item of the delegate component must be a QDeclarativeItem or + QDeclarativeItem derived item. +*/ +QDeclarativeComponent* QQuickTextEdit::cursorDelegate() const +{ + Q_D(const QQuickTextEdit); + return d->cursorComponent; +} + +void QQuickTextEdit::setCursorDelegate(QDeclarativeComponent* c) +{ + Q_D(QQuickTextEdit); + if (d->cursorComponent) { + if (d->cursor) { + d->control->setCursorWidth(-1); + updateCursor(); + delete d->cursor; + d->cursor = 0; + } + } + d->cursorComponent = c; + if (c && c->isReady()) { + loadCursorDelegate(); + } else { + if (c) + connect(c, SIGNAL(statusChanged()), + this, SLOT(loadCursorDelegate())); + } + + emit cursorDelegateChanged(); +} + +void QQuickTextEdit::loadCursorDelegate() +{ + Q_D(QQuickTextEdit); + if (d->cursorComponent->isLoading()) + return; + QDeclarativeContext *creationContext = d->cursorComponent->creationContext(); + QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this)); + d->cursor = qobject_cast(object); + if (d->cursor) { + d->control->setCursorWidth(0); + updateCursor(); + QDeclarative_setParent_noEvent(d->cursor, this); + d->cursor->setParentItem(this); + d->cursor->setHeight(QFontMetrics(d->font).height()); + moveCursorDelegate(); + }else{ + delete object; + qmlInfo(this) << "Error loading cursor delegate."; + } +} + +/*! + \qmlproperty int QtQuick2::TextEdit::selectionStart + + The cursor position before the first character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionEnd, cursorPosition, selectedText +*/ +int QQuickTextEdit::selectionStart() const +{ + Q_D(const QQuickTextEdit); + return d->control->textCursor().selectionStart(); +} + +/*! + \qmlproperty int QtQuick2::TextEdit::selectionEnd + + The cursor position after the last character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionStart, cursorPosition, selectedText +*/ +int QQuickTextEdit::selectionEnd() const +{ + Q_D(const QQuickTextEdit); + return d->control->textCursor().selectionEnd(); +} + +/*! + \qmlproperty string QtQuick2::TextEdit::selectedText + + This read-only property provides the text currently selected in the + text edit. + + It is equivalent to the following snippet, but is faster and easier + to use. + \code + //myTextEdit is the id of the TextEdit + myTextEdit.text.toString().substring(myTextEdit.selectionStart, + myTextEdit.selectionEnd); + \endcode +*/ +QString QQuickTextEdit::selectedText() const +{ + Q_D(const QQuickTextEdit); + return d->control->textCursor().selectedText(); +} + +/*! + \qmlproperty bool QtQuick2::TextEdit::activeFocusOnPress + + Whether the TextEdit should gain active focus on a mouse press. By default this is + set to true. +*/ +bool QQuickTextEdit::focusOnPress() const +{ + Q_D(const QQuickTextEdit); + return d->focusOnPress; +} + +void QQuickTextEdit::setFocusOnPress(bool on) +{ + Q_D(QQuickTextEdit); + if (d->focusOnPress == on) + return; + d->focusOnPress = on; + emit activeFocusOnPressChanged(d->focusOnPress); +} + +/*! + \qmlproperty bool QtQuick2::TextEdit::persistentSelection + + Whether the TextEdit should keep the selection visible when it loses active focus to another + item in the scene. By default this is set to true; +*/ +bool QQuickTextEdit::persistentSelection() const +{ + Q_D(const QQuickTextEdit); + return d->persistentSelection; +} + +void QQuickTextEdit::setPersistentSelection(bool on) +{ + Q_D(QQuickTextEdit); + if (d->persistentSelection == on) + return; + d->persistentSelection = on; + emit persistentSelectionChanged(d->persistentSelection); +} + +/* + \qmlproperty real QtQuick2::TextEdit::textMargin + + The margin, in pixels, around the text in the TextEdit. +*/ +qreal QQuickTextEdit::textMargin() const +{ + Q_D(const QQuickTextEdit); + return d->textMargin; +} + +void QQuickTextEdit::setTextMargin(qreal margin) +{ + Q_D(QQuickTextEdit); + if (d->textMargin == margin) + return; + d->textMargin = margin; + d->document->setDocumentMargin(d->textMargin); + emit textMarginChanged(d->textMargin); +} + +void QQuickTextEdit::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if (newGeometry.width() != oldGeometry.width()) + updateSize(); + QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); +} + +/*! + Ensures any delayed caching or data loading the class + needs to performed is complete. +*/ +void QQuickTextEdit::componentComplete() +{ + Q_D(QQuickTextEdit); + QQuickImplicitSizeItem::componentComplete(); + + if (d->richText) + d->useImageFallback = qmlEnableImageCache(); + + if (d->dirty) { + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); + updateSize(); + d->dirty = false; + } + +} +/*! + \qmlproperty bool QtQuick2::TextEdit::selectByMouse + + Defaults to false. + + If true, the user can use the mouse to select text in some + platform-specific way. Note that for some platforms this may + not be an appropriate interaction (eg. may conflict with how + the text needs to behave inside a Flickable. +*/ +bool QQuickTextEdit::selectByMouse() const +{ + Q_D(const QQuickTextEdit); + return d->selectByMouse; +} + +void QQuickTextEdit::setSelectByMouse(bool on) +{ + Q_D(QQuickTextEdit); + if (d->selectByMouse != on) { + d->selectByMouse = on; + setKeepMouseGrab(on); + if (on) + setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse); + else + setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse); + emit selectByMouseChanged(on); + } +} + +/*! + \qmlproperty enum QtQuick2::TextEdit::mouseSelectionMode + + Specifies how text should be selected using a mouse. + + \list + \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default) + \o TextEdit.SelectWords - The selection is updated with whole words. + \endlist + + This property only applies when \l selectByMouse is true. +*/ +QQuickTextEdit::SelectionMode QQuickTextEdit::mouseSelectionMode() const +{ + Q_D(const QQuickTextEdit); + return d->mouseSelectionMode; +} + +void QQuickTextEdit::setMouseSelectionMode(SelectionMode mode) +{ + Q_D(QQuickTextEdit); + if (d->mouseSelectionMode != mode) { + d->mouseSelectionMode = mode; + d->control->setWordSelectionEnabled(mode == SelectWords); + emit mouseSelectionModeChanged(mode); + } +} + +/*! + \qmlproperty bool QtQuick2::TextEdit::readOnly + + Whether the user can interact with the TextEdit item. If this + property is set to true the text cannot be edited by user interaction. + + By default this property is false. +*/ +void QQuickTextEdit::setReadOnly(bool r) +{ + Q_D(QQuickTextEdit); + if (r == isReadOnly()) + return; + + setFlag(QQuickItem::ItemAcceptsInputMethod, !r); + Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse; + if (d->selectByMouse) + flags = flags | Qt::TextSelectableByMouse; + if (!r) + flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable; + d->control->setTextInteractionFlags(flags); + if (!r) + d->control->moveCursor(QTextCursor::End); + + emit readOnlyChanged(r); +} + +bool QQuickTextEdit::isReadOnly() const +{ + Q_D(const QQuickTextEdit); + return !(d->control->textInteractionFlags() & Qt::TextEditable); +} + +/*! + Sets how the text edit should interact with user input to the given + \a flags. +*/ +void QQuickTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags) +{ + Q_D(QQuickTextEdit); + d->control->setTextInteractionFlags(flags); +} + +/*! + Returns the flags specifying how the text edit should interact + with user input. +*/ +Qt::TextInteractionFlags QQuickTextEdit::textInteractionFlags() const +{ + Q_D(const QQuickTextEdit); + return d->control->textInteractionFlags(); +} + +/*! + \qmlproperty rectangle QtQuick2::TextEdit::cursorRectangle + + The rectangle where the text cursor is rendered + within the text edit. Read-only. +*/ +QRect QQuickTextEdit::cursorRectangle() const +{ + Q_D(const QQuickTextEdit); + return d->control->cursorRect().toRect().translated(0,d->yoff); +} + +bool QQuickTextEdit::event(QEvent *event) +{ + Q_D(QQuickTextEdit); + if (event->type() == QEvent::ShortcutOverride) { + d->control->processEvent(event, QPointF(0, -d->yoff)); + return event->isAccepted(); + } + return QQuickImplicitSizeItem::event(event); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QQuickTextEdit::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QQuickImplicitSizeItem::keyPressEvent(event); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QQuickImplicitSizeItem::keyReleaseEvent(event); +} + +/*! + \qmlmethod void QtQuick2::TextEdit::deselect() + + Removes active text selection. +*/ +void QQuickTextEdit::deselect() +{ + Q_D(QQuickTextEdit); + QTextCursor c = d->control->textCursor(); + c.clearSelection(); + d->control->setTextCursor(c); +} + +/*! + \qmlmethod void QtQuick2::TextEdit::selectAll() + + Causes all text to be selected. +*/ +void QQuickTextEdit::selectAll() +{ + Q_D(QQuickTextEdit); + d->control->selectAll(); +} + +/*! + \qmlmethod void QtQuick2::TextEdit::selectWord() + + Causes the word closest to the current cursor position to be selected. +*/ +void QQuickTextEdit::selectWord() +{ + Q_D(QQuickTextEdit); + QTextCursor c = d->control->textCursor(); + c.select(QTextCursor::WordUnderCursor); + d->control->setTextCursor(c); +} + +/*! + \qmlmethod void QtQuick2::TextEdit::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd +*/ +void QQuickTextEdit::select(int start, int end) +{ + Q_D(QQuickTextEdit); + if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length()) + return; + QTextCursor cursor = d->control->textCursor(); + cursor.beginEditBlock(); + cursor.setPosition(start, QTextCursor::MoveAnchor); + cursor.setPosition(end, QTextCursor::KeepAnchor); + cursor.endEditBlock(); + d->control->setTextCursor(cursor); + + // QTBUG-11100 + updateSelectionMarkers(); +} + +/*! + \qmlmethod void QtQuick2::TextEdit::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. +*/ +bool QQuickTextEdit::isRightToLeft(int start, int end) +{ + Q_D(QQuickTextEdit); + if (start > end) { + qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start."; + return false; + } else { + return d->text.mid(start, end - start).isRightToLeft(); + } +} + +#ifndef QT_NO_CLIPBOARD +/*! + \qmlmethod QtQuick2::TextEdit::cut() + + Moves the currently selected text to the system clipboard. +*/ +void QQuickTextEdit::cut() +{ + Q_D(QQuickTextEdit); + d->control->cut(); +} + +/*! + \qmlmethod QtQuick2::TextEdit::copy() + + Copies the currently selected text to the system clipboard. +*/ +void QQuickTextEdit::copy() +{ + Q_D(QQuickTextEdit); + d->control->copy(); +} + +/*! + \qmlmethod QtQuick2::TextEdit::paste() + + Replaces the currently selected text by the contents of the system clipboard. +*/ +void QQuickTextEdit::paste() +{ + Q_D(QQuickTextEdit); + d->control->paste(); +} +#endif // QT_NO_CLIPBOARD + +/*! +\overload +Handles the given mouse \a event. +*/ +void QQuickTextEdit::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickTextEdit); + if (d->focusOnPress){ + bool hadActiveFocus = hasActiveFocus(); + forceActiveFocus(); + // re-open input panel on press if already focused + if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) + openSoftwareInputPanel(); + } + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QQuickImplicitSizeItem::mousePressEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + + if (!event->isAccepted()) + QQuickImplicitSizeItem::mouseReleaseEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QQuickImplicitSizeItem::mouseDoubleClickEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickTextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QQuickImplicitSizeItem::mouseMoveEvent(event); +} + +/*! +\overload +Handles the given input method \a event. +*/ +void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QQuickTextEdit); + const bool wasComposing = isInputMethodComposing(); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (wasComposing != isInputMethodComposing()) + emit inputMethodComposingChanged(); +} + +void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickTextEdit); + if (change == ItemActiveFocusHasChanged) { + setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus()); + } + QQuickItem::itemChange(change, value); +} + +/*! +\overload +Returns the value of the given \a property. +*/ +QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QQuickTextEdit); + + QVariant v; + switch (property) { + case Qt::ImEnabled: + v = (bool)(flags() & ItemAcceptsInputMethod); + break; + case Qt::ImHints: + v = (int)inputMethodHints(); + break; + default: + v = d->control->inputMethodQuery(property); + break; + } + return v; + +} + +void QQuickTextEdit::updateImageCache(const QRectF &) +{ + Q_D(QQuickTextEdit); + + // Do we really need the image cache? + if (!d->richText || !d->useImageFallback) { + if (!d->pixmapCache.isNull()) + d->pixmapCache = QPixmap(); + return; + } + + if (width() != d->pixmapCache.width() || height() != d->pixmapCache.height()) + d->pixmapCache = QPixmap(width(), height()); + + if (d->pixmapCache.isNull()) + return; + + // ### Use supplied rect, clear area and update only this part (for cursor updates) + QRectF bounds = QRectF(0, 0, width(), height()); + d->pixmapCache.fill(Qt::transparent); + { + QPainter painter(&d->pixmapCache); + + painter.setRenderHint(QPainter::TextAntialiasing); + painter.translate(0, d->yoff); + + d->control->drawContents(&painter, bounds); + } + +} + +QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) +{ + Q_UNUSED(updatePaintNodeData); + Q_D(QQuickTextEdit); + + QSGNode *currentNode = oldNode; + if (d->richText && d->useImageFallback) { + QSGImageNode *node = 0; + if (oldNode == 0 || d->nodeType != QQuickTextEditPrivate::NodeIsTexture) { + delete oldNode; + node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode(); + d->texture = new QSGPlainTexture(); + d->nodeType = QQuickTextEditPrivate::NodeIsTexture; + currentNode = node; + } else { + node = static_cast(oldNode); + } + + qobject_cast(d->texture)->setImage(d->pixmapCache.toImage()); + node->setTexture(0); + node->setTexture(d->texture); + + node->setTargetRect(QRectF(0, 0, d->pixmapCache.width(), d->pixmapCache.height())); + node->setSourceRect(QRectF(0, 0, 1, 1)); + node->setHorizontalWrapMode(QSGTexture::ClampToEdge); + node->setVerticalWrapMode(QSGTexture::ClampToEdge); + node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that.. + node->update(); + + } else if (oldNode == 0 || d->documentDirty) { + d->documentDirty = false; + +#if defined(Q_WS_MAC) + // Make sure document is relayouted in the paint node on Mac + // to avoid crashes due to the font engines created in the + // shaping process + d->document->markContentsDirty(0, d->document->characterCount()); +#endif + + QQuickTextNode *node = 0; + if (oldNode == 0 || d->nodeType != QQuickTextEditPrivate::NodeIsText) { + delete oldNode; + node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext()); + d->nodeType = QQuickTextEditPrivate::NodeIsText; + currentNode = node; + } else { + node = static_cast(oldNode); + } + + node->deleteContent(); + node->setMatrix(QMatrix4x4()); + + QRectF bounds = boundingRect(); + + QColor selectionColor = d->control->palette().color(QPalette::Highlight); + QColor selectedTextColor = d->control->palette().color(QPalette::HighlightedText); + node->addTextDocument(bounds.topLeft(), d->document, d->color, QQuickText::Normal, QColor(), + selectionColor, selectedTextColor, selectionStart(), + selectionEnd() - 1); // selectionEnd() returns first char after + // selection + +#if defined(Q_WS_MAC) + // We also need to make sure the document layout is redone when + // control is returned to the main thread, as all the font engines + // are now owned by the rendering thread + d->document->markContentsDirty(0, d->document->characterCount()); +#endif + } + + if (d->nodeType == QQuickTextEditPrivate::NodeIsText && d->cursorComponent == 0 && !isReadOnly()) { + QQuickTextNode *node = static_cast(currentNode); + + QColor color = (!d->cursorVisible || !d->control->cursorOn()) + ? QColor(0, 0, 0, 0) + : d->color; + + if (node->cursorNode() == 0) { + node->setCursor(cursorRectangle(), color); + } else { + node->cursorNode()->setRect(cursorRectangle()); + node->cursorNode()->setColor(color); + } + + } + + return currentNode; +} + +/*! + \qmlproperty bool QtQuick2::TextEdit::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty bool QtQuick2::TextEdit::canPaste + + Returns true if the TextEdit is writable and the content of the clipboard is + suitable for pasting into the TextEdit. +*/ +bool QQuickTextEdit::canPaste() const +{ + Q_D(const QQuickTextEdit); + return d->canPaste; +} + +/*! + \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing + + + This property holds whether the TextEdit has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextEdit to edit or commit the partial text. This property can be used + to determine when to disable events handlers that may interfere with the + correct operation of an input method. +*/ +bool QQuickTextEdit::isInputMethodComposing() const +{ + Q_D(const QQuickTextEdit); + if (QTextLayout *layout = d->control->textCursor().block().layout()) + return layout->preeditAreaText().length() > 0; + return false; +} + +void QQuickTextEditPrivate::init() +{ + Q_Q(QQuickTextEdit); + + q->setSmooth(smooth); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QQuickItem::ItemAcceptsInputMethod); + q->setFlag(QQuickItem::ItemHasContents); + + control = new QTextControl(q); + control->setIgnoreUnusedNavigationEvents(true); + control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable); + control->setDragEnabled(false); + + // By default, QTextControl will issue both a updateCursorRequest() and an updateRequest() + // when the cursor needs to be repainted. We need the signals to be separate to be able to + // distinguish the cursor updates so that we can avoid updating the whole subtree when the + // cursor blinks. + if (!QObject::disconnect(control, SIGNAL(updateCursorRequest(QRectF)), + control, SIGNAL(updateRequest(QRectF)))) { + qWarning("QQuickTextEditPrivate::init: Failed to disconnect updateCursorRequest and updateRequest"); + } + + // QTextControl follows the default text color + // defined by the platform, declarative text + // should be black by default + QPalette pal = control->palette(); + if (pal.color(QPalette::Text) != color) { + pal.setColor(QPalette::Text, color); + control->setPalette(pal); + } + + QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument())); + QObject::connect(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor())); + QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged())); + QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged())); + QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers())); + QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers())); + QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged())); + QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate())); + QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString))); +#ifndef QT_NO_CLIPBOARD + QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged())); + QObject::connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged())); + canPaste = control->canPaste(); +#endif + + document = control->document(); + document->setDefaultFont(font); + document->setDocumentMargin(textMargin); + document->setUndoRedoEnabled(false); // flush undo buffer. + document->setUndoRedoEnabled(true); + updateDefaultTextOption(); +} + +void QQuickTextEdit::q_textChanged() +{ + Q_D(QQuickTextEdit); + d->text = text(); + d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft(); + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); + updateSize(); + updateTotalLines(); + emit textChanged(d->text); +} + +void QQuickTextEdit::moveCursorDelegate() +{ + Q_D(QQuickTextEdit); + d->determineHorizontalAlignment(); + updateMicroFocus(); + emit cursorRectangleChanged(); + if (!d->cursor) + return; + QRectF cursorRect = cursorRectangle(); + d->cursor->setX(cursorRect.x()); + d->cursor->setY(cursorRect.y()); +} + +void QQuickTextEditPrivate::updateSelection() +{ + Q_Q(QQuickTextEdit); + QTextCursor cursor = control->textCursor(); + bool startChange = (lastSelectionStart != cursor.selectionStart()); + bool endChange = (lastSelectionEnd != cursor.selectionEnd()); + cursor.beginEditBlock(); + cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor); + cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor); + cursor.endEditBlock(); + control->setTextCursor(cursor); + if (startChange) + q->selectionStartChanged(); + if (endChange) + q->selectionEndChanged(); +} + +void QQuickTextEdit::updateSelectionMarkers() +{ + Q_D(QQuickTextEdit); + if (d->lastSelectionStart != d->control->textCursor().selectionStart()) { + d->lastSelectionStart = d->control->textCursor().selectionStart(); + emit selectionStartChanged(); + } + if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) { + d->lastSelectionEnd = d->control->textCursor().selectionEnd(); + emit selectionEndChanged(); + } +} + +QRectF QQuickTextEdit::boundingRect() const +{ + Q_D(const QQuickTextEdit); + QRectF r = QQuickImplicitSizeItem::boundingRect(); + int cursorWidth = 1; + if (d->cursor) + cursorWidth = d->cursor->width(); + if (!d->document->isEmpty()) + cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor + + // Could include font max left/right bearings to either side of rectangle. + + r.setRight(r.right() + cursorWidth); + return r.translated(0,d->yoff); +} + +qreal QQuickTextEditPrivate::getImplicitWidth() const +{ + Q_Q(const QQuickTextEdit); + if (!requireImplicitWidth) { + // We don't calculate implicitWidth unless it is required. + // We need to force a size update now to ensure implicitWidth is calculated + const_cast(this)->requireImplicitWidth = true; + const_cast(q)->updateSize(); + } + return implicitWidth; +} + +//### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't +// need to do all the calculations each time +void QQuickTextEdit::updateSize() +{ + Q_D(QQuickTextEdit); + if (isComponentComplete()) { + qreal naturalWidth = d->implicitWidth; + // ### assumes that if the width is set, the text will fill to edges + // ### (unless wrap is false, then clipping will occur) + if (widthValid()) { + if (!d->requireImplicitWidth) { + emit implicitWidthChanged(); + // if the implicitWidth is used, then updateSize() has already been called (recursively) + if (d->requireImplicitWidth) + return; + } + if (d->requireImplicitWidth) { + d->document->setTextWidth(-1); + naturalWidth = d->document->idealWidth(); + } + if (d->document->textWidth() != width()) + d->document->setTextWidth(width()); + } else { + d->document->setTextWidth(-1); + } + QFontMetrics fm = QFontMetrics(d->font); + int dy = height(); + dy -= (int)d->document->size().height(); + + int nyoff; + if (heightValid()) { + if (d->vAlign == AlignBottom) + nyoff = dy; + else if (d->vAlign == AlignVCenter) + nyoff = dy/2; + else + nyoff = 0; + } else { + nyoff = 0; + } + if (nyoff != d->yoff) + d->yoff = nyoff; + setBaselineOffset(fm.ascent() + d->yoff + d->textMargin); + + //### need to comfirm cost of always setting these + int newWidth = qCeil(d->document->idealWidth()); + if (!widthValid() && d->document->textWidth() != newWidth) + d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug) + // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. + if (!widthValid()) + setImplicitWidth(newWidth); + else if (d->requireImplicitWidth) + setImplicitWidth(naturalWidth); + qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height(); + setImplicitHeight(newHeight); + + d->paintedSize = QSize(newWidth, newHeight); + emit paintedSizeChanged(); + } else { + d->dirty = true; + } + updateDocument(); +} + +void QQuickTextEdit::updateDocument() +{ + Q_D(QQuickTextEdit); + d->documentDirty = true; + + if (isComponentComplete()) { + updateImageCache(); + update(); + } +} + +void QQuickTextEdit::updateCursor() +{ + Q_D(QQuickTextEdit); + if (isComponentComplete()) { + updateImageCache(d->control->cursorRect()); + update(); + } +} + +void QQuickTextEdit::updateTotalLines() +{ + Q_D(QQuickTextEdit); + + int subLines = 0; + + for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) { + QTextLayout *layout = it.layout(); + if (!layout) + continue; + subLines += layout->lineCount()-1; + } + + int newTotalLines = d->document->lineCount() + subLines; + if (d->lineCount != newTotalLines) { + d->lineCount = newTotalLines; + emit lineCountChanged(); + } +} + +void QQuickTextEditPrivate::updateDefaultTextOption() +{ + Q_Q(QQuickTextEdit); + QTextOption opt = document->defaultTextOption(); + int oldAlignment = opt.alignment(); + + QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QQuickTextEdit::AlignLeft) + horizontalAlignment = QQuickTextEdit::AlignRight; + else if (horizontalAlignment == QQuickTextEdit::AlignRight) + horizontalAlignment = QQuickTextEdit::AlignLeft; + } + opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign)); + + QTextOption::WrapMode oldWrapMode = opt.wrapMode(); + opt.setWrapMode(QTextOption::WrapMode(wrapMode)); + + bool oldUseDesignMetrics = opt.useDesignMetrics(); + bool useDesignMetrics = !qmlDisableDistanceField(); + opt.setUseDesignMetrics(useDesignMetrics); + + if (oldWrapMode == opt.wrapMode() + && oldAlignment == opt.alignment() + && oldUseDesignMetrics == useDesignMetrics) { + return; + } + document->setDefaultTextOption(opt); +} + + + +/*! + \qmlmethod void QtQuick2::TextEdit::openSoftwareInputPanel() + + Opens software input panels like virtual keyboards for typing, useful for + customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms + the panels are automatically opened when TextEdit element gains active focus. Input panels are + always closed if no editor has active focus. + + You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \code + import QtQuick 1.0 + TextEdit { + id: textEdit + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textEdit.activeFocus) { + textEdit.forceActiveFocus(); + textEdit.openSoftwareInputPanel(); + } else { + textEdit.focus = false; + } + } + onPressAndHold: textEdit.closeSoftwareInputPanel(); + } + } + \endcode +*/ +void QQuickTextEdit::openSoftwareInputPanel() +{ + if (qGuiApp) + qGuiApp->inputPanel()->show(); +} + +/*! + \qmlmethod void QtQuick2::TextEdit::closeSoftwareInputPanel() + + Closes a software input panel like a virtual keyboard shown on the screen, useful + for customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms + the panels are automatically opened when TextEdit element gains active focus. Input panels are + always closed if no editor has active focus. + + You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \code + import QtQuick 1.0 + TextEdit { + id: textEdit + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textEdit.activeFocus) { + textEdit.forceActiveFocus(); + textEdit.openSoftwareInputPanel(); + } else { + textEdit.focus = false; + } + } + onPressAndHold: textEdit.closeSoftwareInputPanel(); + } + } + \endcode +*/ +void QQuickTextEdit::closeSoftwareInputPanel() +{ + if (qGuiApp) + qGuiApp->inputPanel()->hide(); +} + +void QQuickTextEdit::focusInEvent(QFocusEvent *event) +{ + Q_D(const QQuickTextEdit); + if (d->focusOnPress && !isReadOnly()) + openSoftwareInputPanel(); + QQuickImplicitSizeItem::focusInEvent(event); +} + +void QQuickTextEdit::q_canPasteChanged() +{ + Q_D(QQuickTextEdit); + bool old = d->canPaste; + d->canPaste = d->control->canPaste(); + if (old!=d->canPaste) + emit canPasteChanged(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquicktextedit_p.h b/src/declarative/items/qquicktextedit_p.h new file mode 100644 index 0000000000..e237e2eba2 --- /dev/null +++ b/src/declarative/items/qquicktextedit_p.h @@ -0,0 +1,306 @@ +// Commit: 27e4302b7f45f22180693d26747f419177c81e27 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTEDIT_P_H +#define QQUICKTEXTEDIT_P_H + +#include "qquickimplicitsizeitem_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickTextEditPrivate; +class Q_AUTOTEST_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(VAlignment) + Q_ENUMS(HAlignment) + Q_ENUMS(TextFormat) + Q_ENUMS(WrapMode) + Q_ENUMS(SelectionMode) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged) + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) + Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged) + Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged) + Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) + Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged) + Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) + Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) + Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged) + Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged) + Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) + Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) + Q_PROPERTY(QDeclarativeComponent* cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) + Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) + Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) + Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectionChanged) + Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged) + Q_PROPERTY(bool persistentSelection READ persistentSelection WRITE setPersistentSelection NOTIFY persistentSelectionChanged) + Q_PROPERTY(qreal textMargin READ textMargin WRITE setTextMargin NOTIFY textMarginChanged) + Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints) + Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) + Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged) + Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged) + +public: + QQuickTextEdit(QQuickItem *parent=0); + + enum HAlignment { + AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter, + AlignJustify = Qt::AlignJustify + }; + + enum VAlignment { + AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter + }; + + enum TextFormat { + PlainText = Qt::PlainText, + RichText = Qt::RichText, + AutoText = Qt::AutoText + }; + + enum WrapMode { NoWrap = QTextOption::NoWrap, + WordWrap = QTextOption::WordWrap, + WrapAnywhere = QTextOption::WrapAnywhere, + WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT + Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere + }; + + enum SelectionMode { + SelectCharacters, + SelectWords + }; + + Q_INVOKABLE void openSoftwareInputPanel(); + Q_INVOKABLE void closeSoftwareInputPanel(); + + QString text() const; + void setText(const QString &); + + TextFormat textFormat() const; + void setTextFormat(TextFormat format); + + QFont font() const; + void setFont(const QFont &font); + + QColor color() const; + void setColor(const QColor &c); + + QColor selectionColor() const; + void setSelectionColor(const QColor &c); + + QColor selectedTextColor() const; + void setSelectedTextColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; + + VAlignment vAlign() const; + void setVAlign(VAlignment align); + + WrapMode wrapMode() const; + void setWrapMode(WrapMode w); + + int lineCount() const; + + bool isCursorVisible() const; + void setCursorVisible(bool on); + + int cursorPosition() const; + void setCursorPosition(int pos); + + QDeclarativeComponent* cursorDelegate() const; + void setCursorDelegate(QDeclarativeComponent*); + + int selectionStart() const; + int selectionEnd() const; + + QString selectedText() const; + + bool focusOnPress() const; + void setFocusOnPress(bool on); + + bool persistentSelection() const; + void setPersistentSelection(bool on); + + qreal textMargin() const; + void setTextMargin(qreal margin); + + bool selectByMouse() const; + void setSelectByMouse(bool); + + SelectionMode mouseSelectionMode() const; + void setMouseSelectionMode(SelectionMode mode); + + bool canPaste() const; + + virtual void componentComplete(); + + /* FROM EDIT */ + void setReadOnly(bool); + bool isReadOnly() const; + + void setTextInteractionFlags(Qt::TextInteractionFlags flags); + Qt::TextInteractionFlags textInteractionFlags() const; + + QRect cursorRectangle() const; + + QVariant inputMethodQuery(Qt::InputMethodQuery property) const; + + qreal paintedWidth() const; + qreal paintedHeight() const; + + Q_INVOKABLE QRectF positionToRectangle(int) const; + Q_INVOKABLE int positionAt(int x, int y) const; + Q_INVOKABLE void moveCursorSelection(int pos); + Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode); + + QRectF boundingRect() const; + + bool isInputMethodComposing() const; + +Q_SIGNALS: + void textChanged(const QString &); + void paintedSizeChanged(); + void cursorPositionChanged(); + void cursorRectangleChanged(); + void selectionStartChanged(); + void selectionEndChanged(); + void selectionChanged(); + void colorChanged(const QColor &color); + void selectionColorChanged(const QColor &color); + void selectedTextColorChanged(const QColor &color); + void fontChanged(const QFont &font); + void horizontalAlignmentChanged(HAlignment alignment); + void verticalAlignmentChanged(VAlignment alignment); + void wrapModeChanged(); + void lineCountChanged(); + void textFormatChanged(TextFormat textFormat); + void readOnlyChanged(bool isReadOnly); + void cursorVisibleChanged(bool isCursorVisible); + void cursorDelegateChanged(); + void activeFocusOnPressChanged(bool activeFocusOnPressed); + void persistentSelectionChanged(bool isPersistentSelection); + void textMarginChanged(qreal textMargin); + void selectByMouseChanged(bool selectByMouse); + void mouseSelectionModeChanged(SelectionMode mode); + void linkActivated(const QString &link); + void canPasteChanged(); + void inputMethodComposingChanged(); + void effectiveHorizontalAlignmentChanged(); + +public Q_SLOTS: + void selectAll(); + void selectWord(); + void select(int start, int end); + void deselect(); + bool isRightToLeft(int start, int end); +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + +private Q_SLOTS: + void q_textChanged(); + void updateSelectionMarkers(); + void moveCursorDelegate(); + void loadCursorDelegate(); + void q_canPasteChanged(); + void updateDocument(); + void updateCursor(); + +private: + void updateSize(); + void updateTotalLines(); + void updateImageCache(const QRectF &rect = QRectF()); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + + bool event(QEvent *); + void keyPressEvent(QKeyEvent *); + void keyReleaseEvent(QKeyEvent *); + void focusInEvent(QFocusEvent *event); + + // mouse filter? + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void inputMethodEvent(QInputMethodEvent *e); + virtual void itemChange(ItemChange, const ItemChangeData &); + + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData); + +private: + Q_DISABLE_COPY(QQuickTextEdit) + Q_DECLARE_PRIVATE(QQuickTextEdit) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTextEdit) + +QT_END_HEADER + +#endif // QQUICKTEXTEDIT_P_H diff --git a/src/declarative/items/qquicktextedit_p_p.h b/src/declarative/items/qquicktextedit_p_p.h new file mode 100644 index 0000000000..7bcbe2a189 --- /dev/null +++ b/src/declarative/items/qquicktextedit_p_p.h @@ -0,0 +1,144 @@ +// Commit: 27e4302b7f45f22180693d26747f419177c81e27 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTEDIT_P_P_H +#define QQUICKTEXTEDIT_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquicktextedit_p.h" +#include "qquickimplicitsizeitem_p_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE +class QTextLayout; +class QTextDocument; +class QTextControl; +class QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickTextEdit) + +public: + QQuickTextEditPrivate() + : color("black"), hAlign(QQuickTextEdit::AlignLeft), vAlign(QQuickTextEdit::AlignTop), + documentDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true), + persistentSelection(true), requireImplicitWidth(false), selectByMouse(false), canPaste(false), + hAlignImplicit(true), rightToLeftText(false), useImageFallback(false), + textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), cursorComponent(0), cursor(0), + format(QQuickTextEdit::AutoText), document(0), wrapMode(QQuickTextEdit::NoWrap), + mouseSelectionMode(QQuickTextEdit::SelectCharacters), + lineCount(0), yoff(0), nodeType(NodeIsNull), texture(0) + { + } + + void init(); + + void updateDefaultTextOption(); + void relayoutDocument(); + void updateSelection(); + bool determineHorizontalAlignment(); + bool setHAlign(QQuickTextEdit::HAlignment, bool forceAlign = false); + void mirrorChange(); + qreal getImplicitWidth() const; + + QString text; + QFont font; + QFont sourceFont; + QColor color; + QColor selectionColor; + QColor selectedTextColor; + QString style; + QColor styleColor; + QQuickTextEdit::HAlignment hAlign; + QQuickTextEdit::VAlignment vAlign; + + bool documentDirty : 1; + bool dirty : 1; + bool richText : 1; + bool cursorVisible : 1; + bool focusOnPress : 1; + bool persistentSelection : 1; + bool requireImplicitWidth:1; + bool selectByMouse:1; + bool canPaste:1; + bool hAlignImplicit:1; + bool rightToLeftText:1; + bool useImageFallback:1; + + qreal textMargin; + int lastSelectionStart; + int lastSelectionEnd; + QDeclarativeComponent* cursorComponent; + QQuickItem* cursor; + QQuickTextEdit::TextFormat format; + QTextDocument *document; + QTextControl *control; + QQuickTextEdit::WrapMode wrapMode; + QQuickTextEdit::SelectionMode mouseSelectionMode; + int lineCount; + int yoff; + QSize paintedSize; + + enum NodeType { + NodeIsNull, + NodeIsTexture, + NodeIsText + }; + NodeType nodeType; + QSGTexture *texture; + QPixmap pixmapCache; +}; + +QT_END_NAMESPACE + +#endif // QQUICKTEXTEDIT_P_P_H diff --git a/src/declarative/items/qquicktextinput.cpp b/src/declarative/items/qquicktextinput.cpp new file mode 100644 index 0000000000..e28832ed4e --- /dev/null +++ b/src/declarative/items/qquicktextinput.cpp @@ -0,0 +1,2007 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktextinput_p.h" +#include "qquicktextinput_p_p.h" +#include "qquickcanvas.h" + +#include +#include + +#include +#include +#include +#include "qquicktextnode_p.h" +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) + +/*! + \qmlclass TextInput QQuickTextInput + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + \brief The TextInput item displays an editable line of text. + \inherits Item + + The TextInput element displays a single line of editable plain text. + + TextInput is used to accept a line of text input. Input constraints + can be placed on a TextInput item (for example, through a \l validator or \l inputMask), + and setting \l echoMode to an appropriate value enables TextInput to be used for + a password input field. + + On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled. + If you want such bindings (on any platform), you will need to construct them in QML. + + \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example} +*/ +QQuickTextInput::QQuickTextInput(QQuickItem* parent) +: QQuickImplicitSizeItem(*(new QQuickTextInputPrivate), parent) +{ + Q_D(QQuickTextInput); + d->init(); +} + +QQuickTextInput::~QQuickTextInput() +{ +} + +/*! + \qmlproperty string QtQuick2::TextInput::text + + The text in the TextInput. +*/ +QString QQuickTextInput::text() const +{ + Q_D(const QQuickTextInput); + return d->control->text(); +} + +void QQuickTextInput::setText(const QString &s) +{ + Q_D(QQuickTextInput); + if (s == text()) + return; + d->control->setText(s); +} + +/*! + \qmlproperty string QtQuick2::TextInput::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool QtQuick2::TextInput::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration QtQuick2::TextInput::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + TextInput { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool QtQuick2::TextInput::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool QtQuick2::TextInput::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool QtQuick2::TextInput::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real QtQuick2::TextInput::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int QtQuick2::TextInput::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. + Use \c pointSize to set the size of the font in a device independent manner. +*/ + +/*! + \qmlproperty real QtQuick2::TextInput::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real QtQuick2::TextInput::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration QtQuick2::TextInput::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + TextInput { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ + +QFont QQuickTextInput::font() const +{ + Q_D(const QQuickTextInput); + return d->sourceFont; +} + +void QQuickTextInput::setFont(const QFont &font) +{ + Q_D(QQuickTextInput); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; + d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + if (oldFont != d->font) { + d->control->setFont(d->font); + updateSize(); + updateCursorRectangle(); + if (d->cursorItem) { + d->cursorItem->setHeight(QFontMetrics(d->font).height()); + } + } + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty color QtQuick2::TextInput::color + + The text color. +*/ +QColor QQuickTextInput::color() const +{ + Q_D(const QQuickTextInput); + return d->color; +} + +void QQuickTextInput::setColor(const QColor &c) +{ + Q_D(QQuickTextInput); + if (c != d->color) { + d->color = c; + update(); + emit colorChanged(c); + } +} + + +/*! + \qmlproperty color QtQuick2::TextInput::selectionColor + + The text highlight color, used behind selections. +*/ +QColor QQuickTextInput::selectionColor() const +{ + Q_D(const QQuickTextInput); + return d->selectionColor; +} + +void QQuickTextInput::setSelectionColor(const QColor &color) +{ + Q_D(QQuickTextInput); + if (d->selectionColor == color) + return; + + d->selectionColor = color; + QPalette p = d->control->palette(); + p.setColor(QPalette::Highlight, d->selectionColor); + d->control->setPalette(p); + if (d->control->hasSelectedText()) + update(); + emit selectionColorChanged(color); +} +/*! + \qmlproperty color QtQuick2::TextInput::selectedTextColor + + The highlighted text color, used in selections. +*/ +QColor QQuickTextInput::selectedTextColor() const +{ + Q_D(const QQuickTextInput); + return d->selectedTextColor; +} + +void QQuickTextInput::setSelectedTextColor(const QColor &color) +{ + Q_D(QQuickTextInput); + if (d->selectedTextColor == color) + return; + + d->selectedTextColor = color; + QPalette p = d->control->palette(); + p.setColor(QPalette::HighlightedText, d->selectedTextColor); + d->control->setPalette(p); + if (d->control->hasSelectedText()) + update(); + emit selectedTextColorChanged(color); +} + +/*! + \qmlproperty enumeration QtQuick2::TextInput::horizontalAlignment + \qmlproperty enumeration QtQuick2::TextInput::effectiveHorizontalAlignment + + Sets the horizontal alignment of the text within the TextInput item's + width and height. By default, the text alignment follows the natural alignment + of the text, for example text that is read from left to right will be aligned to + the left. + + TextInput does not have vertical alignment, as the natural height is + exactly the height of the single line of text. If you set the height + manually to something larger, TextInput will always be top aligned + vertically. You can use anchors to align it however you want within + another item. + + The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and + \c TextInput.AlignHCenter. + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextInput, use the read-only property \c effectiveHorizontalAlignment. +*/ +QQuickTextInput::HAlignment QQuickTextInput::hAlign() const +{ + Q_D(const QQuickTextInput); + return d->hAlign; +} + +void QQuickTextInput::setHAlign(HAlignment align) +{ + Q_D(QQuickTextInput); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + updateCursorRectangle(); + } +} + +void QQuickTextInput::resetHAlign() +{ + Q_D(QQuickTextInput); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + updateCursorRectangle(); + } +} + +QQuickTextInput::HAlignment QQuickTextInput::effectiveHAlign() const +{ + Q_D(const QQuickTextInput); + QQuickTextInput::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QQuickTextInput::AlignLeft: + effectiveAlignment = QQuickTextInput::AlignRight; + break; + case QQuickTextInput::AlignRight: + effectiveAlignment = QQuickTextInput::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment alignment, bool forceAlign) +{ + Q_Q(QQuickTextInput); + if ((hAlign != alignment || forceAlign) && alignment <= QQuickTextInput::AlignHCenter) { // justify not supported + QQuickTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QQuickTextInputPrivate::determineHorizontalAlignment() +{ + if (hAlignImplicit) { + // if no explicit alignment has been set, follow the natural layout direction of the text + QString text = control->text(); + if (text.isEmpty()) + text = control->preeditAreaText(); + bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft(); + return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft); + } + return false; +} + +void QQuickTextInputPrivate::mirrorChange() +{ + Q_Q(QQuickTextInput); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QQuickTextInput::AlignRight || hAlign == QQuickTextInput::AlignLeft)) { + q->updateCursorRectangle(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +/*! + \qmlproperty bool QtQuick2::TextInput::readOnly + + Sets whether user input can modify the contents of the TextInput. + + If readOnly is set to true, then user input will not affect the text + property. Any bindings or attempts to set the text property will still + work. +*/ +bool QQuickTextInput::isReadOnly() const +{ + Q_D(const QQuickTextInput); + return d->control->isReadOnly(); +} + +void QQuickTextInput::setReadOnly(bool ro) +{ + Q_D(QQuickTextInput); + if (d->control->isReadOnly() == ro) + return; + + setFlag(QQuickItem::ItemAcceptsInputMethod, !ro); + d->control->setReadOnly(ro); + if (!ro) + d->control->setCursorPosition(d->control->end()); + + emit readOnlyChanged(ro); +} + +/*! + \qmlproperty int QtQuick2::TextInput::maximumLength + The maximum permitted length of the text in the TextInput. + + If the text is too long, it is truncated at the limit. + + By default, this property contains a value of 32767. +*/ +int QQuickTextInput::maxLength() const +{ + Q_D(const QQuickTextInput); + return d->control->maxLength(); +} + +void QQuickTextInput::setMaxLength(int ml) +{ + Q_D(QQuickTextInput); + if (d->control->maxLength() == ml) + return; + + d->control->setMaxLength(ml); + + emit maximumLengthChanged(ml); +} + +/*! + \qmlproperty bool QtQuick2::TextInput::cursorVisible + Set to true when the TextInput shows a cursor. + + This property is set and unset when the TextInput gets active focus, so that other + properties can be bound to whether the cursor is currently showing. As it + gets set and unset automatically, when you set the value yourself you must + keep in mind that your value may be overwritten. + + It can be set directly in script, for example if a KeyProxy might + forward keys to it and you desire it to look active when this happens + (but without actually giving it active focus). + + It should not be set directly on the element, like in the below QML, + as the specified value will be overridden an lost on focus changes. + + \code + TextInput { + text: "Text" + cursorVisible: false + } + \endcode + + In the above snippet the cursor will still become visible when the + TextInput gains active focus. +*/ +bool QQuickTextInput::isCursorVisible() const +{ + Q_D(const QQuickTextInput); + return d->cursorVisible; +} + +void QQuickTextInput::setCursorVisible(bool on) +{ + Q_D(QQuickTextInput); + if (d->cursorVisible == on) + return; + d->cursorVisible = on; + d->control->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0); + QRect r = d->control->cursorRect(); + if (d->control->inputMask().isEmpty()) + updateRect(r); + else + updateRect(); + emit cursorVisibleChanged(d->cursorVisible); +} + +/*! + \qmlproperty int QtQuick2::TextInput::cursorPosition + The position of the cursor in the TextInput. +*/ +int QQuickTextInput::cursorPosition() const +{ + Q_D(const QQuickTextInput); + return d->control->cursor(); +} +void QQuickTextInput::setCursorPosition(int cp) +{ + Q_D(QQuickTextInput); + if (cp < 0 || cp > d->control->text().length()) + return; + d->control->moveCursor(cp); +} + +/*! + Returns a Rect which encompasses the cursor, but which may be larger than is + required. Ignores custom cursor delegates. +*/ +QRect QQuickTextInput::cursorRectangle() const +{ + Q_D(const QQuickTextInput); + QRect r = d->control->cursorRect(); + // Scroll and make consistent with TextEdit + // QLineControl inexplicably adds 1 to the height and horizontal padding + // for unicode direction markers. + r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1); + return r; +} +/*! + \qmlproperty int QtQuick2::TextInput::selectionStart + + The cursor position before the first character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionEnd, cursorPosition, selectedText +*/ +int QQuickTextInput::selectionStart() const +{ + Q_D(const QQuickTextInput); + return d->lastSelectionStart; +} +/*! + \qmlproperty int QtQuick2::TextInput::selectionEnd + + The cursor position after the last character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionStart, cursorPosition, selectedText +*/ +int QQuickTextInput::selectionEnd() const +{ + Q_D(const QQuickTextInput); + return d->lastSelectionEnd; +} +/*! + \qmlmethod void QtQuick2::TextInput::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd +*/ +void QQuickTextInput::select(int start, int end) +{ + Q_D(QQuickTextInput); + if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length()) + return; + d->control->setSelection(start, end-start); +} + +/*! + \qmlproperty string QtQuick2::TextInput::selectedText + + This read-only property provides the text currently selected in the + text input. + + It is equivalent to the following snippet, but is faster and easier + to use. + + \js + myTextInput.text.toString().substring(myTextInput.selectionStart, + myTextInput.selectionEnd); + \endjs +*/ +QString QQuickTextInput::selectedText() const +{ + Q_D(const QQuickTextInput); + return d->control->selectedText(); +} + +/*! + \qmlproperty bool QtQuick2::TextInput::activeFocusOnPress + + Whether the TextInput should gain active focus on a mouse press. By default this is + set to true. +*/ +bool QQuickTextInput::focusOnPress() const +{ + Q_D(const QQuickTextInput); + return d->focusOnPress; +} + +void QQuickTextInput::setFocusOnPress(bool b) +{ + Q_D(QQuickTextInput); + if (d->focusOnPress == b) + return; + + d->focusOnPress = b; + + emit activeFocusOnPressChanged(d->focusOnPress); +} +/*! + \qmlproperty bool QtQuick2::TextInput::autoScroll + + Whether the TextInput should scroll when the text is longer than the width. By default this is + set to true. +*/ +bool QQuickTextInput::autoScroll() const +{ + Q_D(const QQuickTextInput); + return d->autoScroll; +} + +void QQuickTextInput::setAutoScroll(bool b) +{ + Q_D(QQuickTextInput); + if (d->autoScroll == b) + return; + + d->autoScroll = b; + //We need to repaint so that the scrolling is taking into account. + updateSize(true); + updateCursorRectangle(); + emit autoScrollChanged(d->autoScroll); +} + +#ifndef QT_NO_VALIDATOR + +/*! + \qmlclass IntValidator QIntValidator + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + + This element provides a validator for integer values. + + IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and + will accept locale specific digits, group separators, and positive and negative signs. In + addition, IntValidator is always guaranteed to accept a number formatted according to the "C" + locale. +*/ +/*! + \qmlproperty int QtQuick2::IntValidator::top + + This property holds the validator's highest acceptable value. + By default, this property's value is derived from the highest signed integer available (typically 2147483647). +*/ +/*! + \qmlproperty int QtQuick2::IntValidator::bottom + + This property holds the validator's lowest acceptable value. + By default, this property's value is derived from the lowest signed integer available (typically -2147483647). +*/ + +/*! + \qmlclass DoubleValidator QDoubleValidator + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + + This element provides a validator for non-integer numbers. +*/ + +/*! + \qmlproperty real QtQuick2::DoubleValidator::top + + This property holds the validator's maximum acceptable value. + By default, this property contains a value of infinity. +*/ +/*! + \qmlproperty real QtQuick2::DoubleValidator::bottom + + This property holds the validator's minimum acceptable value. + By default, this property contains a value of -infinity. +*/ +/*! + \qmlproperty int QtQuick2::DoubleValidator::decimals + + This property holds the validator's maximum number of digits after the decimal point. + By default, this property contains a value of 1000. +*/ +/*! + \qmlproperty enumeration QtQuick2::DoubleValidator::notation + This property holds the notation of how a string can describe a number. + + The possible values for this property are: + + \list + \o DoubleValidator.StandardNotation + \o DoubleValidator.ScientificNotation (default) + \endlist + + If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2). +*/ + +/*! + \qmlclass RegExpValidator QRegExpValidator + \inqmlmodule QtQuick 2 + \ingroup qml-basic-visual-elements + + This element provides a validator, which counts as valid any string which + matches a specified regular expression. +*/ +/*! + \qmlproperty regExp QtQuick2::RegExpValidator::regExp + + This property holds the regular expression used for validation. + + Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression + matching "a". + + By default, this property contains a regular expression with the pattern .* that matches any string. +*/ + +/*! + \qmlproperty Validator QtQuick2::TextInput::validator + + Allows you to set a validator on the TextInput. When a validator is set + the TextInput will only accept input which leaves the text property in + an acceptable or intermediate state. The accepted signal will only be sent + if the text is in an acceptable state when enter is pressed. + + Currently supported validators are IntValidator, DoubleValidator and + RegExpValidator. An example of using validators is shown below, which allows + input of integers between 11 and 31 into the text input: + + \code + import QtQuick 1.0 + TextInput{ + validator: IntValidator{bottom: 11; top: 31;} + focus: true + } + \endcode + + \sa acceptableInput, inputMask +*/ + +QValidator* QQuickTextInput::validator() const +{ + Q_D(const QQuickTextInput); + //###const cast isn't good, but needed for property system? + return const_cast(d->control->validator()); +} + +void QQuickTextInput::setValidator(QValidator* v) +{ + Q_D(QQuickTextInput); + if (d->control->validator() == v) + return; + + d->control->setValidator(v); + if (!d->control->hasAcceptableInput()) { + d->oldValidity = false; + emit acceptableInputChanged(); + } + + emit validatorChanged(); +} +#endif // QT_NO_VALIDATOR + +/*! + \qmlproperty string QtQuick2::TextInput::inputMask + + Allows you to set an input mask on the TextInput, restricting the allowable + text inputs. See QLineEdit::inputMask for further details, as the exact + same mask strings are used by TextInput. + + \sa acceptableInput, validator +*/ +QString QQuickTextInput::inputMask() const +{ + Q_D(const QQuickTextInput); + return d->control->inputMask(); +} + +void QQuickTextInput::setInputMask(const QString &im) +{ + Q_D(QQuickTextInput); + if (d->control->inputMask() == im) + return; + + d->control->setInputMask(im); + emit inputMaskChanged(d->control->inputMask()); +} + +/*! + \qmlproperty bool QtQuick2::TextInput::acceptableInput + + This property is always true unless a validator or input mask has been set. + If a validator or input mask has been set, this property will only be true + if the current text is acceptable to the validator or input mask as a final + string (not as an intermediate string). +*/ +bool QQuickTextInput::hasAcceptableInput() const +{ + Q_D(const QQuickTextInput); + return d->control->hasAcceptableInput(); +} + +/*! + \qmlsignal QtQuick2::TextInput::onAccepted() + + This handler is called when the Return or Enter key is pressed. + Note that if there is a \l validator or \l inputMask set on the text + input, the handler will only be emitted if the input is in an acceptable + state. +*/ + +void QQuickTextInputPrivate::updateInputMethodHints() +{ + Q_Q(QQuickTextInput); + Qt::InputMethodHints hints = inputMethodHints; + uint echo = control->echoMode(); + if (echo == QQuickTextInput::Password || echo == QQuickTextInput::NoEcho) + hints |= Qt::ImhHiddenText; + else if (echo == QQuickTextInput::PasswordEchoOnEdit) + hints &= ~Qt::ImhHiddenText; + if (echo != QQuickTextInput::Normal) + hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText); + q->setInputMethodHints(hints); +} +/*! + \qmlproperty enumeration QtQuick2::TextInput::echoMode + + Specifies how the text should be displayed in the TextInput. + \list + \o TextInput.Normal - Displays the text as it is. (Default) + \o TextInput.Password - Displays asterixes instead of characters. + \o TextInput.NoEcho - Displays nothing. + \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered + while editing, otherwise displays asterisks. + \endlist +*/ +QQuickTextInput::EchoMode QQuickTextInput::echoMode() const +{ + Q_D(const QQuickTextInput); + return (QQuickTextInput::EchoMode)d->control->echoMode(); +} + +void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo) +{ + Q_D(QQuickTextInput); + if (echoMode() == echo) + return; + d->control->setEchoMode((QLineControl::EchoMode)echo); + d->updateInputMethodHints(); + q_textChanged(); + emit echoModeChanged(echoMode()); +} + +Qt::InputMethodHints QQuickTextInput::imHints() const +{ + Q_D(const QQuickTextInput); + return d->inputMethodHints; +} + +void QQuickTextInput::setIMHints(Qt::InputMethodHints hints) +{ + Q_D(QQuickTextInput); + if (d->inputMethodHints == hints) + return; + d->inputMethodHints = hints; + d->updateInputMethodHints(); +} + +/*! + \qmlproperty Component QtQuick2::TextInput::cursorDelegate + The delegate for the cursor in the TextInput. + + If you set a cursorDelegate for a TextInput, this delegate will be used for + drawing the cursor instead of the standard cursor. An instance of the + delegate will be created and managed by the TextInput when a cursor is + needed, and the x property of delegate instance will be set so as + to be one pixel before the top left of the current character. + + Note that the root item of the delegate component must be a QDeclarativeItem or + QDeclarativeItem derived item. +*/ +QDeclarativeComponent* QQuickTextInput::cursorDelegate() const +{ + Q_D(const QQuickTextInput); + return d->cursorComponent; +} + +void QQuickTextInput::setCursorDelegate(QDeclarativeComponent* c) +{ + Q_D(QQuickTextInput); + if (d->cursorComponent == c) + return; + + d->cursorComponent = c; + if (!c) { + //note that the components are owned by something else + delete d->cursorItem; + } else { + d->startCreatingCursor(); + } + + emit cursorDelegateChanged(); +} + +void QQuickTextInputPrivate::startCreatingCursor() +{ + Q_Q(QQuickTextInput); + if (cursorComponent->isReady()) { + q->createCursor(); + } else if (cursorComponent->isLoading()) { + q->connect(cursorComponent, SIGNAL(statusChanged(int)), + q, SLOT(createCursor())); + } else { // isError + qmlInfo(q, cursorComponent->errors()) << QQuickTextInput::tr("Could not load cursor delegate"); + } +} + +void QQuickTextInput::createCursor() +{ + Q_D(QQuickTextInput); + if (d->cursorComponent->isError()) { + qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate"); + return; + } + + if (!d->cursorComponent->isReady()) + return; + + if (d->cursorItem) + delete d->cursorItem; + QDeclarativeContext *creationContext = d->cursorComponent->creationContext(); + QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this)); + d->cursorItem = qobject_cast(object); + if (!d->cursorItem) { + delete object; + qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate"); + return; + } + + QDeclarative_setParent_noEvent(d->cursorItem, this); + d->cursorItem->setParentItem(this); + d->cursorItem->setX(d->control->cursorToX()); + d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. +} + +/*! + \qmlmethod rect QtQuick2::TextInput::positionToRectangle(int pos) + + This function takes a character position and returns the rectangle that the + cursor would occupy, if it was placed at that character position. + + This is similar to setting the cursorPosition, and then querying the cursor + rectangle, but the cursorPosition is not changed. +*/ +QRectF QQuickTextInput::positionToRectangle(int pos) const +{ + Q_D(const QQuickTextInput); + if (pos > d->control->cursorPosition()) + pos += d->control->preeditAreaText().length(); + return QRectF(d->control->cursorToX(pos)-d->hscroll, + 0.0, + d->control->cursorWidth(), + cursorRectangle().height()); +} + +/*! + \qmlmethod int QtQuick2::TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters) + + This function returns the character position at + x pixels from the left of the textInput. Position 0 is before the + first character, position 1 is after the first character but before the second, + and so on until position text.length, which is after all characters. + + This means that for all x values before the first character this function returns 0, + and for all x values after the last character this function returns text.length. + + The cursor position type specifies how the cursor position should be resolved. + + \list + \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x. + \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x. + \endlist +*/ +int QQuickTextInput::positionAt(int x) const +{ + return positionAt(x, CursorBetweenCharacters); +} + +int QQuickTextInput::positionAt(int x, CursorPosition position) const +{ + Q_D(const QQuickTextInput); + int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); + const int cursor = d->control->cursor(); + if (pos > cursor) { + const int preeditLength = d->control->preeditAreaText().length(); + pos = pos > cursor + preeditLength + ? pos - preeditLength + : cursor; + } + return pos; +} + +void QQuickTextInput::keyPressEvent(QKeyEvent* ev) +{ + Q_D(QQuickTextInput); + // Don't allow MacOSX up/down support, and we don't allow a completer. + bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier; + if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) { + // Ignore when moving off the end unless there is a selection, + // because then moving will do something (deselect). + int cursorPosition = d->control->cursor(); + if (cursorPosition == 0) + ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right); + if (cursorPosition == d->control->text().length()) + ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left); + } + if (ignore) { + ev->ignore(); + } else { + d->control->processKeyEvent(ev); + } + if (!ev->isAccepted()) + QQuickImplicitSizeItem::keyPressEvent(ev); +} + +void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev) +{ + Q_D(QQuickTextInput); + const bool wasComposing = d->control->preeditAreaText().length() > 0; + if (d->control->isReadOnly()) { + ev->ignore(); + } else { + d->control->processInputMethodEvent(ev); + } + if (!ev->isAccepted()) + QQuickImplicitSizeItem::inputMethodEvent(ev); + + if (wasComposing != (d->control->preeditAreaText().length() > 0)) + emit inputMethodComposingChanged(); +} + +void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickTextInput); + if (d->sendMouseEventToInputContext(event)) + return; + if (d->selectByMouse) { + int cursor = d->xToPos(event->localPos().x()); + d->control->selectWordAtPos(cursor); + event->setAccepted(true); + if (!d->hasPendingTripleClick()) { + d->tripleClickStartPoint = event->localPos().toPoint(); + d->tripleClickTimer.start(); + } + } else { + QQuickImplicitSizeItem::mouseDoubleClickEvent(event); + } +} + +void QQuickTextInput::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickTextInput); + if (d->sendMouseEventToInputContext(event)) + return; + if (d->focusOnPress) { + bool hadActiveFocus = hasActiveFocus(); + forceActiveFocus(); + // re-open input panel on press if already focused + if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) + openSoftwareInputPanel(); + } + if (d->selectByMouse) { + setKeepMouseGrab(false); + d->selectPressed = true; + d->pressPos = event->localPos(); + QPoint distanceVector = d->pressPos.toPoint() - d->tripleClickStartPoint; + if (d->hasPendingTripleClick() + && distanceVector.manhattanLength() < qApp->styleHints()->startDragDistance()) { + event->setAccepted(true); + selectAll(); + return; + } + } + bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; + int cursor = d->xToPos(event->localPos().x()); + d->control->moveCursor(cursor, mark); + event->setAccepted(true); +} + +void QQuickTextInput::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickTextInput); + if (d->sendMouseEventToInputContext(event)) + return; + if (d->selectPressed) { + if (qAbs(int(event->localPos().x() - d->pressPos.x())) > qApp->styleHints()->startDragDistance()) + setKeepMouseGrab(true); + moveCursorSelection(d->xToPos(event->localPos().x()), d->mouseSelectionMode); + event->setAccepted(true); + } else { + QQuickImplicitSizeItem::mouseMoveEvent(event); + } +} + +void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickTextInput); + if (d->sendMouseEventToInputContext(event)) + return; + if (d->selectPressed) { + d->selectPressed = false; + setKeepMouseGrab(false); + } + d->control->processEvent(event); + if (!event->isAccepted()) + QQuickImplicitSizeItem::mouseReleaseEvent(event); +} + +bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event) +{ +#if !defined QT_NO_IM + if (control->composeMode() && event->type() == QEvent::KeyRelease) { + int tmp_cursor = xToPos(event->localPos().x()); + int mousePos = tmp_cursor - control->cursor(); + if (mousePos < 0 || mousePos > control->preeditAreaText().length()) { + mousePos = -1; + // don't send move events outside the preedit area + if (event->type() == QEvent::MouseMove) + return true; + } + + // may be causing reset() in some input methods + qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos); + if (!control->preeditAreaText().isEmpty()) + return true; + } +#else + Q_UNUSED(event); + Q_UNUSED(eventType) +#endif + + return false; +} + +void QQuickTextInput::mouseUngrabEvent() +{ + Q_D(QQuickTextInput); + d->selectPressed = false; + setKeepMouseGrab(false); +} + +bool QQuickTextInput::event(QEvent* ev) +{ + Q_D(QQuickTextInput); + //Anything we don't deal with ourselves, pass to the control + bool handled = false; + switch (ev->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease://###Should the control be doing anything with release? + case QEvent::InputMethod: + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + break; + default: + handled = d->control->processEvent(ev); + } + if (!handled) + handled = QQuickImplicitSizeItem::event(ev); + return handled; +} + +void QQuickTextInput::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if (newGeometry.width() != oldGeometry.width()) { + updateSize(); + updateCursorRectangle(); + } + QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); +} + +int QQuickTextInputPrivate::calculateTextWidth() +{ + return qRound(control->naturalTextWidth()); +} + +void QQuickTextInputPrivate::updateHorizontalScroll() +{ + Q_Q(QQuickTextInput); + const int preeditLength = control->preeditAreaText().length(); + const int width = q->width(); + int widthUsed = calculateTextWidth(); + + if (!autoScroll || widthUsed <= width) { + QQuickTextInput::HAlignment effectiveHAlign = q->effectiveHAlign(); + // text fits in br; use hscroll for alignment + switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { + case Qt::AlignRight: + hscroll = widthUsed - width; + break; + case Qt::AlignHCenter: + hscroll = (widthUsed - width) / 2; + break; + default: + // Left + hscroll = 0; + break; + } + } else { + int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); + if (cix - hscroll >= width) { + // text doesn't fit, cursor is to the right of br (scroll right) + hscroll = cix - width; + } else if (cix - hscroll < 0 && hscroll < widthUsed) { + // text doesn't fit, cursor is to the left of br (scroll left) + hscroll = cix; + } else if (widthUsed - hscroll < width) { + // text doesn't fit, text document is to the left of br; align + // right + hscroll = widthUsed - width; + } + if (preeditLength > 0) { + // check to ensure long pre-edit text doesn't push the cursor + // off to the left + cix = qRound(control->cursorToX( + control->cursor() + qMax(0, control->preeditCursor() - 1))); + if (cix < hscroll) + hscroll = cix; + } + } +} + +QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + Q_D(QQuickTextInput); + + QQuickTextNode *node = static_cast(oldNode); + if (node == 0) + node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext()); + d->textNode = node; + + if (!d->textLayoutDirty) { + QSGSimpleRectNode *cursorNode = node->cursorNode(); + if (cursorNode != 0 && !isReadOnly()) { + QFontMetrics fm = QFontMetrics(d->font); + // the y offset is there to keep the baseline constant in case we have script changes in the text. + QPoint offset(-d->hscroll, fm.ascent() - d->control->ascent()); + offset.rx() += d->control->cursorToX(); + + QRect br(boundingRect().toRect()); + cursorNode->setRect(QRectF(offset, QSizeF(d->control->cursorWidth(), br.height()))); + + if (!d->cursorVisible + || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) { + d->hideCursor(); + } else { + d->showCursor(); + } + } + } else { + node->deleteContent(); + node->setMatrix(QMatrix4x4()); + + QPoint offset = QPoint(0,0); + QFontMetrics fm = QFontMetrics(d->font); + QRect br(boundingRect().toRect()); + if (d->autoScroll) { + // the y offset is there to keep the baseline constant in case we have script changes in the text. + offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent()); + } else { + offset = QPoint(d->hscroll, 0); + } + + QTextLayout *textLayout = d->control->textLayout(); + if (!textLayout->text().isEmpty()) { + node->addTextLayout(offset, textLayout, d->color, + QQuickText::Normal, QColor(), + d->selectionColor, d->selectedTextColor, + d->control->selectionStart(), + d->control->selectionEnd() - 1); // selectionEnd() returns first char after + // selection + } + + if (!isReadOnly() && d->cursorItem == 0) { + offset.rx() += d->control->cursorToX(); + node->setCursor(QRectF(offset, QSizeF(d->control->cursorWidth(), br.height())), d->color); + if (!d->cursorVisible + || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) { + d->hideCursor(); + } else { + d->showCursor(); + } + } + + d->textLayoutDirty = false; + } + + return node; +} + +QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QQuickTextInput); + switch (property) { + case Qt::ImEnabled: + return QVariant((bool)(flags() & ItemAcceptsInputMethod)); + case Qt::ImHints: + return QVariant((int)inputMethodHints()); + case Qt::ImCursorRectangle: + return cursorRectangle(); + case Qt::ImFont: + return font(); + case Qt::ImCursorPosition: + return QVariant(d->control->cursor()); + case Qt::ImSurroundingText: + if (d->control->echoMode() == QLineControl::PasswordEchoOnEdit + && !d->control->passwordEchoEditing()) { + return QVariant(displayText()); + } else { + return QVariant(text()); + } + case Qt::ImCurrentSelection: + return QVariant(selectedText()); + case Qt::ImMaximumTextLength: + return QVariant(maxLength()); + case Qt::ImAnchorPosition: + if (d->control->selectionStart() == d->control->selectionEnd()) + return QVariant(d->control->cursor()); + else if (d->control->selectionStart() == d->control->cursor()) + return QVariant(d->control->selectionEnd()); + else + return QVariant(d->control->selectionStart()); + default: + return QVariant(); + } +} + +/*! + \qmlmethod void QtQuick2::TextInput::deselect() + + Removes active text selection. +*/ +void QQuickTextInput::deselect() +{ + Q_D(QQuickTextInput); + d->control->deselect(); +} + +/*! + \qmlmethod void QtQuick2::TextInput::selectAll() + + Causes all text to be selected. +*/ +void QQuickTextInput::selectAll() +{ + Q_D(QQuickTextInput); + d->control->setSelection(0, d->control->text().length()); +} + +/*! + \qmlmethod void QtQuick2::TextInput::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. +*/ +bool QQuickTextInput::isRightToLeft(int start, int end) +{ + Q_D(QQuickTextInput); + if (start > end) { + qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start."; + return false; + } else { + return d->control->text().mid(start, end - start).isRightToLeft(); + } +} + +#ifndef QT_NO_CLIPBOARD +/*! + \qmlmethod QtQuick2::TextInput::cut() + + Moves the currently selected text to the system clipboard. +*/ +void QQuickTextInput::cut() +{ + Q_D(QQuickTextInput); + d->control->copy(); + d->control->del(); +} + +/*! + \qmlmethod QtQuick2::TextInput::copy() + + Copies the currently selected text to the system clipboard. +*/ +void QQuickTextInput::copy() +{ + Q_D(QQuickTextInput); + d->control->copy(); +} + +/*! + \qmlmethod QtQuick2::TextInput::paste() + + Replaces the currently selected text by the contents of the system clipboard. +*/ +void QQuickTextInput::paste() +{ + Q_D(QQuickTextInput); + if (!d->control->isReadOnly()) + d->control->paste(); +} +#endif // QT_NO_CLIPBOARD + +/*! + \qmlmethod void QtQuick2::TextInput::selectWord() + + Causes the word closest to the current cursor position to be selected. +*/ +void QQuickTextInput::selectWord() +{ + Q_D(QQuickTextInput); + d->control->selectWordAtPos(d->control->cursor()); +} + +/*! + \qmlproperty bool QtQuick2::TextInput::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty string QtQuick2::TextInput::passwordCharacter + + This is the character displayed when echoMode is set to Password or + PasswordEchoOnEdit. By default it is an asterisk. + + If this property is set to a string with more than one character, + the first character is used. If the string is empty, the value + is ignored and the property is not set. +*/ +QString QQuickTextInput::passwordCharacter() const +{ + Q_D(const QQuickTextInput); + return QString(d->control->passwordCharacter()); +} + +void QQuickTextInput::setPasswordCharacter(const QString &str) +{ + Q_D(QQuickTextInput); + if (str.length() < 1) + return; + d->control->setPasswordCharacter(str.constData()[0]); + EchoMode echoMode_ = echoMode(); + if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) { + updateSize(); + } + emit passwordCharacterChanged(); +} + +/*! + \qmlproperty string QtQuick2::TextInput::displayText + + This is the text displayed in the TextInput. + + If \l echoMode is set to TextInput::Normal, this holds the + same value as the TextInput::text property. Otherwise, + this property holds the text visible to the user, while + the \l text property holds the actual entered text. +*/ +QString QQuickTextInput::displayText() const +{ + Q_D(const QQuickTextInput); + return d->control->displayText(); +} + +/*! + \qmlproperty bool QtQuick2::TextInput::selectByMouse + + Defaults to false. + + If true, the user can use the mouse to select text in some + platform-specific way. Note that for some platforms this may + not be an appropriate interaction (eg. may conflict with how + the text needs to behave inside a Flickable. +*/ +bool QQuickTextInput::selectByMouse() const +{ + Q_D(const QQuickTextInput); + return d->selectByMouse; +} + +void QQuickTextInput::setSelectByMouse(bool on) +{ + Q_D(QQuickTextInput); + if (d->selectByMouse != on) { + d->selectByMouse = on; + emit selectByMouseChanged(on); + } +} + +/*! + \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode + + Specifies how text should be selected using a mouse. + + \list + \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default) + \o TextInput.SelectWords - The selection is updated with whole words. + \endlist + + This property only applies when \l selectByMouse is true. +*/ + +QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const +{ + Q_D(const QQuickTextInput); + return d->mouseSelectionMode; +} + +void QQuickTextInput::setMouseSelectionMode(SelectionMode mode) +{ + Q_D(QQuickTextInput); + if (d->mouseSelectionMode != mode) { + d->mouseSelectionMode = mode; + emit mouseSelectionModeChanged(mode); + } +} + +/*! + \qmlproperty bool QtQuick2::TextInput::canPaste + + Returns true if the TextInput is writable and the content of the clipboard is + suitable for pasting into the TextEdit. +*/ +bool QQuickTextInput::canPaste() const +{ + Q_D(const QQuickTextInput); + return d->canPaste; +} + +void QQuickTextInput::moveCursorSelection(int position) +{ + Q_D(QQuickTextInput); + d->control->moveCursor(position, true); +} + +/*! + \qmlmethod void QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters) + + Moves the cursor to \a position and updates the selection according to the optional \a mode + parameter. (To only move the cursor, set the \l cursorPosition property.) + + When this method is called it additionally sets either the + selectionStart or the selectionEnd (whichever was at the previous cursor position) + to the specified position. This allows you to easily extend and contract the selected + text range. + + The selection mode specifies whether the selection is updated on a per character or a per word + basis. If not specified the selection mode will default to TextInput.SelectCharacters. + + \list + \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at + the previous cursor position) to the specified position. + \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all + words between the specified postion and the previous cursor position. Words partially in the + range are included. + \endlist + + For example, take this sequence of calls: + + \code + cursorPosition = 5 + moveCursorSelection(9, TextInput.SelectCharacters) + moveCursorSelection(7, TextInput.SelectCharacters) + \endcode + + This moves the cursor to position 5, extend the selection end from 5 to 9 + and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 + selected (the 6th and 7th characters). + + The same sequence with TextInput.SelectWords will extend the selection start to a word boundary + before or on position 5 and extend the selection end to a word boundary on or past position 9. +*/ +void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode) +{ + Q_D(QQuickTextInput); + + if (mode == SelectCharacters) { + d->control->moveCursor(pos, true); + } else if (pos != d->control->cursor()){ + const int cursor = d->control->cursor(); + int anchor; + if (!d->control->hasSelectedText()) + anchor = d->control->cursor(); + else if (d->control->selectionStart() == d->control->cursor()) + anchor = d->control->selectionEnd(); + else + anchor = d->control->selectionStart(); + + if (anchor < pos || (anchor == pos && cursor < pos)) { + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); + finder.setPosition(anchor); + + const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); + if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord) + || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) { + finder.toPreviousBoundary(); + } + anchor = finder.position() != -1 ? finder.position() : 0; + + finder.setPosition(pos); + if (pos > 0 && !finder.boundaryReasons()) + finder.toNextBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : text.length(); + + d->control->setSelection(anchor, cursor - anchor); + } else if (anchor > pos || (anchor == pos && cursor > pos)) { + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); + finder.setPosition(anchor); + + const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); + if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord) + || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) { + finder.toNextBoundary(); + } + + anchor = finder.position() != -1 ? finder.position() : text.length(); + + finder.setPosition(pos); + if (pos < text.length() && !finder.boundaryReasons()) + finder.toPreviousBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : 0; + + d->control->setSelection(anchor, cursor - anchor); + } + } +} + +/*! + \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel() + + Opens software input panels like virtual keyboards for typing, useful for + customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms + the panels are automatically opened when TextInput element gains active focus. Input panels are + always closed if no editor has active focus. + + . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \qml + import QtQuick 1.0 + TextInput { + id: textInput + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textInput.activeFocus) { + textInput.forceActiveFocus() + textInput.openSoftwareInputPanel(); + } else { + textInput.focus = false; + } + } + onPressAndHold: textInput.closeSoftwareInputPanel(); + } + } + \endqml +*/ +void QQuickTextInput::openSoftwareInputPanel() +{ + if (qGuiApp) + qGuiApp->inputPanel()->show(); +} + +/*! + \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel() + + Closes a software input panel like a virtual keyboard shown on the screen, useful + for customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms + the panels are automatically opened when TextInput element gains active focus. Input panels are + always closed if no editor has active focus. + + . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \qml + import QtQuick 1.0 + TextInput { + id: textInput + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textInput.activeFocus) { + textInput.forceActiveFocus(); + textInput.openSoftwareInputPanel(); + } else { + textInput.focus = false; + } + } + onPressAndHold: textInput.closeSoftwareInputPanel(); + } + } + \endqml +*/ +void QQuickTextInput::closeSoftwareInputPanel() +{ + if (qGuiApp) + qGuiApp->inputPanel()->hide(); +} + +void QQuickTextInput::focusInEvent(QFocusEvent *event) +{ + Q_D(const QQuickTextInput); + if (d->focusOnPress && !isReadOnly()) + openSoftwareInputPanel(); + QQuickImplicitSizeItem::focusInEvent(event); +} + +void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickTextInput); + if (change == ItemActiveFocusHasChanged) { + bool hasFocus = value.boolValue; + d->focused = hasFocus; + setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus() + if (echoMode() == QQuickTextInput::PasswordEchoOnEdit && !hasFocus) + d->control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events + if (!hasFocus) + d->control->deselect(); + } + QQuickItem::itemChange(change, value); +} + +/*! + \qmlproperty bool QtQuick2::TextInput::inputMethodComposing + + + This property holds whether the TextInput has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextInput to edit or commit the partial text. This property can be + used to determine when to disable events handlers that may interfere with + the correct operation of an input method. +*/ +bool QQuickTextInput::isInputMethodComposing() const +{ + Q_D(const QQuickTextInput); + return d->control->preeditAreaText().length() > 0; +} + +void QQuickTextInputPrivate::init() +{ + Q_Q(QQuickTextInput); +#if defined(Q_WS_MAC) + control->setThreadChecks(true); +#endif + control->setParent(q);//Now mandatory due to accessibility changes + control->setCursorWidth(1); + control->setPasswordCharacter(QLatin1Char('*')); + q->setSmooth(smooth); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QQuickItem::ItemAcceptsInputMethod); + q->setFlag(QQuickItem::ItemHasContents); + q->connect(control, SIGNAL(cursorPositionChanged(int,int)), + q, SLOT(cursorPosChanged())); + q->connect(control, SIGNAL(selectionChanged()), + q, SLOT(selectionChanged())); + q->connect(control, SIGNAL(textChanged(QString)), + q, SLOT(q_textChanged())); + q->connect(control, SIGNAL(accepted()), + q, SIGNAL(accepted())); + q->connect(control, SIGNAL(updateNeeded(QRect)), + q, SLOT(updateRect(QRect))); +#ifndef QT_NO_CLIPBOARD + q->connect(q, SIGNAL(readOnlyChanged(bool)), + q, SLOT(q_canPasteChanged())); + q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), + q, SLOT(q_canPasteChanged())); + canPaste = !control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; +#endif // QT_NO_CLIPBOARD + q->connect(control, SIGNAL(updateMicroFocus()), + q, SLOT(updateCursorRectangle())); + q->connect(control, SIGNAL(displayTextChanged(QString)), + q, SLOT(updateRect())); + q->updateSize(); + imHints &= ~Qt::ImhMultiLine; + oldValidity = control->hasAcceptableInput(); + lastSelectionStart = 0; + lastSelectionEnd = 0; + QPalette p = control->palette(); + selectedTextColor = p.color(QPalette::HighlightedText); + selectionColor = p.color(QPalette::Highlight); + determineHorizontalAlignment(); + + if (!qmlDisableDistanceField()) { + QTextOption option = control->textLayout()->textOption(); + option.setUseDesignMetrics(true); + control->textLayout()->setTextOption(option); + } +} + +void QQuickTextInput::cursorPosChanged() +{ + Q_D(QQuickTextInput); + updateCursorRectangle(); + emit cursorPositionChanged(); + // XXX todo - not in 4.8? +#if 0 + d->control->resetCursorBlinkTimer(); +#endif + + if (!d->control->hasSelectedText()) { + if (d->lastSelectionStart != d->control->cursor()) { + d->lastSelectionStart = d->control->cursor(); + emit selectionStartChanged(); + } + if (d->lastSelectionEnd != d->control->cursor()) { + d->lastSelectionEnd = d->control->cursor(); + emit selectionEndChanged(); + } + } +} + +void QQuickTextInput::updateCursorRectangle() +{ + Q_D(QQuickTextInput); + d->determineHorizontalAlignment(); + d->updateHorizontalScroll(); + updateRect();//TODO: Only update rect between pos's + updateMicroFocus(); + emit cursorRectangleChanged(); + if (d->cursorItem) + d->cursorItem->setX(d->control->cursorToX() - d->hscroll); +} + +void QQuickTextInput::selectionChanged() +{ + Q_D(QQuickTextInput); + updateRect();//TODO: Only update rect in selection + emit selectedTextChanged(); + + if (d->lastSelectionStart != d->control->selectionStart()) { + d->lastSelectionStart = d->control->selectionStart(); + if (d->lastSelectionStart == -1) + d->lastSelectionStart = d->control->cursor(); + emit selectionStartChanged(); + } + if (d->lastSelectionEnd != d->control->selectionEnd()) { + d->lastSelectionEnd = d->control->selectionEnd(); + if (d->lastSelectionEnd == -1) + d->lastSelectionEnd = d->control->cursor(); + emit selectionEndChanged(); + } +} + +void QQuickTextInput::q_textChanged() +{ + Q_D(QQuickTextInput); + emit textChanged(); + emit displayTextChanged(); + updateSize(); + d->determineHorizontalAlignment(); + d->updateHorizontalScroll(); + updateMicroFocus(); + if (hasAcceptableInput() != d->oldValidity) { + d->oldValidity = hasAcceptableInput(); + emit acceptableInputChanged(); + } +} + +void QQuickTextInputPrivate::showCursor() +{ + if (textNode != 0 && textNode->cursorNode() != 0) + textNode->cursorNode()->setColor(color); +} + +void QQuickTextInputPrivate::hideCursor() +{ + if (textNode != 0 && textNode->cursorNode() != 0) + textNode->cursorNode()->setColor(QColor(0, 0, 0, 0)); +} + +void QQuickTextInput::updateRect(const QRect &r) +{ + Q_D(QQuickTextInput); + if (!isComponentComplete()) + return; + + if (r.isEmpty()) { + d->textLayoutDirty = true; + } + + update(); +} + +QRectF QQuickTextInput::boundingRect() const +{ + Q_D(const QQuickTextInput); + QRectF r = QQuickImplicitSizeItem::boundingRect(); + + int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth(); + + // Could include font max left/right bearings to either side of rectangle. + + r.setRight(r.right() + cursorWidth); + return r; +} + +void QQuickTextInput::updateSize(bool needsRedraw) +{ + Q_D(QQuickTextInput); + int w = width(); + int h = height(); + setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. + setImplicitWidth(d->calculateTextWidth()); + if (w==width() && h==height() && needsRedraw) + update(); +} + +void QQuickTextInput::q_canPasteChanged() +{ + Q_D(QQuickTextInput); + bool old = d->canPaste; +#ifndef QT_NO_CLIPBOARD + d->canPaste = !d->control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; +#endif + if (d->canPaste != old) + emit canPasteChanged(); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/items/qquicktextinput_p.h b/src/declarative/items/qquicktextinput_p.h new file mode 100644 index 0000000000..a322b8c73a --- /dev/null +++ b/src/declarative/items/qquicktextinput_p.h @@ -0,0 +1,303 @@ +// Commit: 2f173e4945dd8414636c1061acfaf9c2d8b718d8 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTINPUT_P_H +#define QQUICKTEXTINPUT_P_H + +#include "qquickimplicitsizeitem_p.h" +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickTextInputPrivate; +class QValidator; +class Q_AUTOTEST_EXPORT QQuickTextInput : public QQuickImplicitSizeItem +{ + Q_OBJECT + Q_ENUMS(HAlignment) + Q_ENUMS(EchoMode) + Q_ENUMS(SelectionMode) + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged) + Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged) + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) + Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) + Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged) + + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged) + Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged) + Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) + Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) + Q_PROPERTY(QDeclarativeComponent *cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) + Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) + Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) + Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged) + + Q_PROPERTY(int maximumLength READ maxLength WRITE setMaxLength NOTIFY maximumLengthChanged) +#ifndef QT_NO_VALIDATOR + Q_PROPERTY(QValidator* validator READ validator WRITE setValidator NOTIFY validatorChanged) +#endif + Q_PROPERTY(QString inputMask READ inputMask WRITE setInputMask NOTIFY inputMaskChanged) + Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ imHints WRITE setIMHints) + + Q_PROPERTY(bool acceptableInput READ hasAcceptableInput NOTIFY acceptableInputChanged) + Q_PROPERTY(EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged) + Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged) + Q_PROPERTY(QString passwordCharacter READ passwordCharacter WRITE setPasswordCharacter NOTIFY passwordCharacterChanged) + Q_PROPERTY(QString displayText READ displayText NOTIFY displayTextChanged) + Q_PROPERTY(bool autoScroll READ autoScroll WRITE setAutoScroll NOTIFY autoScrollChanged) + Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) + Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged) + Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged) + +public: + QQuickTextInput(QQuickItem * parent=0); + ~QQuickTextInput(); + + enum EchoMode {//To match QLineEdit::EchoMode + Normal, + NoEcho, + Password, + PasswordEchoOnEdit + }; + + enum HAlignment { + AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter + }; + + enum SelectionMode { + SelectCharacters, + SelectWords + }; + + enum CursorPosition { + CursorBetweenCharacters, + CursorOnCharacter + }; + + //Auxilliary functions needed to control the TextInput from QML + Q_INVOKABLE int positionAt(int x) const; + Q_INVOKABLE int positionAt(int x, CursorPosition position) const; + Q_INVOKABLE QRectF positionToRectangle(int pos) const; + Q_INVOKABLE void moveCursorSelection(int pos); + Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode); + + Q_INVOKABLE void openSoftwareInputPanel(); + Q_INVOKABLE void closeSoftwareInputPanel(); + + QString text() const; + void setText(const QString &); + + QFont font() const; + void setFont(const QFont &font); + + QColor color() const; + void setColor(const QColor &c); + + QColor selectionColor() const; + void setSelectionColor(const QColor &c); + + QColor selectedTextColor() const; + void setSelectedTextColor(const QColor &c); + + HAlignment hAlign() const; + void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; + + bool isReadOnly() const; + void setReadOnly(bool); + + bool isCursorVisible() const; + void setCursorVisible(bool on); + + int cursorPosition() const; + void setCursorPosition(int cp); + + QRect cursorRectangle() const; + + int selectionStart() const; + int selectionEnd() const; + + QString selectedText() const; + + int maxLength() const; + void setMaxLength(int ml); + +#ifndef QT_NO_VALIDATOR + QValidator * validator() const; + void setValidator(QValidator* v); +#endif + QString inputMask() const; + void setInputMask(const QString &im); + + EchoMode echoMode() const; + void setEchoMode(EchoMode echo); + + QString passwordCharacter() const; + void setPasswordCharacter(const QString &str); + + QString displayText() const; + + QDeclarativeComponent* cursorDelegate() const; + void setCursorDelegate(QDeclarativeComponent*); + + bool focusOnPress() const; + void setFocusOnPress(bool); + + bool autoScroll() const; + void setAutoScroll(bool); + + bool selectByMouse() const; + void setSelectByMouse(bool); + + SelectionMode mouseSelectionMode() const; + void setMouseSelectionMode(SelectionMode mode); + + bool hasAcceptableInput() const; + + QVariant inputMethodQuery(Qt::InputMethodQuery property) const; + + QRectF boundingRect() const; + bool canPaste() const; + + bool isInputMethodComposing() const; + + Qt::InputMethodHints imHints() const; + void setIMHints(Qt::InputMethodHints hints); + +Q_SIGNALS: + void textChanged(); + void cursorPositionChanged(); + void cursorRectangleChanged(); + void selectionStartChanged(); + void selectionEndChanged(); + void selectedTextChanged(); + void accepted(); + void acceptableInputChanged(); + void colorChanged(const QColor &color); + void selectionColorChanged(const QColor &color); + void selectedTextColorChanged(const QColor &color); + void fontChanged(const QFont &font); + void horizontalAlignmentChanged(HAlignment alignment); + void readOnlyChanged(bool isReadOnly); + void cursorVisibleChanged(bool isCursorVisible); + void cursorDelegateChanged(); + void maximumLengthChanged(int maximumLength); + void validatorChanged(); + void inputMaskChanged(const QString &inputMask); + void echoModeChanged(EchoMode echoMode); + void passwordCharacterChanged(); + void displayTextChanged(); + void activeFocusOnPressChanged(bool activeFocusOnPress); + void autoScrollChanged(bool autoScroll); + void selectByMouseChanged(bool selectByMouse); + void mouseSelectionModeChanged(SelectionMode mode); + void canPasteChanged(); + void inputMethodComposingChanged(); + void effectiveHorizontalAlignmentChanged(); + +protected: + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + bool sceneEvent(QEvent *event); + void keyPressEvent(QKeyEvent* ev); + void inputMethodEvent(QInputMethodEvent *); + void mouseUngrabEvent(); + bool event(QEvent *e); + void focusInEvent(QFocusEvent *event); + virtual void itemChange(ItemChange, const ItemChangeData &); + QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data); + +public Q_SLOTS: + void selectAll(); + void selectWord(); + void select(int start, int end); + void deselect(); + bool isRightToLeft(int start, int end); +#ifndef QT_NO_CLIPBOARD + void cut(); + void copy(); + void paste(); +#endif + +private Q_SLOTS: + void updateSize(bool needsRedraw = true); + void q_textChanged(); + void selectionChanged(); + void createCursor(); + void cursorPosChanged(); + void updateCursorRectangle(); + void updateRect(const QRect &r = QRect()); + void q_canPasteChanged(); + +private: + Q_DECLARE_PRIVATE(QQuickTextInput) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTextInput) +#ifndef QT_NO_VALIDATOR +QML_DECLARE_TYPE(QValidator) +QML_DECLARE_TYPE(QIntValidator) +QML_DECLARE_TYPE(QDoubleValidator) +QML_DECLARE_TYPE(QRegExpValidator) +#endif + +QT_END_HEADER + +#endif // QQUICKTEXTINPUT_P_H diff --git a/src/declarative/items/qquicktextinput_p_p.h b/src/declarative/items/qquicktextinput_p_p.h new file mode 100644 index 0000000000..900b0804bb --- /dev/null +++ b/src/declarative/items/qquicktextinput_p_p.h @@ -0,0 +1,172 @@ +// Commit: 47712d1f330e4b22ce6dd30e7557288ef7f7fca0 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTINPUT_P_P_H +#define QQUICKTEXTINPUT_P_P_H + +#include "qquicktextinput_p.h" +#include "qquicktext_p.h" +#include "qquickimplicitsizeitem_p_p.h" + +#include + +#include +#include +#include +#include +#include + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +QT_BEGIN_NAMESPACE + +class QQuickTextNode; + +class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickTextInput) +public: + QQuickTextInputPrivate() + : control(new QLineControl(QString())) + , color((QRgb)0) + , style(QQuickText::Normal) + , styleColor((QRgb)0) + , hAlign(QQuickTextInput::AlignLeft) + , mouseSelectionMode(QQuickTextInput::SelectCharacters) + , inputMethodHints(Qt::ImhNone) + , textNode(0) + , hscroll(0) + , oldScroll(0) + , oldValidity(false) + , focused(false) + , focusOnPress(true) + , cursorVisible(false) + , autoScroll(true) + , selectByMouse(false) + , canPaste(false) + , hAlignImplicit(true) + , selectPressed(false) + , textLayoutDirty(true) + { + } + + ~QQuickTextInputPrivate() + { + } + + int xToPos(int x, QTextLine::CursorPosition betweenOrOn = QTextLine::CursorBetweenCharacters) const + { + Q_Q(const QQuickTextInput); + QRect cr = q->boundingRect().toRect(); + x-= cr.x() - hscroll; + return control->xToPos(x, betweenOrOn); + } + + void init(); + void startCreatingCursor(); + void updateHorizontalScroll(); + bool determineHorizontalAlignment(); + bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false); + void mirrorChange(); + int calculateTextWidth(); + bool sendMouseEventToInputContext(QMouseEvent *event); + void updateInputMethodHints(); + void hideCursor(); + void showCursor(); + + QLineControl* control; + + QFont font; + QFont sourceFont; + QColor color; + QColor selectionColor; + QColor selectedTextColor; + QQuickText::TextStyle style; + QColor styleColor; + QQuickTextInput::HAlignment hAlign; + QQuickTextInput::SelectionMode mouseSelectionMode; + Qt::InputMethodHints inputMethodHints; + QPointer cursorComponent; + QPointer cursorItem; + QPointF pressPos; + QQuickTextNode *textNode; + QElapsedTimer tripleClickTimer; + QPoint tripleClickStartPoint; + + int lastSelectionStart; + int lastSelectionEnd; + int oldHeight; + int oldWidth; + int hscroll; + int oldScroll; + + bool oldValidity:1; + bool focused:1; + bool focusOnPress:1; + bool cursorVisible:1; + bool autoScroll:1; + bool selectByMouse:1; + bool canPaste:1; + bool hAlignImplicit:1; + bool selectPressed:1; + bool textLayoutDirty:1; + + static inline QQuickTextInputPrivate *get(QQuickTextInput *t) { + return t->d_func(); + } + bool hasPendingTripleClick() const { + return !tripleClickTimer.hasExpired(qApp->styleHints()->mouseDoubleClickInterval()); + } +}; + +QT_END_NAMESPACE + +#endif // QQUICKTEXTINPUT_P_P_H diff --git a/src/declarative/items/qquicktextnode.cpp b/src/declarative/items/qquicktextnode.cpp new file mode 100644 index 0000000000..fab592ffdc --- /dev/null +++ b/src/declarative/items/qquicktextnode.cpp @@ -0,0 +1,1343 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktextnode_p.h" +#include "qsgsimplerectnode.h" +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + Creates an empty QQuickTextNode +*/ +QQuickTextNode::QQuickTextNode(QSGContext *context) + : m_context(context), m_cursorNode(0) +{ +#if defined(QML_RUNTIME_TESTING) + description = QLatin1String("text"); +#endif +} + +QQuickTextNode::~QQuickTextNode() +{ + qDeleteAll(m_textures); +} + +#if 0 +void QQuickTextNode::setColor(const QColor &color) +{ + if (m_usePixmapCache) { + setUpdateFlag(UpdateNodes); + } else { + for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) { + if (childNode->subType() == GlyphNodeSubType) { + QSGGlyphNode *glyphNode = static_cast(childNode); + if (glyphNode->color() == m_color) + glyphNode->setColor(color); + } else if (childNode->subType() == SolidRectNodeSubType) { + QSGSimpleRectNode *solidRectNode = static_cast(childNode); + if (solidRectNode->color() == m_color) + solidRectNode->setColor(color); + } + } + } + m_color = color; +} + +void QQuickTextNode::setStyleColor(const QColor &styleColor) +{ + if (m_textStyle != QQuickTextNode::NormalTextStyle) { + if (m_usePixmapCache) { + setUpdateFlag(UpdateNodes); + } else { + for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) { + if (childNode->subType() == GlyphNodeSubType) { + QSGGlyphNode *glyphNode = static_cast(childNode); + if (glyphNode->color() == m_styleColor) + glyphNode->setColor(styleColor); + } else if (childNode->subType() == SolidRectNodeSubType) { + QSGSimpleRectNode *solidRectNode = static_cast(childNode); + if (solidRectNode->color() == m_styleColor) + solidRectNode->setColor(styleColor); + } + } + } + } + m_styleColor = styleColor; +} +#endif + +QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, + QQuickText::TextStyle style, const QColor &styleColor, + QSGNode *parentNode) +{ + QSGGlyphNode *node = m_context->createGlyphNode(); + node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs); + node->setStyle(style); + node->setStyleColor(styleColor); + node->setColor(color); + node->update(); + + /* We flag the geometry as static, but we never call markVertexDataDirty + or markIndexDataDirty on them. This is because all text nodes are + discarded when a change occurs. If we start appending/removing from + existing geometry, then we also need to start marking the geometry as + dirty. + */ + node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern); + node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern); + + if (parentNode == 0) + parentNode = this; + parentNode->appendChildNode(node); + + return node; +} + +void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color) +{ + if (m_cursorNode != 0) + delete m_cursorNode; + + m_cursorNode = new QSGSimpleRectNode(rect, color); + appendChildNode(m_cursorNode); +} + +namespace { + + struct BinaryTreeNode { + enum SelectionState { + Unselected, + Selected + }; + + BinaryTreeNode() + : selectionState(Unselected) + , clipNode(0) + , decorations(QQuickTextNode::NoDecoration) + , ascent(0.0) + , leftChildIndex(-1) + , rightChildIndex(-1) + { + + } + + BinaryTreeNode(const QRectF &brect, const QImage &i, SelectionState selState, qreal a) + : boundingRect(brect) + , selectionState(selState) + , clipNode(0) + , decorations(QQuickTextNode::NoDecoration) + , image(i) + , ascent(a) + , leftChildIndex(-1) + , rightChildIndex(-1) + { + } + + BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect, + const QQuickTextNode::Decorations &decs, const QColor &c, const QColor &bc, + const QPointF &pos, qreal a) + : glyphRun(g) + , boundingRect(brect) + , selectionState(selState) + , clipNode(0) + , decorations(decs) + , color(c) + , backgroundColor(bc) + , position(pos) + , ascent(a) + , leftChildIndex(-1) + , rightChildIndex(-1) + { + } + + QGlyphRun glyphRun; + QRectF boundingRect; + SelectionState selectionState; + QSGClipNode *clipNode; + QQuickTextNode::Decorations decorations; + QColor color; + QColor backgroundColor; + QPointF position; + QImage image; + qreal ascent; + + int leftChildIndex; + int rightChildIndex; + + static void insert(QVarLengthArray *binaryTree, + const QRectF &rect, + const QImage &image, + qreal ascent, + SelectionState selectionState) + { + insert(binaryTree, BinaryTreeNode(rect, image, selectionState, ascent)); + } + + static void insert(QVarLengthArray *binaryTree, + const QGlyphRun &glyphRun, + SelectionState selectionState, + const QColor &textColor, + const QColor &backgroundColor, + const QPointF &position) + { + QRectF searchRect = glyphRun.boundingRect(); + searchRect.translate(position); + + if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height())) + return; + + QQuickTextNode::Decorations decorations = QQuickTextNode::NoDecoration; + decorations |= (glyphRun.underline() ? QQuickTextNode::Underline : QQuickTextNode::NoDecoration); + decorations |= (glyphRun.overline() ? QQuickTextNode::Overline : QQuickTextNode::NoDecoration); + decorations |= (glyphRun.strikeOut() ? QQuickTextNode::StrikeOut : QQuickTextNode::NoDecoration); + decorations |= (backgroundColor.isValid() ? QQuickTextNode::Background : QQuickTextNode::NoDecoration); + + qreal ascent = glyphRun.rawFont().ascent(); + insert(binaryTree, BinaryTreeNode(glyphRun, selectionState, searchRect, decorations, + textColor, backgroundColor, position, ascent)); + } + + static void insert(QVarLengthArray *binaryTree, + const BinaryTreeNode &binaryTreeNode) + { + int newIndex = binaryTree->size(); + binaryTree->append(binaryTreeNode); + if (newIndex == 0) + return; + + int searchIndex = 0; + forever { + BinaryTreeNode *node = binaryTree->data() + searchIndex; + if (binaryTreeNode.boundingRect.left() < node->boundingRect.left()) { + if (node->leftChildIndex < 0) { + node->leftChildIndex = newIndex; + break; + } else { + searchIndex = node->leftChildIndex; + } + } else { + if (node->rightChildIndex < 0) { + node->rightChildIndex = newIndex; + break; + } else { + searchIndex = node->rightChildIndex; + } + } + } + } + + static void inOrder(const QVarLengthArray &binaryTree, + QVarLengthArray *sortedIndexes, + int currentIndex = 0) + { + Q_ASSERT(currentIndex < binaryTree.size()); + + const BinaryTreeNode *node = binaryTree.data() + currentIndex; + if (node->leftChildIndex >= 0) + inOrder(binaryTree, sortedIndexes, node->leftChildIndex); + + sortedIndexes->append(currentIndex); + + if (node->rightChildIndex >= 0) + inOrder(binaryTree, sortedIndexes, node->rightChildIndex); + } + }; + + // Engine that takes glyph runs as input, and produces a set of glyph nodes, clip nodes, + // and rectangle nodes to represent the text, decorations and selection. Will try to minimize + // number of nodes, and join decorations in neighbouring items + class SelectionEngine + { + public: + SelectionEngine() : m_hasSelection(false) {} + + QTextLine currentLine() const { return m_currentLine; } + + void setCurrentLine(const QTextLine ¤tLine) + { + if (m_currentLine.isValid()) + processCurrentLine(); + + m_currentLine = currentLine; + } + + void addBorder(const QRectF &rect, qreal border, QTextFrameFormat::BorderStyle borderStyle, + const QBrush &borderBrush); + void addFrameDecorations(QTextDocument *document, QTextFrame *frame); + void addImage(const QRectF &rect, const QImage &image, qreal ascent, + BinaryTreeNode::SelectionState selectionState, + QTextFrameFormat::Position layoutPosition); + void addTextObject(const QPointF &position, const QTextCharFormat &format, + BinaryTreeNode::SelectionState selectionState, + QTextDocument *textDocument, int pos, + QTextFrameFormat::Position layoutPosition = QTextFrameFormat::InFlow); + void addSelectedGlyphs(const QGlyphRun &glyphRun); + void addUnselectedGlyphs(const QGlyphRun &glyphRun); + void addGlyphsInRange(int rangeStart, int rangeEnd, + const QColor &color, const QColor &backgroundColor, + int selectionStart, int selectionEnd); + void addGlyphsForRanges(const QVarLengthArray &ranges, + int start, int end, + int selectionStart, int selectionEnd); + + void addToSceneGraph(QQuickTextNode *parent, + QQuickText::TextStyle style = QQuickText::Normal, + const QColor &styleColor = QColor()); + + void setSelectionColor(const QColor &selectionColor) + { + m_selectionColor = selectionColor; + } + + void setSelectedTextColor(const QColor &selectedTextColor) + { + m_selectedTextColor = selectedTextColor; + } + + void setTextColor(const QColor &textColor) + { + m_textColor = textColor; + } + + void setPosition(const QPointF &position) + { + m_position = position; + } + + private: + struct TextDecoration + { + TextDecoration() : selectionState(BinaryTreeNode::Unselected) {} + TextDecoration(const BinaryTreeNode::SelectionState &s, + const QRectF &r, + const QColor &c) + : selectionState(s) + , rect(r) + , color(c) + { + } + + BinaryTreeNode::SelectionState selectionState; + QRectF rect; + QColor color; + }; + + void processCurrentLine(); + void addTextDecorations(const QVarLengthArray &textDecorations, + qreal offset, qreal thickness); + + QColor m_selectionColor; + QColor m_textColor; + QColor m_backgroundColor; + QColor m_selectedTextColor; + QPointF m_position; + + QTextLine m_currentLine; + bool m_hasSelection; + + QList > m_backgrounds; + QList m_selectionRects; + QVarLengthArray m_currentLineTree; + + QList m_lines; + QVector m_processedNodes; + + QList > m_images; + }; + + void SelectionEngine::addTextDecorations(const QVarLengthArray &textDecorations, + qreal offset, qreal thickness) + { + for (int i=0; i sortedIndexes; // Indexes in tree sorted by x position + BinaryTreeNode::inOrder(m_currentLineTree, &sortedIndexes); + + Q_ASSERT(sortedIndexes.size() == m_currentLineTree.size()); + + BinaryTreeNode::SelectionState currentSelectionState = BinaryTreeNode::Unselected; + QRectF currentRect; + + QQuickTextNode::Decorations currentDecorations = QQuickTextNode::NoDecoration; + qreal underlineOffset = 0.0; + qreal underlineThickness = 0.0; + + qreal overlineOffset = 0.0; + qreal overlineThickness = 0.0; + + qreal strikeOutOffset = 0.0; + qreal strikeOutThickness = 0.0; + + QRectF decorationRect = currentRect; + + QColor lastColor; + QColor lastBackgroundColor; + + QVarLengthArray pendingUnderlines; + QVarLengthArray pendingOverlines; + QVarLengthArray pendingStrikeOuts; + if (!sortedIndexes.isEmpty()) { + QSGClipNode *currentClipNode = m_hasSelection ? new QSGClipNode : 0; + bool currentClipNodeUsed = false; + for (int i=0; i<=sortedIndexes.size(); ++i) { + BinaryTreeNode *node = 0; + if (i < sortedIndexes.size()) { + int sortedIndex = sortedIndexes.at(i); + Q_ASSERT(sortedIndex < m_currentLineTree.size()); + + node = m_currentLineTree.data() + sortedIndex; + } + + if (i == 0) + currentSelectionState = node->selectionState; + + // Update decorations + if (currentDecorations != QQuickTextNode::NoDecoration) { + decorationRect.setY(m_position.y() + m_currentLine.y()); + decorationRect.setHeight(m_currentLine.height()); + + if (node != 0) + decorationRect.setRight(node->boundingRect.left()); + + TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor); + if (currentDecorations & QQuickTextNode::Underline) + pendingUnderlines.append(textDecoration); + + if (currentDecorations & QQuickTextNode::Overline) + pendingOverlines.append(textDecoration); + + if (currentDecorations & QQuickTextNode::StrikeOut) + pendingStrikeOuts.append(textDecoration); + + if (currentDecorations & QQuickTextNode::Background) + m_backgrounds.append(qMakePair(decorationRect, lastBackgroundColor)); + } + + // If we've reached an unselected node from a selected node, we add the + // selection rect to the graph, and we add decoration every time the + // selection state changes, because that means the text color changes + if (node == 0 || node->selectionState != currentSelectionState) { + if (node != 0) + currentRect.setRight(node->boundingRect.left()); + currentRect.setY(m_position.y() + m_currentLine.y()); + currentRect.setHeight(m_currentLine.height()); + + // Draw selection all the way up to the left edge of the unselected item + if (currentSelectionState == BinaryTreeNode::Selected) + m_selectionRects.append(currentRect); + + if (currentClipNode != 0) { + if (!currentClipNodeUsed) { + delete currentClipNode; + } else { + currentClipNode->setIsRectangular(true); + currentClipNode->setClipRect(currentRect); + } + } + + if (node != 0 && m_hasSelection) + currentClipNode = new QSGClipNode; + else + currentClipNode = 0; + currentClipNodeUsed = false; + + if (node != 0) { + currentSelectionState = node->selectionState; + currentRect = node->boundingRect; + + // Make sure currentRect is valid, otherwise the unite won't work + if (currentRect.isNull()) + currentRect.setSize(QSizeF(1, 1)); + } + } else { + if (currentRect.isNull()) + currentRect = node->boundingRect; + else + currentRect = currentRect.united(node->boundingRect); + } + + if (node != 0) { + node->clipNode = currentClipNode; + currentClipNodeUsed = true; + + decorationRect = node->boundingRect; + + // If previous item(s) had underline and current does not, then we add the + // pending lines to the lists and likewise for overlines and strikeouts + if (!pendingUnderlines.isEmpty() + && !(node->decorations & QQuickTextNode::Underline)) { + addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); + + pendingUnderlines.clear(); + + underlineOffset = 0.0; + underlineThickness = 0.0; + } + + // ### Add pending when overlineOffset/thickness changes to minimize number of + // nodes + if (!pendingOverlines.isEmpty()) { + addTextDecorations(pendingOverlines, overlineOffset, overlineThickness); + + pendingOverlines.clear(); + + overlineOffset = 0.0; + overlineThickness = 0.0; + } + + // ### Add pending when overlineOffset/thickness changes to minimize number of + // nodes + if (!pendingStrikeOuts.isEmpty()) { + addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness); + + pendingStrikeOuts.clear(); + + strikeOutOffset = 0.0; + strikeOutThickness = 0.0; + } + + // Merge current values with previous. Prefer greatest thickness + QRawFont rawFont = node->glyphRun.rawFont(); + if (node->decorations & QQuickTextNode::Underline) { + if (rawFont.lineThickness() > underlineThickness) { + underlineThickness = rawFont.lineThickness(); + underlineOffset = rawFont.underlinePosition(); + } + } + + if (node->decorations & QQuickTextNode::Overline) { + overlineOffset = -rawFont.ascent(); + overlineThickness = rawFont.lineThickness(); + } + + if (node->decorations & QQuickTextNode::StrikeOut) { + strikeOutThickness = rawFont.lineThickness(); + strikeOutOffset = rawFont.ascent() / -3.0; + } + + currentDecorations = node->decorations; + lastColor = node->color; + lastBackgroundColor = node->backgroundColor; + m_processedNodes.append(*node); + } + } + + if (!pendingUnderlines.isEmpty()) + addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); + + if (!pendingOverlines.isEmpty()) + addTextDecorations(pendingOverlines, overlineOffset, overlineThickness); + + if (!pendingStrikeOuts.isEmpty()) + addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness); + } + + m_currentLineTree.clear(); + m_currentLine = QTextLine(); + m_hasSelection = false; + } + + void SelectionEngine::addImage(const QRectF &rect, const QImage &image, qreal ascent, + BinaryTreeNode::SelectionState selectionState, + QTextFrameFormat::Position layoutPosition) + { + QRectF searchRect = rect; + if (layoutPosition == QTextFrameFormat::InFlow) { + if (m_currentLineTree.isEmpty()) { + searchRect.moveTopLeft(m_position); + } else { + const BinaryTreeNode *lastNode = m_currentLineTree.data() + m_currentLineTree.size() - 1; + if (lastNode->glyphRun.isRightToLeft()) { + QPointF lastPos = lastNode->boundingRect.topLeft(); + searchRect.moveTopRight(lastPos - QPointF(0, ascent)); + } else { + QPointF lastPos = lastNode->boundingRect.topRight(); + searchRect.moveTopLeft(lastPos - QPointF(0, ascent)); + } + } + } + + BinaryTreeNode::insert(&m_currentLineTree, searchRect, image, ascent, selectionState); + } + + void SelectionEngine::addTextObject(const QPointF &position, const QTextCharFormat &format, + BinaryTreeNode::SelectionState selectionState, + QTextDocument *textDocument, int pos, + QTextFrameFormat::Position layoutPosition) + { + QTextObjectInterface *handler = textDocument->documentLayout()->handlerForObject(format.objectType()); + if (handler != 0) { + QImage image; + QSizeF size = handler->intrinsicSize(textDocument, pos, format); + + if (format.objectType() == QTextFormat::ImageObject) { + QTextImageFormat imageFormat = format.toImageFormat(); + QTextImageHandler *imageHandler = static_cast(handler); + image = imageHandler->image(textDocument, imageFormat); + } + + if (image.isNull()) { + image = QImage(size.toSize(), QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + { + QPainter painter(&image); + handler->drawObject(&painter, image.rect(), textDocument, pos, format); + } + } + + qreal ascent; + QFontMetrics m(format.font()); + switch (format.verticalAlignment()) + { + case QTextCharFormat::AlignMiddle: + ascent = size.height() / 2 - 1; + break; + case QTextCharFormat::AlignBaseline: + ascent = size.height() - m.descent() - 1; + break; + default: + ascent = size.height() - 1; + } + + addImage(QRectF(position, size), image, ascent, selectionState, layoutPosition); + } + } + + void SelectionEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun) + { + BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Unselected, + m_textColor, m_backgroundColor, m_position); + } + + void SelectionEngine::addSelectedGlyphs(const QGlyphRun &glyphRun) + { + int currentSize = m_currentLineTree.size(); + BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Selected, + m_textColor, m_backgroundColor, m_position); + m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize; + } + + void SelectionEngine::addGlyphsForRanges(const QVarLengthArray &ranges, + int start, int end, + int selectionStart, int selectionEnd) + { + int currentPosition = start; + int remainingLength = end - start; + for (int j=0; j= currentPosition + && range.start < currentPosition + remainingLength) { + + if (range.start > currentPosition) { + addGlyphsInRange(currentPosition, range.start - currentPosition, + QColor(), QColor(), selectionStart, selectionEnd); + } + + int rangeEnd = qMin(range.start + range.length, currentPosition + remainingLength); + QColor rangeColor = range.format.hasProperty(QTextFormat::ForegroundBrush) + ? range.format.foreground().color() + : QColor(); + QColor rangeBackgroundColor = range.format.hasProperty(QTextFormat::BackgroundBrush) + ? range.format.background().color() + : QColor(); + + addGlyphsInRange(range.start, rangeEnd - range.start, + rangeColor, rangeBackgroundColor, + selectionStart, selectionEnd); + + currentPosition = range.start + range.length; + remainingLength = end - currentPosition; + + } else if (range.start > currentPosition + remainingLength || remainingLength <= 0) { + break; + } + } + + if (remainingLength > 0) { + addGlyphsInRange(currentPosition, remainingLength, QColor(), QColor(), + selectionStart, selectionEnd); + } + + } + + void SelectionEngine::addGlyphsInRange(int rangeStart, int rangeLength, + const QColor &color, const QColor &backgroundColor, + int selectionStart, int selectionEnd) + { + QColor oldColor; + if (color.isValid()) { + oldColor = m_textColor; + m_textColor = color; + } + + QColor oldBackgroundColor = m_backgroundColor; + if (backgroundColor.isValid()) { + oldBackgroundColor = m_backgroundColor; + m_backgroundColor = backgroundColor; + } + + bool hasSelection = selectionEnd >= 0 + && selectionStart <= selectionEnd; + + QTextLine &line = m_currentLine; + int rangeEnd = rangeStart + rangeLength; + if (!hasSelection || (selectionStart > rangeEnd || selectionEnd < rangeStart)) { + QList glyphRuns = line.glyphRuns(rangeStart, rangeLength); + for (int j=0; j glyphRuns = line.glyphRuns(rangeStart, + qMin(selectionStart - rangeStart, + rangeLength)); + + for (int j=0; j selectionStart) { + int start = qMax(selectionStart, rangeStart); + int length = qMin(selectionEnd - start + 1, rangeEnd - start); + QList glyphRuns = line.glyphRuns(start, length); + + for (int j=0; j= rangeStart && selectionEnd < rangeEnd) { + QList glyphRuns = line.glyphRuns(selectionEnd + 1, rangeEnd - selectionEnd - 1); + for (int j=0; j(document->documentLayout()); + QTextFrameFormat frameFormat = frame->format().toFrameFormat(); + + QTextTable *table = qobject_cast(frame); + QRectF boundingRect = table == 0 + ? documentLayout->frameBoundingRect(frame) + : documentLayout->tableBoundingRect(table); + + QBrush bg = frame->frameFormat().background(); + if (bg.style() != Qt::NoBrush) + m_backgrounds.append(qMakePair(boundingRect, bg.color())); + + if (!frameFormat.hasProperty(QTextFormat::FrameBorder)) + return; + + qreal borderWidth = frameFormat.border(); + if (qFuzzyIsNull(borderWidth)) + return; + + QBrush borderBrush = frameFormat.borderBrush(); + QTextFrameFormat::BorderStyle borderStyle = frameFormat.borderStyle(); + if (borderStyle == QTextFrameFormat::BorderStyle_None) + return; + + addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(), + -frameFormat.rightMargin(), -frameFormat.bottomMargin()), + borderWidth, borderStyle, borderBrush); + if (table != 0) { + int rows = table->rows(); + int columns = table->columns(); + + for (int row=0; rowcellAt(row, column); + + QRectF cellRect = documentLayout->tableCellBoundingRect(table, cell); + addBorder(cellRect.adjusted(-borderWidth, -borderWidth, 0, 0), borderWidth, + borderStyle, borderBrush); + } + } + } + } + + void SelectionEngine::addToSceneGraph(QQuickTextNode *parentNode, + QQuickText::TextStyle style, + const QColor &styleColor) + { + if (m_currentLine.isValid()) + processCurrentLine(); + + + for (int i=0; iappendChildNode(new QSGSimpleRectNode(rect, color)); + } + + // First, prepend all selection rectangles to the tree + for (int i=0; iappendChildNode(new QSGSimpleRectNode(rect, m_selectionColor)); + } + + // Finally, add decorations for each node to the tree. + for (int i=0; iappendChildNode(new QSGSimpleRectNode(textDecoration.rect, color)); + } + + // Then, go through all the nodes for all lines and combine all QGlyphRuns with a common + // font, selection state and clip node. + typedef QPair > > KeyType; + QHash map; + for (int i=0; iimage.isNull()) { + QGlyphRun glyphRun = node->glyphRun; + QRawFont rawFont = glyphRun.rawFont(); + QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont); + + QFontEngine *fontEngine = rawFontD->fontEngine; + + KeyType key(qMakePair(fontEngine, + qMakePair(node->clipNode, + qMakePair(node->color.rgba(), int(node->selectionState))))); + + BinaryTreeNode *otherNode = map.value(key, 0); + if (otherNode != 0) { + QGlyphRun &otherGlyphRun = otherNode->glyphRun; + + QVector otherGlyphIndexes = otherGlyphRun.glyphIndexes(); + QVector otherGlyphPositions = otherGlyphRun.positions(); + + otherGlyphIndexes += glyphRun.glyphIndexes(); + + QVector glyphPositions = glyphRun.positions(); + for (int j=0; jposition - otherNode->position); + } + + otherGlyphRun.setGlyphIndexes(otherGlyphIndexes); + otherGlyphRun.setPositions(otherGlyphPositions); + + } else { + map.insert(key, node); + } + } else { + parentNode->addImage(node->boundingRect, node->image); + if (node->selectionState == BinaryTreeNode::Selected) { + QColor color = m_selectionColor; + color.setAlpha(128); + parentNode->appendChildNode(new QSGSimpleRectNode(node->boundingRect, color)); + } + } + } + + // ...and add clip nodes and glyphs to tree. + QHash::const_iterator it = map.constBegin(); + while (it != map.constEnd()) { + + BinaryTreeNode *node = it.value(); + + QSGClipNode *clipNode = node->clipNode; + if (clipNode != 0 && clipNode->parent() == 0 ) + parentNode->appendChildNode(clipNode); + + QColor color = node->selectionState == BinaryTreeNode::Selected + ? m_selectedTextColor + : node->color; + + parentNode->addGlyphs(node->position, node->glyphRun, color, style, styleColor, clipNode); + + ++it; + } + } +} + +void QQuickTextNode::mergeFormats(QTextLayout *textLayout, + QVarLengthArray *mergedFormats) +{ + Q_ASSERT(mergedFormats != 0); + if (textLayout == 0) + return; + + QList additionalFormats = textLayout->additionalFormats(); + for (int i=0; iisEmpty()) { + QTextLayout::FormatRange *lastFormat = mergedFormats->data() + mergedFormats->size() - 1; + + if (additionalFormat.start < lastFormat->start + lastFormat->length) { + QTextLayout::FormatRange *mergedRange = 0; + + int length = additionalFormat.length; + if (additionalFormat.start > lastFormat->start) { + lastFormat->length = additionalFormat.start - lastFormat->start; + length -= lastFormat->length; + + mergedFormats->append(QTextLayout::FormatRange()); + mergedRange = mergedFormats->data() + mergedFormats->size() - 1; + lastFormat = mergedFormats->data() + mergedFormats->size() - 2; + } else { + mergedRange = lastFormat; + } + + mergedRange->format = lastFormat->format; + mergedRange->format.merge(additionalFormat.format); + mergedRange->start = additionalFormat.start; + + int end = qMin(additionalFormat.start + additionalFormat.length, + lastFormat->start + lastFormat->length); + + mergedRange->length = end - mergedRange->start; + length -= mergedRange->length; + + additionalFormat.start = end; + additionalFormat.length = length; + } + } + + if (additionalFormat.length > 0) + mergedFormats->append(additionalFormat); + } + } + +} + +namespace { + + class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout + { + public: + inline QTextCharFormat formatAccessor(int pos) + { + return format(pos); + } + }; + +} + +void QQuickTextNode::addImage(const QRectF &rect, const QImage &image) +{ + QSGImageNode *node = m_context->createImageNode(); + QSGTexture *texture = m_context->createTexture(image); + m_textures.append(texture); + node->setTargetRect(rect); + node->setTexture(texture); + appendChildNode(node); + node->update(); +} + +void QQuickTextNode::addTextDocument(const QPointF &, QTextDocument *textDocument, + const QColor &textColor, + QQuickText::TextStyle style, const QColor &styleColor, + const QColor &selectionColor, const QColor &selectedTextColor, + int selectionStart, int selectionEnd) +{ + SelectionEngine engine; + engine.setTextColor(textColor); + engine.setSelectedTextColor(selectedTextColor); + engine.setSelectionColor(selectionColor); + + QList frames; + frames.append(textDocument->rootFrame()); + while (!frames.isEmpty()) { + QTextFrame *textFrame = frames.takeFirst(); + frames.append(textFrame->childFrames()); + + engine.addFrameDecorations(textDocument, textFrame); + + if (textFrame->firstPosition() > textFrame->lastPosition() + && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) { + const int pos = textFrame->firstPosition() - 1; + ProtectedLayoutAccessor *a = static_cast(textDocument->documentLayout()); + QTextCharFormat format = a->formatAccessor(pos); + QRectF rect = a->frameBoundingRect(textFrame); + + QTextBlock block = textFrame->firstCursorPosition().block(); + engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); + engine.addTextObject(rect.topLeft(), format, BinaryTreeNode::Unselected, textDocument, + pos, textFrame->frameFormat().position()); + } else { + QTextFrame::iterator it = textFrame->begin(); + + while (!it.atEnd()) { + Q_ASSERT(!engine.currentLine().isValid()); + + QTextBlock block = it.currentBlock(); + int preeditLength = block.isValid() ? block.layout()->preeditAreaText().length() : 0; + int preeditPosition = block.isValid() ? block.layout()->preeditAreaPosition() : -1; + + QVarLengthArray colorChanges; + mergeFormats(block.layout(), &colorChanges); + + QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft(); + if (QTextList *textList = block.textList()) { + QPointF pos = blockPosition; + QTextLayout *layout = block.layout(); + if (layout->lineCount() > 0) { + QTextLine firstLine = layout->lineAt(0); + Q_ASSERT(firstLine.isValid()); + + engine.setCurrentLine(firstLine); + + QRectF textRect = firstLine.naturalTextRect(); + pos += textRect.topLeft(); + if (block.textDirection() == Qt::RightToLeft) + pos.rx() += textRect.width(); + + const QTextCharFormat charFormat = block.charFormat(); + QFont font(charFormat.font()); + QFontMetricsF fontMetrics(font); + QTextListFormat listFormat = textList->format(); + + QString listItemBullet; + switch (listFormat.style()) { + case QTextListFormat::ListCircle: + listItemBullet = QChar(0x25E6); // White bullet + break; + case QTextListFormat::ListSquare: + listItemBullet = QChar(0x25AA); // Black small square + break; + case QTextListFormat::ListDecimal: + case QTextListFormat::ListLowerAlpha: + case QTextListFormat::ListUpperAlpha: + case QTextListFormat::ListLowerRoman: + case QTextListFormat::ListUpperRoman: + listItemBullet = textList->itemText(block); + break; + default: + listItemBullet = QChar(0x2022); // Black bullet + break; + }; + + QSizeF size(fontMetrics.width(listItemBullet), fontMetrics.height()); + qreal xoff = fontMetrics.width(QLatin1Char(' ')); + if (block.textDirection() == Qt::LeftToRight) + xoff = -xoff - size.width(); + engine.setPosition(pos + QPointF(xoff, 0)); + + QTextLayout layout; + layout.setFont(font); + layout.setText(listItemBullet); // Bullet + layout.beginLayout(); + QTextLine line = layout.createLine(); + line.setPosition(QPointF(0, 0)); + layout.endLayout(); + + QList glyphRuns = layout.glyphRuns(); + for (int i=0; i(textDocument->objectForFormat(charFormat)); + if (frame && frame->frameFormat().position() == QTextFrameFormat::InFlow) { + BinaryTreeNode::SelectionState selectionState = + (selectionStart < textPos + text.length() + && selectionEnd >= textPos) + ? BinaryTreeNode::Selected + : BinaryTreeNode::Unselected; + + engine.addTextObject(QPointF(), charFormat, selectionState, textDocument, textPos); + } + textPos += text.length(); + } else { + if (!textColor.isValid()) + engine.setTextColor(charFormat.foreground().color()); + + int fragmentEnd = textPos + fragment.length(); + if (preeditPosition >= 0 + && preeditPosition >= textPos + && preeditPosition < fragmentEnd) { + fragmentEnd += preeditLength; + } + + while (textPos < fragmentEnd) { + int blockRelativePosition = textPos - block.position(); + QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); + if (!engine.currentLine().isValid() + || line.lineNumber() != engine.currentLine().lineNumber()) { + engine.setCurrentLine(line); + } + + Q_ASSERT(line.textLength() > 0); + int lineEnd = line.textStart() + block.position() + line.textLength(); + + int len = qMin(lineEnd - textPos, fragmentEnd - textPos); + Q_ASSERT(len > 0); + + int currentStepEnd = textPos + len; + + engine.addGlyphsForRanges(colorChanges, + textPos - block.position(), + currentStepEnd - block.position(), + selectionStart - block.position(), + selectionEnd - block.position()); + + textPos = currentStepEnd; + } + } + + ++blockIterator; + } + + engine.setCurrentLine(QTextLine()); // Reset current line because the text layout changed + ++it; + } + } + } + + engine.addToSceneGraph(this, style, styleColor); +} + +void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color, + QQuickText::TextStyle style, const QColor &styleColor, + const QColor &selectionColor, const QColor &selectedTextColor, + int selectionStart, int selectionEnd) +{ + SelectionEngine engine; + engine.setTextColor(color); + engine.setSelectedTextColor(selectedTextColor); + engine.setSelectionColor(selectionColor); + engine.setPosition(position); + + int preeditLength = textLayout->preeditAreaText().length(); + int preeditPosition = textLayout->preeditAreaPosition(); + + QVarLengthArray colorChanges; + mergeFormats(textLayout, &colorChanges); + + for (int i=0; ilineCount(); ++i) { + QTextLine line = textLayout->lineAt(i); + + int start = line.textStart(); + int length = line.textLength(); + int end = start + length; + + if (preeditPosition >= 0 + && preeditPosition >= start + && preeditPosition < end) { + end += preeditLength; + } + + engine.setCurrentLine(line); + engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); + } + + engine.addToSceneGraph(this, style, styleColor); +} + +void QQuickTextNode::deleteContent() +{ + while (firstChild() != 0) + delete firstChild(); + m_cursorNode = 0; +} + +#if 0 +void QQuickTextNode::updateNodes() +{ + return; + deleteContent(); + if (m_text.isEmpty()) + return; + + if (m_usePixmapCache) { + // ### gunnar: port properly +// QPixmap pixmap = generatedPixmap(); +// if (pixmap.isNull()) +// return; + +// QSGImageNode *pixmapNode = m_context->createImageNode(); +// pixmapNode->setRect(pixmap.rect()); +// pixmapNode->setSourceRect(pixmap.rect()); +// pixmapNode->setOpacity(m_opacity); +// pixmapNode->setClampToEdge(true); +// pixmapNode->setLinearFiltering(m_linearFiltering); + +// appendChildNode(pixmapNode); + } else { + if (m_text.isEmpty()) + return; + + // Implement styling by drawing text several times at slight shifts. shiftForStyle + // contains the sequence of shifted positions at which to draw the text. All except + // the last will be drawn with styleColor. + QList shiftForStyle; + switch (m_textStyle) { + case OutlineTextStyle: + // ### Should be made faster by implementing outline material + shiftForStyle << QPointF(-1, 0); + shiftForStyle << QPointF(0, -1); + shiftForStyle << QPointF(1, 0); + shiftForStyle << QPointF(0, 1); + break; + case SunkenTextStyle: + shiftForStyle << QPointF(0, -1); + break; + case RaisedTextStyle: + shiftForStyle << QPointF(0, 1); + break; + default: + break; + } + + shiftForStyle << QPointF(0, 0); // Regular position + while (!shiftForStyle.isEmpty()) { + QPointF shift = shiftForStyle.takeFirst(); + + // Use styleColor for all but last shift + if (m_richText) { + QColor overrideColor = shiftForStyle.isEmpty() ? QColor() : m_styleColor; + + QTextFrame *textFrame = m_textDocument->rootFrame(); + QPointF p = m_textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft(); + + QTextFrame::iterator it = textFrame->begin(); + while (!it.atEnd()) { + addTextBlock(shift + p, it.currentBlock(), overrideColor); + ++it; + } + } else { + addTextLayout(shift, m_textLayout, shiftForStyle.isEmpty() + ? m_color + : m_styleColor); + } + } + } +} +#endif + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquicktextnode_p.h b/src/declarative/items/qquicktextnode_p.h new file mode 100644 index 0000000000..16e92e34f3 --- /dev/null +++ b/src/declarative/items/qquicktextnode_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTNODE_P_H +#define QQUICKTEXTNODE_P_H + +#include +#include "qquicktext_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGGlyphNode; +class QTextBlock; +class QColor; +class QTextDocument; +class QSGContext; +class QRawFont; +class QSGSimpleRectNode; +class QSGClipNode; +class QSGTexture; + +class QQuickTextNode : public QSGTransformNode +{ +public: + enum Decoration { + NoDecoration = 0x0, + Underline = 0x1, + Overline = 0x2, + StrikeOut = 0x4, + Background = 0x8 + }; + Q_DECLARE_FLAGS(Decorations, Decoration) + + QQuickTextNode(QSGContext *); + ~QQuickTextNode(); + + static bool isComplexRichText(QTextDocument *); + + void deleteContent(); + void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color = QColor(), + QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(), + const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(), + int selectionStart = -1, int selectionEnd = -1); + void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color = QColor(), + QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(), + const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(), + int selectionStart = -1, int selectionEnd = -1); + + void setCursor(const QRectF &rect, const QColor &color); + QSGSimpleRectNode *cursorNode() const { return m_cursorNode; } + + QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, + QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(), + QSGNode *parentNode = 0); + void addImage(const QRectF &rect, const QImage &image); + +private: + void mergeFormats(QTextLayout *textLayout, QVarLengthArray *mergedFormats); + + QSGContext *m_context; + QSGSimpleRectNode *m_cursorNode; + QList m_textures; +}; + +QT_END_NAMESPACE + +#endif // QQUICKTEXTNODE_P_H diff --git a/src/declarative/items/qquicktranslate.cpp b/src/declarative/items/qquicktranslate.cpp new file mode 100644 index 0000000000..7347cb0624 --- /dev/null +++ b/src/declarative/items/qquicktranslate.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktranslate_p.h" +#include "qquickitem_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QQuickTranslatePrivate : public QQuickTransformPrivate +{ +public: + QQuickTranslatePrivate() + : x(0), y(0) {} + + qreal x; + qreal y; +}; + +/*! + Constructs an empty QQuickTranslate object with the given \a parent. +*/ +QQuickTranslate::QQuickTranslate(QObject *parent) +: QQuickTransform(*new QQuickTranslatePrivate, parent) +{ +} + +/*! + Destroys the graphics scale. +*/ +QQuickTranslate::~QQuickTranslate() +{ +} + +/*! + \property QQuickTranslate::x + \brief the horizontal translation. + + The translation can be any real number; the default value is 0.0. + + \sa y +*/ +qreal QQuickTranslate::x() const +{ + Q_D(const QQuickTranslate); + return d->x; +} + +void QQuickTranslate::setX(qreal x) +{ + Q_D(QQuickTranslate); + if (d->x == x) + return; + d->x = x; + update(); + emit xChanged(); +} + +/*! + \property QQuickTranslate::y + \brief the vertical translation. + + The translation can be any real number; the default value is 0.0. + + \sa x +*/ +qreal QQuickTranslate::y() const +{ + Q_D(const QQuickTranslate); + return d->y; +} +void QQuickTranslate::setY(qreal y) +{ + Q_D(QQuickTranslate); + if (d->y == y) + return; + d->y = y; + update(); + emit yChanged(); +} + +void QQuickTranslate::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QQuickTranslate); + matrix->translate(d->x, d->y, 0); +} + +class QQuickScalePrivate : public QQuickTransformPrivate +{ +public: + QQuickScalePrivate() + : xScale(1), yScale(1), zScale(1) {} + QVector3D origin; + qreal xScale; + qreal yScale; + qreal zScale; +}; + +QQuickScale::QQuickScale(QObject *parent) + : QQuickTransform(*new QQuickScalePrivate, parent) +{ +} + +QQuickScale::~QQuickScale() +{ +} + +QVector3D QQuickScale::origin() const +{ + Q_D(const QQuickScale); + return d->origin; +} +void QQuickScale::setOrigin(const QVector3D &point) +{ + Q_D(QQuickScale); + if (d->origin == point) + return; + d->origin = point; + update(); + emit originChanged(); +} + +qreal QQuickScale::xScale() const +{ + Q_D(const QQuickScale); + return d->xScale; +} +void QQuickScale::setXScale(qreal scale) +{ + Q_D(QQuickScale); + if (d->xScale == scale) + return; + d->xScale = scale; + update(); + emit xScaleChanged(); + emit scaleChanged(); +} + +qreal QQuickScale::yScale() const +{ + Q_D(const QQuickScale); + return d->yScale; +} +void QQuickScale::setYScale(qreal scale) +{ + Q_D(QQuickScale); + if (d->yScale == scale) + return; + d->yScale = scale; + update(); + emit yScaleChanged(); + emit scaleChanged(); +} + +qreal QQuickScale::zScale() const +{ + Q_D(const QQuickScale); + return d->zScale; +} +void QQuickScale::setZScale(qreal scale) +{ + Q_D(QQuickScale); + if (d->zScale == scale) + return; + d->zScale = scale; + update(); + emit zScaleChanged(); + emit scaleChanged(); +} + +void QQuickScale::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QQuickScale); + matrix->translate(d->origin); + matrix->scale(d->xScale, d->yScale, d->zScale); + matrix->translate(-d->origin); +} + +class QQuickRotationPrivate : public QQuickTransformPrivate +{ +public: + QQuickRotationPrivate() + : angle(0), axis(0, 0, 1) {} + QVector3D origin; + qreal angle; + QVector3D axis; +}; + +QQuickRotation::QQuickRotation(QObject *parent) + : QQuickTransform(*new QQuickRotationPrivate, parent) +{ +} + +QQuickRotation::~QQuickRotation() +{ +} + +QVector3D QQuickRotation::origin() const +{ + Q_D(const QQuickRotation); + return d->origin; +} + +void QQuickRotation::setOrigin(const QVector3D &point) +{ + Q_D(QQuickRotation); + if (d->origin == point) + return; + d->origin = point; + update(); + emit originChanged(); +} + +qreal QQuickRotation::angle() const +{ + Q_D(const QQuickRotation); + return d->angle; +} +void QQuickRotation::setAngle(qreal angle) +{ + Q_D(QQuickRotation); + if (d->angle == angle) + return; + d->angle = angle; + update(); + emit angleChanged(); +} + +QVector3D QQuickRotation::axis() const +{ + Q_D(const QQuickRotation); + return d->axis; +} +void QQuickRotation::setAxis(const QVector3D &axis) +{ + Q_D(QQuickRotation); + if (d->axis == axis) + return; + d->axis = axis; + update(); + emit axisChanged(); +} + +void QQuickRotation::setAxis(Qt::Axis axis) +{ + switch (axis) + { + case Qt::XAxis: + setAxis(QVector3D(1, 0, 0)); + break; + case Qt::YAxis: + setAxis(QVector3D(0, 1, 0)); + break; + case Qt::ZAxis: + setAxis(QVector3D(0, 0, 1)); + break; + } +} + +class QGraphicsRotation { +public: + static inline void projectedRotate(QMatrix4x4 *matrix, qreal angle, qreal x, qreal y, qreal z) + { + matrix->projectedRotate(angle, x, y, z); + } +}; + +void QQuickRotation::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QQuickRotation); + + if (d->angle == 0. || d->axis.isNull()) + return; + + matrix->translate(d->origin); + QGraphicsRotation::projectedRotate(matrix, d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + matrix->translate(-d->origin); +} + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquicktranslate_p.h b/src/declarative/items/qquicktranslate_p.h new file mode 100644 index 0000000000..6c2333df34 --- /dev/null +++ b/src/declarative/items/qquicktranslate_p.h @@ -0,0 +1,162 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTRANSLATE_P_H +#define QQUICKTRANSLATE_P_H + +#include "qquickitem.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickTranslatePrivate; +class Q_AUTOTEST_EXPORT QQuickTranslate : public QQuickTransform +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + +public: + QQuickTranslate(QObject *parent = 0); + ~QQuickTranslate(); + + qreal x() const; + void setX(qreal); + + qreal y() const; + void setY(qreal); + + void applyTo(QMatrix4x4 *matrix) const; + +Q_SIGNALS: + void xChanged(); + void yChanged(); + +private: + Q_DECLARE_PRIVATE(QQuickTranslate) + Q_DISABLE_COPY(QQuickTranslate) +}; + +class QQuickScalePrivate; +class Q_AUTOTEST_EXPORT QQuickScale : public QQuickTransform +{ + Q_OBJECT + + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY xScaleChanged) + Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY yScaleChanged) + Q_PROPERTY(qreal zScale READ zScale WRITE setZScale NOTIFY zScaleChanged) +public: + QQuickScale(QObject *parent = 0); + ~QQuickScale(); + + QVector3D origin() const; + void setOrigin(const QVector3D &point); + + qreal xScale() const; + void setXScale(qreal); + + qreal yScale() const; + void setYScale(qreal); + + qreal zScale() const; + void setZScale(qreal); + + void applyTo(QMatrix4x4 *matrix) const; + +Q_SIGNALS: + void originChanged(); + void xScaleChanged(); + void yScaleChanged(); + void zScaleChanged(); + void scaleChanged(); + +private: + Q_DECLARE_PRIVATE(QQuickScale) +}; + +class QQuickRotationPrivate; +class Q_AUTOTEST_EXPORT QQuickRotation : public QQuickTransform +{ + Q_OBJECT + + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) +public: + QQuickRotation(QObject *parent = 0); + ~QQuickRotation(); + + QVector3D origin() const; + void setOrigin(const QVector3D &point); + + qreal angle() const; + void setAngle(qreal); + + QVector3D axis() const; + void setAxis(const QVector3D &axis); + void setAxis(Qt::Axis axis); + + void applyTo(QMatrix4x4 *matrix) const; + +Q_SIGNALS: + void originChanged(); + void angleChanged(); + void axisChanged(); + +private: + Q_DECLARE_PRIVATE(QQuickRotation) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTranslate) + +QT_END_HEADER + +#endif diff --git a/src/declarative/items/qquickview.cpp b/src/declarative/items/qquickview.cpp new file mode 100644 index 0000000000..cc87b775a5 --- /dev/null +++ b/src/declarative/items/qquickview.cpp @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickview.h" +#include "qquickview_p.h" + +#include "qquickcanvas_p.h" +#include "qquickitem_p.h" +#include "qquickitemchangelistener_p.h" + +#include +#include + +#include +#include +#include + + +// XXX todo - This whole class should probably be merged with QDeclarativeView for +// maximum seamlessness +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE) + +void QQuickViewPrivate::init() +{ + Q_Q(QQuickView); + + QDeclarativeEnginePrivate::get(&engine)->sgContext = QQuickCanvasPrivate::context; + + engine.setIncubationController(q->incubationController()); + + if (QDeclarativeDebugService::isDebuggingEnabled()) + QDeclarativeInspectorService::instance()->addView(q); +} + +QQuickViewPrivate::QQuickViewPrivate() + : root(0), component(0), resizeMode(QQuickView::SizeViewToRootObject), initialSize(0,0), resized(false) +{ +} + +QQuickViewPrivate::~QQuickViewPrivate() +{ + if (QDeclarativeDebugService::isDebuggingEnabled()) + QDeclarativeInspectorService::instance()->removeView(q_func()); + + delete root; +} + +void QQuickViewPrivate::execute() +{ + Q_Q(QQuickView); + if (root) { + delete root; + root = 0; + } + if (component) { + delete component; + component = 0; + } + if (!source.isEmpty()) { + component = new QDeclarativeComponent(&engine, source, q); + if (!component->isLoading()) { + q->continueExecute(); + } else { + QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), + q, SLOT(continueExecute())); + } + } +} + +void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QQuickView); + if (resizeItem == root && resizeMode == QQuickView::SizeViewToRootObject) { + // wait for both width and height to be changed + resizetimer.start(0,q); + } + QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); +} + +QQuickView::QQuickView(QWindow *parent, Qt::WindowFlags f) +: QQuickCanvas(*(new QQuickViewPrivate), parent) +{ + setWindowFlags(f); + d_func()->init(); +} + +QQuickView::QQuickView(const QUrl &source, QWindow *parent, Qt::WindowFlags f) +: QQuickCanvas(*(new QQuickViewPrivate), parent) +{ + setWindowFlags(f); + d_func()->init(); + setSource(source); +} + +QQuickView::~QQuickView() +{ +} + +void QQuickView::setSource(const QUrl& url) +{ + Q_D(QQuickView); + d->source = url; + d->execute(); +} + +QUrl QQuickView::source() const +{ + Q_D(const QQuickView); + return d->source; +} + +QDeclarativeEngine* QQuickView::engine() const +{ + Q_D(const QQuickView); + return const_cast(&d->engine); +} + +QDeclarativeContext* QQuickView::rootContext() const +{ + Q_D(const QQuickView); + return d->engine.rootContext(); +} + +QQuickView::Status QQuickView::status() const +{ + Q_D(const QQuickView); + if (!d->component) + return QQuickView::Null; + + return QQuickView::Status(d->component->status()); +} + +QList QQuickView::errors() const +{ + Q_D(const QQuickView); + if (d->component) + return d->component->errors(); + return QList(); +} + +void QQuickView::setResizeMode(ResizeMode mode) +{ + Q_D(QQuickView); + if (d->resizeMode == mode) + return; + + if (d->root) { + if (d->resizeMode == SizeViewToRootObject) { + QQuickItemPrivate *p = QQuickItemPrivate::get(d->root); + p->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + } + } + + d->resizeMode = mode; + if (d->root) { + d->initResize(); + } +} + +void QQuickViewPrivate::initResize() +{ + if (root) { + if (resizeMode == QQuickView::SizeViewToRootObject) { + QQuickItemPrivate *p = QQuickItemPrivate::get(root); + p->addItemChangeListener(this, QQuickItemPrivate::Geometry); + } + } + updateSize(); +} + +void QQuickViewPrivate::updateSize() +{ + Q_Q(QQuickView); + if (!root) + return; + + if (resizeMode == QQuickView::SizeViewToRootObject) { + QSize newSize = QSize(root->width(), root->height()); + if (newSize.isValid() && newSize != q->size()) { + q->resize(newSize); + } + } else if (resizeMode == QQuickView::SizeRootObjectToView) { + if (!qFuzzyCompare(q->width(), root->width())) + root->setWidth(q->width()); + if (!qFuzzyCompare(q->height(), root->height())) + root->setHeight(q->height()); + } +} + +QSize QQuickViewPrivate::rootObjectSize() const +{ + QSize rootObjectSize(0,0); + int widthCandidate = -1; + int heightCandidate = -1; + if (root) { + widthCandidate = root->width(); + heightCandidate = root->height(); + } + if (widthCandidate > 0) { + rootObjectSize.setWidth(widthCandidate); + } + if (heightCandidate > 0) { + rootObjectSize.setHeight(heightCandidate); + } + return rootObjectSize; +} + +QQuickView::ResizeMode QQuickView::resizeMode() const +{ + Q_D(const QQuickView); + return d->resizeMode; +} + +/*! + \internal + */ +void QQuickView::continueExecute() +{ + Q_D(QQuickView); + disconnect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueExecute())); + + if (d->component->isError()) { + QList errorList = d->component->errors(); + foreach (const QDeclarativeError &error, errorList) { + qWarning() << error; + } + emit statusChanged(status()); + return; + } + + QObject *obj = d->component->create(); + + if (d->component->isError()) { + QList errorList = d->component->errors(); + foreach (const QDeclarativeError &error, errorList) { + qWarning() << error; + } + emit statusChanged(status()); + return; + } + + d->setRootObject(obj); + emit statusChanged(status()); +} + + +/*! + \internal +*/ +void QQuickViewPrivate::setRootObject(QObject *obj) +{ + Q_Q(QQuickView); + if (root == obj) + return; + if (QQuickItem *sgItem = qobject_cast(obj)) { + root = sgItem; + sgItem->setParentItem(q->QQuickCanvas::rootItem()); + } else { + qWarning() << "QQuickView only supports loading of root objects that derive from QQuickItem." << endl + << endl + << "If your example is using QML 2, (such as qmlscene) and the .qml file you" << endl + << "loaded has 'import QtQuick 1.0' or 'import Qt 4.7', this error will occur." << endl + << endl + << "To load files with 'import QtQuick 1.0' with QML 2, specify:" << endl + << " QMLSCENE_IMPORT_NAME=quick1" << endl + << "on as an environment variable prior to launching the application." << endl + << endl + << "To load files with 'import Qt 4.7' with QML 2, specify:" << endl + << " QMLSCENE_IMPORT_NAME=qt" << endl + << "on as an environment variable prior to launching the application." << endl; + delete obj; + root = 0; + } + if (root) { + initialSize = rootObjectSize(); + if ((resizeMode == QQuickView::SizeViewToRootObject || !resized) // ### refactor: || !q->testAttribute(Qt::WA_Resized) + && initialSize != q->size()) { + + q->resize(initialSize); + resized = true; + } + initResize(); + } +} + +/*! + \internal + If the \l {QTimerEvent} {timer event} \a e is this + view's resize timer, sceneResized() is emitted. + */ +void QQuickView::timerEvent(QTimerEvent* e) +{ + Q_D(QQuickView); + if (!e || e->timerId() == d->resizetimer.timerId()) { + d->updateSize(); + d->resizetimer.stop(); + } +} + +/*! + \internal + Preferred size follows the root object geometry. +*/ +QSize QQuickView::sizeHint() const +{ + Q_D(const QQuickView); + QSize rootObjectSize = d->rootObjectSize(); + if (rootObjectSize.isEmpty()) { + return size(); + } else { + return rootObjectSize; + } +} + +QSize QQuickView::initialSize() const +{ + Q_D(const QQuickView); + return d->initialSize; +} + +QQuickItem *QQuickView::rootObject() const +{ + Q_D(const QQuickView); + return d->root; +} + +/*! + \internal + This function handles the \l {QResizeEvent} {resize event} + \a e. + */ +void QQuickView::resizeEvent(QResizeEvent *e) +{ + Q_D(QQuickView); + if (d->resizeMode == SizeRootObjectToView) + d->updateSize(); + + QQuickCanvas::resizeEvent(e); +} + +void QQuickView::keyPressEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QQuickCanvas::keyPressEvent(e); +} + +void QQuickView::keyReleaseEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QQuickCanvas::keyReleaseEvent(e); +} + +void QQuickView::mouseMoveEvent(QMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QQuickCanvas::mouseMoveEvent(e); +} + +void QQuickView::mousePressEvent(QMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QQuickCanvas::mousePressEvent(e); +} + +void QQuickView::mouseReleaseEvent(QMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QQuickCanvas::mouseReleaseEvent(e); +} + + +QT_END_NAMESPACE diff --git a/src/declarative/items/qquickview.h b/src/declarative/items/qquickview.h new file mode 100644 index 0000000000..8321655d63 --- /dev/null +++ b/src/declarative/items/qquickview.h @@ -0,0 +1,120 @@ +// Commit: 0b83a2161261be525f01359397ab1c8c34827749 +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKVIEW_H +#define QQUICKVIEW_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; +class QDeclarativeContext; +class QDeclarativeError; +class QQuickItem; + +class QQuickViewPrivate; +class Q_DECLARATIVE_EXPORT QQuickView : public QQuickCanvas +{ + Q_OBJECT + Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true) + Q_ENUMS(ResizeMode Status) +public: + explicit QQuickView(QWindow *parent = 0, Qt::WindowFlags f = 0); + QQuickView(const QUrl &source, QWindow *parent = 0, Qt::WindowFlags f = 0); + virtual ~QQuickView(); + + QUrl source() const; + + QDeclarativeEngine* engine() const; + QDeclarativeContext* rootContext() const; + + QQuickItem *rootObject() const; + + enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView }; + ResizeMode resizeMode() const; + void setResizeMode(ResizeMode); + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + + QList errors() const; + + QSize sizeHint() const; + QSize initialSize() const; + +public Q_SLOTS: + void setSource(const QUrl&); + +Q_SIGNALS: + void statusChanged(QQuickView::Status); + +private Q_SLOTS: + void continueExecute(); + +protected: + virtual void resizeEvent(QResizeEvent *); + virtual void timerEvent(QTimerEvent*); + + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); +private: + Q_DISABLE_COPY(QQuickView) + Q_DECLARE_PRIVATE(QQuickView) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKVIEW_H diff --git a/src/declarative/items/qquickview_p.h b/src/declarative/items/qquickview_p.h new file mode 100644 index 0000000000..12be09bab9 --- /dev/null +++ b/src/declarative/items/qquickview_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKVIEW_P_H +#define QQUICKVIEW_P_H + +#include "qquickview.h" + +#include +#include +#include +#include +#include +#include "qquickcanvas_p.h" + +#include "qquickitemchangelistener_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeContext; +class QDeclarativeError; +class QQuickItem; +class QDeclarativeComponent; + +class QQuickViewPrivate : public QQuickCanvasPrivate, + public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickView) +public: + static QQuickViewPrivate* get(QQuickView *view) { return view->d_func(); } + static const QQuickViewPrivate* get(const QQuickView *view) { return view->d_func(); } + + QQuickViewPrivate(); + ~QQuickViewPrivate(); + + void execute(); + void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void initResize(); + void updateSize(); + void setRootObject(QObject *); + + void init(); + + QSize rootObjectSize() const; + + QPointer root; + + QUrl source; + + QDeclarativeEngine engine; + QDeclarativeComponent *component; + QBasicTimer resizetimer; + + QQuickView::ResizeMode resizeMode; + QSize initialSize; + QElapsedTimer frameTimer; + + bool resized; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQUICKVIEW_P_H diff --git a/src/declarative/items/qquickvisualadaptormodel.cpp b/src/declarative/items/qquickvisualadaptormodel.cpp new file mode 100644 index 0000000000..28ea830db4 --- /dev/null +++ b/src/declarative/items/qquickvisualadaptormodel.cpp @@ -0,0 +1,889 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickvisualadaptormodel_p.h" +#include "qquickitem.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +Q_DECLARE_METATYPE(QModelIndex) + +QT_BEGIN_NAMESPACE + +class VDMDelegateDataType : public QDeclarativeRefCount +{ +public: + VDMDelegateDataType() + : metaObject(0) + , propertyCache(0) + , propertyOffset(0) + , signalOffset(0) + , shared(true) + { + } + + VDMDelegateDataType(const VDMDelegateDataType &type) + : metaObject(0) + , propertyCache(0) + , propertyOffset(type.propertyOffset) + , signalOffset(type.signalOffset) + , shared(false) + , builder(type.metaObject, QMetaObjectBuilder::Properties + | QMetaObjectBuilder::Signals + | QMetaObjectBuilder::SuperClass + | QMetaObjectBuilder::ClassName) + { + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + } + + ~VDMDelegateDataType() + { + if (propertyCache) + propertyCache->release(); + qFree(metaObject); + } + + QMetaObject *metaObject; + QDeclarativePropertyCache *propertyCache; + int propertyOffset; + int signalOffset; + bool shared : 1; + QMetaObjectBuilder builder; +}; + +class QQuickVisualAdaptorModelData : public QObject +{ + Q_OBJECT + Q_PROPERTY(int index READ index NOTIFY indexChanged) +public: + QQuickVisualAdaptorModelData(int index, QQuickVisualAdaptorModel *model); + ~QQuickVisualAdaptorModelData(); + + int index() const; + void setIndex(int index); + +Q_SIGNALS: + void indexChanged(); + +public: + int m_index; + QDeclarativeGuard m_model; + QIntrusiveListNode m_cacheNode; +}; + +typedef QIntrusiveList QQuickVisualAdaptorModelDataCache; + +class QQuickVisualAdaptorModelDataMetaObject; +class QQuickVisualAdaptorModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickVisualAdaptorModel) +public: + QQuickVisualAdaptorModelPrivate() + : m_engine(0) + , m_listAccessor(0) + , m_delegateDataType(0) + , createModelData(&initializeModelData) + , m_ref(0) + , m_count(0) + , m_objectList(false) + { + } + + + static QQuickVisualAdaptorModelPrivate *get(QQuickVisualAdaptorModel *m) { + return static_cast(QObjectPrivate::get(m)); + } + + void addProperty(int role, int propertyId, const char *propertyName, const char *propertyType, bool isModelData = false); + template void setModelDataType() + { + createModelData = &T::create; + m_delegateDataType->builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + m_delegateDataType->builder.setClassName(T::staticMetaObject.className()); + m_delegateDataType->builder.setSuperClass(&T::staticMetaObject); + m_delegateDataType->propertyOffset = T::staticMetaObject.propertyCount(); + m_delegateDataType->signalOffset = T::staticMetaObject.methodCount(); + } + QQuickVisualAdaptorModelData *createMetaObject(int index, QQuickVisualAdaptorModel *model); + + static QQuickVisualAdaptorModelData *initializeModelData(int index, QQuickVisualAdaptorModel *model) { + return get(model)->createMetaObject(index, model); + } + + typedef QQuickVisualAdaptorModelData *(*CreateModelData)(int index, QQuickVisualAdaptorModel *model); + + struct PropertyData { + int role; + bool isModelData : 1; + }; + + int modelCount() const { + if (m_listModelInterface) + return m_listModelInterface->count(); + if (m_abstractItemModel) + return m_abstractItemModel->rowCount(m_root); + if (m_listAccessor) + return m_listAccessor->count(); + return 0; + } + + QDeclarativeGuard m_engine; + QDeclarativeGuard m_listModelInterface; + QDeclarativeGuard m_abstractItemModel; + QDeclarativeListAccessor *m_listAccessor; + VDMDelegateDataType *m_delegateDataType; + CreateModelData createModelData; + + int m_ref; + int m_count; + QQuickVisualAdaptorModel::Flags m_flags; + bool m_objectList : 1; + + QVariant m_modelVariant; + QModelIndex m_root; + + QList m_roles; + QList watchedRoleIds; + QList watchedRoles; + QHash m_roleNames; + QVector m_propertyData; + QQuickVisualAdaptorModelDataCache m_cache; +}; + +class QQuickVisualAdaptorModelDataMetaObject : public QAbstractDynamicMetaObject +{ +public: + QQuickVisualAdaptorModelDataMetaObject(QQuickVisualAdaptorModelData *data, VDMDelegateDataType *type) + : m_data(data) + , m_type(type) + { + QObjectPrivate *op = QObjectPrivate::get(m_data); + *static_cast(this) = *type->metaObject; + op->metaObject = this; + m_type->addref(); + } + + ~QQuickVisualAdaptorModelDataMetaObject() { m_type->release(); } + + QQuickVisualAdaptorModelData *m_data; + VDMDelegateDataType *m_type; +}; + +class QQuickVDMAbstractItemModelDataMetaObject : public QQuickVisualAdaptorModelDataMetaObject +{ +public: + QQuickVDMAbstractItemModelDataMetaObject(QQuickVisualAdaptorModelData *object, VDMDelegateDataType *type) + : QQuickVisualAdaptorModelDataMetaObject(object, type) {} + + int metaCall(QMetaObject::Call call, int id, void **arguments) + { + if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) { + QQuickVisualAdaptorModelPrivate *model = QQuickVisualAdaptorModelPrivate::get(m_data->m_model); + if (m_data->m_index == -1 || !model->m_abstractItemModel) + return -1; + *static_cast(arguments[0]) = model->m_abstractItemModel->index( + m_data->m_index, 0, model->m_root).data(model->m_propertyData.at(id - m_type->propertyOffset).role); + return -1; + } else { + return m_data->qt_metacall(call, id, arguments); + } + } +}; + +class QQuickVDMAbstractItemModelData : public QQuickVisualAdaptorModelData +{ + Q_OBJECT + Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) +public: + bool hasModelChildren() const + { + QQuickVisualAdaptorModelPrivate *model = QQuickVisualAdaptorModelPrivate::get(m_model); + return model->m_abstractItemModel->hasChildren(model->m_abstractItemModel->index(m_index, 0, model->m_root)); + } + + static QQuickVisualAdaptorModelData *create(int index, QQuickVisualAdaptorModel *model) { + return new QQuickVDMAbstractItemModelData(index, model); } +private: + QQuickVDMAbstractItemModelData(int index, QQuickVisualAdaptorModel *model) + : QQuickVisualAdaptorModelData(index, model) + { + new QQuickVDMAbstractItemModelDataMetaObject( + this, QQuickVisualAdaptorModelPrivate::get(m_model)->m_delegateDataType); + } +}; + +class QQuickVDMListModelInterfaceDataMetaObject : public QQuickVisualAdaptorModelDataMetaObject +{ +public: + QQuickVDMListModelInterfaceDataMetaObject(QQuickVisualAdaptorModelData *object, VDMDelegateDataType *type) + : QQuickVisualAdaptorModelDataMetaObject(object, type) {} + + int metaCall(QMetaObject::Call call, int id, void **arguments) + { + if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) { + QQuickVisualAdaptorModelPrivate *model = QQuickVisualAdaptorModelPrivate::get(m_data->m_model); + if (m_data->m_index == -1 || !model->m_listModelInterface) + return -1; + *static_cast(arguments[0]) = model->m_listModelInterface->data( + m_data->m_index, model->m_propertyData.at(id - m_type->propertyOffset).role); + return -1; + } else { + return m_data->qt_metacall(call, id, arguments); + } + } +}; + +class QQuickVDMListModelInterfaceData : public QQuickVisualAdaptorModelData +{ +public: + static QQuickVisualAdaptorModelData *create(int index, QQuickVisualAdaptorModel *model) { + return new QQuickVDMListModelInterfaceData(index, model); } +private: + QQuickVDMListModelInterfaceData(int index, QQuickVisualAdaptorModel *model) + : QQuickVisualAdaptorModelData(index, model) + { + new QQuickVDMListModelInterfaceDataMetaObject( + this, QQuickVisualAdaptorModelPrivate::get(m_model)->m_delegateDataType); + } +}; + +class QQuickVDMListAccessorData : public QQuickVisualAdaptorModelData +{ + Q_OBJECT + Q_PROPERTY(QVariant modelData READ modelData CONSTANT) +public: + QVariant modelData() const { + return QQuickVisualAdaptorModelPrivate::get(m_model)->m_listAccessor->at(m_index); } + + static QQuickVisualAdaptorModelData *create(int index, QQuickVisualAdaptorModel *model) { + return new QQuickVDMListAccessorData(index, model); } +private: + QQuickVDMListAccessorData(int index, QQuickVisualAdaptorModel *model) + : QQuickVisualAdaptorModelData(index, model) + { + } +}; + +class QQuickVDMObjectDataMetaObject : public QQuickVisualAdaptorModelDataMetaObject +{ +public: + QQuickVDMObjectDataMetaObject(QQuickVisualAdaptorModelData *data, VDMDelegateDataType *type) + : QQuickVisualAdaptorModelDataMetaObject(data, type) + , m_object(QQuickVisualAdaptorModelPrivate::get(data->m_model)->m_listAccessor->at(data->m_index).value()) + {} + + int metaCall(QMetaObject::Call call, int id, void **arguments) + { + if (id >= m_type->propertyOffset + && (call == QMetaObject::ReadProperty + || call == QMetaObject::WriteProperty + || call == QMetaObject::ResetProperty)) { + if (m_object) + QMetaObject::metacall(m_object, call, id - m_type->propertyOffset + 1, arguments); + return -1; + } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) { + QMetaObject::activate(m_data, this, id, 0); + return -1; + } else { + return m_data->qt_metacall(call, id, arguments); + } + } + + int createProperty(const char *name, const char *) + { + if (!m_object) + return -1; + const QMetaObject *metaObject = m_object->metaObject(); + + const int previousPropertyCount = propertyCount() - propertyOffset(); + int propertyIndex = metaObject->indexOfProperty(name); + if (propertyIndex == -1) + return -1; + if (previousPropertyCount + 1 == metaObject->propertyCount()) + return propertyIndex + m_type->propertyOffset - 1; + + if (m_type->shared) { + VDMDelegateDataType *type = m_type; + m_type = new VDMDelegateDataType(*m_type); + type->release(); + } + + const int previousMethodCount = methodCount(); + int notifierId = previousMethodCount; + for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - 1; ++propertyId) { + QMetaProperty property = metaObject->property(propertyId + 1); + QMetaPropertyBuilder propertyBuilder; + if (property.hasNotifySignal()) { + m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); + propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId); + ++notifierId; + } else { + propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName()); + } + propertyBuilder.setWritable(property.isWritable()); + propertyBuilder.setResettable(property.isResettable()); + propertyBuilder.setConstant(property.isConstant()); + } + + if (m_type->metaObject) + qFree(m_type->metaObject); + m_type->metaObject = m_type->builder.toMetaObject(); + *static_cast(this) = *m_type->metaObject; + + notifierId = previousMethodCount; + for (int i = previousPropertyCount; i < metaObject->propertyCount(); ++i) { + QMetaProperty property = metaObject->property(i); + if (property.hasNotifySignal()) { + QDeclarativePropertyPrivate::connect( + m_object, property.notifySignalIndex(), m_data, notifierId); + ++notifierId; + } + } + return propertyIndex + m_type->propertyOffset - 1; + } + + QDeclarativeGuard m_object; +}; + +class QQuickVDMObjectData : public QQuickVisualAdaptorModelData, public QQuickVisualAdaptorModelProxyInterface +{ + Q_OBJECT + Q_PROPERTY(QObject *modelData READ modelData CONSTANT) + Q_INTERFACES(QQuickVisualAdaptorModelProxyInterface) +public: + QObject *modelData() const { return m_metaObject->m_object; } + QObject *proxiedObject() { return m_metaObject->m_object; } + + static QQuickVisualAdaptorModelData *create(int index, QQuickVisualAdaptorModel *model) { + return new QQuickVDMObjectData(index, model); } + +private: + QQuickVDMObjectData(int index, QQuickVisualAdaptorModel *model) + : QQuickVisualAdaptorModelData(index, model) + , m_metaObject(new QQuickVDMObjectDataMetaObject(this, QQuickVisualAdaptorModelPrivate::get(m_model)->m_delegateDataType)) + { + } + + QQuickVDMObjectDataMetaObject *m_metaObject; +}; + +void QQuickVisualAdaptorModelPrivate::addProperty( + int role, int propertyId, const char *propertyName, const char *propertyType, bool isModelData) +{ + PropertyData propertyData; + propertyData.role = role; + propertyData.isModelData = isModelData; + m_delegateDataType->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); + QMetaPropertyBuilder property = m_delegateDataType->builder.addProperty( + propertyName, propertyType, propertyId); + property.setWritable(false); + + m_propertyData.append(propertyData); +} + +QQuickVisualAdaptorModelData *QQuickVisualAdaptorModelPrivate::createMetaObject(int index, QQuickVisualAdaptorModel *model) +{ + Q_ASSERT(!m_delegateDataType); + + m_objectList = false; + m_propertyData.clear(); + if (m_listAccessor + && m_listAccessor->type() != QDeclarativeListAccessor::ListProperty + && m_listAccessor->type() != QDeclarativeListAccessor::Instance) { + createModelData = &QQuickVDMListAccessorData::create; + m_flags = QQuickVisualAdaptorModel::MetaObjectCacheable; + return QQuickVDMListAccessorData::create(index, model); + } + + m_delegateDataType = new VDMDelegateDataType; + if (m_listModelInterface) { + setModelDataType(); + QList roles = m_listModelInterface->roles(); + for (int propertyId = 0; propertyId < roles.count(); ++propertyId) { + const int role = roles.at(propertyId); + const QByteArray propertyName = m_listModelInterface->toString(role).toUtf8(); + addProperty(role, propertyId, propertyName, "QVariant"); + m_roleNames.insert(propertyName, role); + } + if (m_propertyData.count() == 1) + addProperty(roles.first(), 1, "modelData", "QVariant", true); + m_flags = QQuickVisualAdaptorModel::MetaObjectCacheable; + } else if (m_abstractItemModel) { + setModelDataType(); + QHash roleNames = m_abstractItemModel->roleNames(); + for (QHash::const_iterator it = roleNames.begin(); it != roleNames.end(); ++it) { + addProperty(it.key(), m_propertyData.count(), it.value(), "QVariant"); + m_roleNames.insert(it.value(), it.key()); + } + if (m_propertyData.count() == 1) + addProperty(roleNames.begin().key(), 1, "modelData", "QVariant", true); + m_flags = QQuickVisualAdaptorModel::MetaObjectCacheable; + } else if (m_listAccessor) { + setModelDataType(); + m_objectList = true; + m_flags = QQuickVisualAdaptorModel::ProxiedObject; + } else { + Q_ASSERT(!"No model set on VisualDataModel"); + return 0; + } + m_delegateDataType->metaObject = m_delegateDataType->builder.toMetaObject(); + if (!m_objectList) { + m_delegateDataType->propertyCache = new QDeclarativePropertyCache( + m_engine, m_delegateDataType->metaObject); + } + return createModelData(index, model); +} + +QQuickVisualAdaptorModelData::QQuickVisualAdaptorModelData(int index, QQuickVisualAdaptorModel *model) + : m_index(index) + , m_model(model) +{ +} + +QQuickVisualAdaptorModelData::~QQuickVisualAdaptorModelData() +{ +} + +int QQuickVisualAdaptorModelData::index() const +{ + return m_index; +} + +// This is internal only - it should not be set from qml +void QQuickVisualAdaptorModelData::setIndex(int index) +{ + m_index = index; + emit indexChanged(); +} + +//--------------------------------------------------------------------------- + +QQuickVisualAdaptorModel::QQuickVisualAdaptorModel(QObject *parent) + : QObject(*(new QQuickVisualAdaptorModelPrivate), parent) +{ +} + +QQuickVisualAdaptorModel::~QQuickVisualAdaptorModel() +{ + Q_D(QQuickVisualAdaptorModel); + if (d->m_listAccessor) + delete d->m_listAccessor; + if (d->m_delegateDataType) + d->m_delegateDataType->release(); +} + +QQuickVisualAdaptorModel::Flags QQuickVisualAdaptorModel::flags() const +{ + Q_D(const QQuickVisualAdaptorModel); + return d->m_flags; +} + +QVariant QQuickVisualAdaptorModel::model() const +{ + Q_D(const QQuickVisualAdaptorModel); + return d->m_modelVariant; +} + +void QQuickVisualAdaptorModel::setModel(const QVariant &model, QDeclarativeEngine *engine) +{ + Q_D(QQuickVisualAdaptorModel); + delete d->m_listAccessor; + d->m_engine = engine; + d->m_listAccessor = 0; + d->m_modelVariant = model; + if (d->m_listModelInterface) { + // Assume caller has released all items. + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList)), + this, SLOT(_q_itemsChanged(int,int,QList))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + d->m_listModelInterface = 0; + } else if (d->m_abstractItemModel) { + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_rowsInserted(QModelIndex,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(_q_dataChanged(QModelIndex,QModelIndex))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset())); + QObject::disconnect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); + d->m_abstractItemModel = 0; + } + + d->m_roles.clear(); + d->m_roleNames.clear(); + d->m_flags = QQuickVisualAdaptorModel::Flags(); + if (d->m_delegateDataType) + d->m_delegateDataType->release(); + d->m_delegateDataType = 0; + d->createModelData = &QQuickVisualAdaptorModelPrivate::initializeModelData; + + if (d->m_count) + emit itemsRemoved(0, d->m_count); + + QObject *object = qvariant_cast(model); + if (object && (d->m_listModelInterface = qobject_cast(object))) { + QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList)), + this, SLOT(_q_itemsChanged(int,int,QList))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), + this, SLOT(_q_itemsInserted(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), + this, SLOT(_q_itemsRemoved(int,int))); + QObject::connect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), + this, SLOT(_q_itemsMoved(int,int,int))); + if ((d->m_count = d->m_listModelInterface->count())) + emit itemsInserted(0, d->m_count); + return; + } else if (object && (d->m_abstractItemModel = qobject_cast(object))) { + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_rowsInserted(QModelIndex,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(_q_dataChanged(QModelIndex,QModelIndex))); + QObject::connect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::connect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset())); + QObject::connect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); + + if ((d->m_count = d->m_abstractItemModel->rowCount(d->m_root))) + emit itemsInserted(0, d->m_count); + return; + } + + d->m_listAccessor = new QDeclarativeListAccessor; + d->m_listAccessor->setList(model, d->m_engine); + if ((d->m_count = d->m_listAccessor->count())) + emit itemsInserted(0, d->m_count); +} + +QVariant QQuickVisualAdaptorModel::rootIndex() const +{ + Q_D(const QQuickVisualAdaptorModel); + return QVariant::fromValue(d->m_root); +} + +void QQuickVisualAdaptorModel::setRootIndex(const QVariant &root) +{ + Q_D(QQuickVisualAdaptorModel); + QModelIndex modelIndex = qvariant_cast(root); + if (d->m_root != modelIndex) { + int oldCount = d->modelCount(); + d->m_root = modelIndex; + if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(modelIndex)) + d->m_abstractItemModel->fetchMore(modelIndex); + int newCount = d->modelCount(); + if (oldCount) + emit itemsRemoved(0, oldCount); + if (newCount) + emit itemsInserted(0, newCount); + emit rootIndexChanged(); + } +} + +QVariant QQuickVisualAdaptorModel::modelIndex(int idx) const +{ + Q_D(const QQuickVisualAdaptorModel); + if (d->m_abstractItemModel) + return QVariant::fromValue(d->m_abstractItemModel->index(idx, 0, d->m_root)); + return QVariant::fromValue(QModelIndex()); +} + +QVariant QQuickVisualAdaptorModel::parentModelIndex() const +{ + Q_D(const QQuickVisualAdaptorModel); + if (d->m_abstractItemModel) + return QVariant::fromValue(d->m_abstractItemModel->parent(d->m_root)); + return QVariant::fromValue(QModelIndex()); +} + +int QQuickVisualAdaptorModel::count() const +{ + Q_D(const QQuickVisualAdaptorModel); + return d->modelCount(); +} + +QObject *QQuickVisualAdaptorModel::data(int index) +{ + Q_D(QQuickVisualAdaptorModel); + QQuickVisualAdaptorModelData *data = d->createModelData(index, this); + d->m_cache.insert(data); + return data; +} + +QString QQuickVisualAdaptorModel::stringValue(int index, const QString &name) +{ + Q_D(QQuickVisualAdaptorModel); + if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor) { + if (QObject *object = d->m_listAccessor->at(index).value()) + return object->property(name.toUtf8()).toString(); + } + + QString val; + QQuickVisualAdaptorModelData *data = d->createModelData(index, this); + + QDeclarativeData *ddata = QDeclarativeData::get(data); + if (ddata && ddata->propertyCache) { + QDeclarativePropertyCache::Data *prop = ddata->propertyCache->property(name); + if (prop) { + if (prop->propType == QVariant::String) { + void *args[] = { &val, 0 }; + QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args); + } else if (prop->propType == qMetaTypeId()) { + QVariant v; + void *args[] = { &v, 0 }; + QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args); + val = v.toString(); + } + } else { + val = data->property(name.toUtf8()).toString(); + } + } else { + val = data->property(name.toUtf8()).toString(); + } + + delete data; + + return val; +} + +int QQuickVisualAdaptorModel::indexOf(QObject *object) const +{ + if (QQuickVisualAdaptorModelData *data = qobject_cast(object)) + return data->index(); + return -1; +} + +bool QQuickVisualAdaptorModel::canFetchMore() const +{ + Q_D(const QQuickVisualAdaptorModel); + return d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root); +} + +void QQuickVisualAdaptorModel::fetchMore() +{ + Q_D(QQuickVisualAdaptorModel); + if (d->m_abstractItemModel) + d->m_abstractItemModel->fetchMore(d->m_root); +} + +void QQuickVisualAdaptorModel::replaceWatchedRoles(const QList &oldRoles, const QList &newRoles) +{ + Q_D(QQuickVisualAdaptorModel); + d->watchedRoleIds.clear(); + foreach (const QByteArray &oldRole, oldRoles) + d->watchedRoles.removeOne(oldRole); + d->watchedRoles += newRoles; +} + +void QQuickVisualAdaptorModel::_q_itemsChanged(int index, int count, const QList &roles) +{ + Q_D(QQuickVisualAdaptorModel); + bool changed = roles.isEmpty(); + if (!d->watchedRoles.isEmpty() && d->watchedRoleIds.isEmpty()) { + foreach (QByteArray r, d->watchedRoles) { + if (d->m_roleNames.contains(r)) + d->watchedRoleIds << d->m_roleNames.value(r); + } + } + + QVector signalIndexes; + for (int i = 0; i < roles.count(); ++i) { + const int role = roles.at(i); + if (!changed && d->watchedRoleIds.contains(role)) + changed = true; + for (int propertyId = 0; propertyId < d->m_propertyData.count(); ++propertyId) { + if (d->m_propertyData.at(propertyId).role == role) + signalIndexes.append(propertyId + d->m_delegateDataType->signalOffset); + } + } + if (roles.isEmpty()) { + for (int propertyId = 0; propertyId < d->m_propertyData.count(); ++propertyId) + signalIndexes.append(propertyId + d->m_delegateDataType->signalOffset); + } + + typedef QQuickVisualAdaptorModelDataCache::iterator iterator; + for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { + const int idx = it->index(); + if (idx >= index && idx < index + count) { + QQuickVisualAdaptorModelData *data = *it; + for (int i = 0; i < signalIndexes.count(); ++i) + QMetaObject::activate(data, signalIndexes.at(i), 0); + } + } + if (changed) + emit itemsChanged(index, count); +} + +void QQuickVisualAdaptorModel::_q_itemsInserted(int index, int count) +{ + Q_D(QQuickVisualAdaptorModel); + if (count <= 0) + return; + d->m_count += count; + + typedef QQuickVisualAdaptorModelDataCache::iterator iterator; + for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { + if (it->index() >= index) + it->setIndex(it->index() + count); + } + + emit itemsInserted(index, count); +} + +void QQuickVisualAdaptorModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QQuickVisualAdaptorModel); + if (count <= 0) + return; + d->m_count -= count; + + typedef QQuickVisualAdaptorModelDataCache::iterator iterator; + for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { + if (it->index() >= index + count) + it->setIndex(it->index() - count); + else if (it->index() >= index) + it->setIndex(-1); + } + + emit itemsRemoved(index, count); +} + +void QQuickVisualAdaptorModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QQuickVisualAdaptorModel); + const int minimum = qMin(from, to); + const int maximum = qMax(from, to) + count; + const int difference = from > to ? count : -count; + + typedef QQuickVisualAdaptorModelDataCache::iterator iterator; + for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { + if (it->index() >= from && it->index() < from + count) + it->setIndex(it->index() - from + to); + else if (it->index() >= minimum && it->index() < maximum) + it->setIndex(it->index() + difference); + } + emit itemsMoved(from, to, count); +} + +void QQuickVisualAdaptorModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQuickVisualAdaptorModel); + if (parent == d->m_root) + _q_itemsInserted(begin, end - begin + 1); +} + +void QQuickVisualAdaptorModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQuickVisualAdaptorModel); + if (parent == d->m_root) + _q_itemsRemoved(begin, end - begin + 1); +} + +void QQuickVisualAdaptorModel::_q_rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) +{ + Q_D(QQuickVisualAdaptorModel); + const int count = sourceEnd - sourceStart + 1; + if (destinationParent == d->m_root && sourceParent == d->m_root) { + _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow-count, count); + } else if (sourceParent == d->m_root) { + _q_itemsRemoved(sourceStart, count); + } else if (destinationParent == d->m_root) { + _q_itemsInserted(destinationRow, count); + } +} + +void QQuickVisualAdaptorModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + Q_D(QQuickVisualAdaptorModel); + if (begin.parent() == d->m_root) + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, d->m_roles); +} + +void QQuickVisualAdaptorModel::_q_layoutChanged() +{ + Q_D(QQuickVisualAdaptorModel); + _q_itemsChanged(0, count(), d->m_roles); +} + +void QQuickVisualAdaptorModel::_q_modelReset() +{ + Q_D(QQuickVisualAdaptorModel); + int oldCount = d->m_count; + d->m_root = QModelIndex(); + d->m_count = d->modelCount(); + emit modelReset(oldCount, d->m_count); + emit rootIndexChanged(); + if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root)) + d->m_abstractItemModel->fetchMore(d->m_root); +} + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QListModelInterface) + +#include diff --git a/src/declarative/items/qquickvisualadaptormodel_p.h b/src/declarative/items/qquickvisualadaptormodel_p.h new file mode 100644 index 0000000000..31eba503ea --- /dev/null +++ b/src/declarative/items/qquickvisualadaptormodel_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKVISUALADAPTORMODEL_P_H +#define QQUICKVISUALADAPTORMODEL_P_H + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; + +class QQuickVisualAdaptorModelPrivate; +class QQuickVisualAdaptorModel : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickVisualAdaptorModel) +public: + enum Flag + { + MetaObjectCacheable = 0x01, + ProxiedObject = 0x02 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + QQuickVisualAdaptorModel(QObject *parent = 0); + virtual ~QQuickVisualAdaptorModel(); + + Flags flags() const; + + QVariant model() const; + void setModel(const QVariant &, QDeclarativeEngine *); + + QVariant rootIndex() const; + void setRootIndex(const QVariant &root); + + QVariant modelIndex(int idx) const; + QVariant parentModelIndex() const; + + int count() const; + QObject *data(int index); + QString stringValue(int index, const QString &role); + void replaceWatchedRoles(const QList &oldRoles, const QList &newRoles); + int indexOf(QObject *object) const; + + bool canFetchMore() const; + void fetchMore(); + +Q_SIGNALS: + void rootIndexChanged(); + void modelReset(int oldCount, int newCount); + + void itemsInserted(int index, int count); + void itemsRemoved(int index, int count); + void itemsMoved(int from, int to, int count); + void itemsChanged(int index, int count); + +private Q_SLOTS: + void _q_itemsChanged(int, int, const QList &); + void _q_itemsInserted(int index, int count); + void _q_itemsRemoved(int index, int count); + void _q_itemsMoved(int from, int to, int count); + void _q_rowsInserted(const QModelIndex &,int,int); + void _q_rowsRemoved(const QModelIndex &,int,int); + void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); + void _q_dataChanged(const QModelIndex&,const QModelIndex&); + void _q_layoutChanged(); + void _q_modelReset(); + +private: + Q_DISABLE_COPY(QQuickVisualAdaptorModel) +}; + +class QQuickVisualAdaptorModelProxyInterface +{ +public: + virtual ~QQuickVisualAdaptorModelProxyInterface() {} + + virtual QObject *proxiedObject() = 0; +}; + +Q_DECLARE_INTERFACE(QQuickVisualAdaptorModelProxyInterface, "com.trolltech.qml.QQuickVisualAdaptorModelProxyInterface") + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/items/qquickvisualdatamodel.cpp b/src/declarative/items/qquickvisualdatamodel.cpp new file mode 100644 index 0000000000..1a24e168d6 --- /dev/null +++ b/src/declarative/items/qquickvisualdatamodel.cpp @@ -0,0 +1,2538 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickvisualdatamodel_p.h" +#include "qquickitem.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +typedef QDeclarativeListCompositor Compositor; + +class QQuickVisualDataGroupEmitter +{ +public: + virtual void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) = 0; + virtual void createdPackage(int, QDeclarativePackage *) {} + virtual void destroyingPackage(QDeclarativePackage *) {} + + QIntrusiveListNode emitterNode; +}; + +typedef QIntrusiveList QQuickVisualDataGroupEmitterList; + +//--------------------------------------------------------------------------- + +class QQuickVisualDataGroupPrivate : public QObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(QQuickVisualDataGroup) + + QQuickVisualDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} + + static QQuickVisualDataGroupPrivate *get(QQuickVisualDataGroup *group) { + return static_cast(QObjectPrivate::get(group)); } + + void setModel(QQuickVisualDataModel *model, Compositor::Group group); + void emitChanges(QV8Engine *engine); + void emitModelUpdated(bool reset); + + void createdPackage(int index, QDeclarativePackage *package); + void destroyingPackage(QDeclarativePackage *package); + + bool parseGroupArgs(QDeclarativeV8Function *args, int *index, int *count, int *groups) const; + + Compositor::Group group; + QDeclarativeGuard model; + QQuickVisualDataGroupEmitterList emitters; + QDeclarativeChangeSet changeSet; + QString name; + bool defaultInclude; +}; + +//--------------------------------------------------------------------------- + +class QQuickVisualDataModelCacheItem; +class QQuickVisualDataModelCacheMetaType; +class QQuickVisualDataModelParts; + +class QQuickVisualDataModelPrivate : public QObjectPrivate, public QQuickVisualDataGroupEmitter +{ + Q_DECLARE_PUBLIC(QQuickVisualDataModel) +public: + QQuickVisualDataModelPrivate(QDeclarativeContext *); + + static QQuickVisualDataModelPrivate *get(QQuickVisualDataModel *m) { + return static_cast(QObjectPrivate::get(m)); + } + + void init(); + void connectModel(QQuickVisualAdaptorModel *model); + + QObject *object(Compositor::Group group, int index, bool complete, bool reference); + void destroy(QObject *object); + QQuickVisualDataModel::ReleaseFlags release(QObject *object); + QString stringValue(Compositor::Group group, int index, const QString &name); + int cacheIndexOf(QObject *object) const; + void emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package); + void emitCreatedItem(Compositor::iterator at, QQuickItem *item) { + emit q_func()->createdItem(at.index[m_compositorGroup], item); } + void emitDestroyingPackage(QDeclarativePackage *package); + void emitDestroyingItem(QQuickItem *item) { emit q_func()->destroyingItem(item); } + + void updateFilterGroup(); + + void addGroups(Compositor::Group group, int index, int count, int groupFlags); + void removeGroups(Compositor::Group group, int index, int count, int groupFlags); + void setGroups(Compositor::Group group, int index, int count, int groupFlags); + + void itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems = 0); + void itemsInserted(const QVector &inserts); + void itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems = 0); + void itemsRemoved(const QVector &removes); + void itemsMoved( + const QVector &removes, const QVector &inserts); + void itemsChanged(const QVector &changes); + template static v8::Local buildChangeList(const QVector &changes); + void emitChanges(); + void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); + + + static void group_append(QDeclarativeListProperty *property, QQuickVisualDataGroup *group); + static int group_count(QDeclarativeListProperty *property); + static QQuickVisualDataGroup *group_at(QDeclarativeListProperty *property, int index); + + QQuickVisualAdaptorModel *m_adaptorModel; + QDeclarativeComponent *m_delegate; + QQuickVisualDataModelCacheMetaType *m_cacheMetaType; + QDeclarativeGuard m_context; + + QList m_cache; + QQuickVisualDataModelParts *m_parts; + QQuickVisualDataGroupEmitterList m_pendingParts; + + QDeclarativeListCompositor m_compositor; + QDeclarativeListCompositor::Group m_compositorGroup; + bool m_complete : 1; + bool m_delegateValidated : 1; + bool m_completePending : 1; + bool m_reset : 1; + bool m_transaction : 1; + + QString m_filterGroup; + QList watchedRoles; + + union { + struct { + QQuickVisualDataGroup *m_cacheItems; + QQuickVisualDataGroup *m_items; + QQuickVisualDataGroup *m_persistedItems; + }; + QQuickVisualDataGroup *m_groups[Compositor::MaximumGroupCount]; + }; + int m_groupCount; +}; + +//--------------------------------------------------------------------------- + +class QQuickVisualPartsModel : public QQuickVisualModel, public QQuickVisualDataGroupEmitter +{ + Q_OBJECT + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) +public: + QQuickVisualPartsModel(QQuickVisualDataModel *model, const QString &part, QObject *parent = 0); + ~QQuickVisualPartsModel(); + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + void updateFilterGroup(); + void updateFilterGroup(Compositor::Group group, const QDeclarativeChangeSet &changeSet); + + int count() const; + bool isValid() const; + QQuickItem *item(int index, bool complete=true); + ReleaseFlags release(QQuickItem *item); + bool completePending() const; + void completeItem(); + QString stringValue(int index, const QString &role); + void setWatchedRoles(QList roles); + + int indexOf(QQuickItem *item, QObject *objectContext) const; + + void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); + + void createdPackage(int index, QDeclarativePackage *package); + void destroyingPackage(QDeclarativePackage *package); + +Q_SIGNALS: + void filterGroupChanged(); + +private: + QQuickVisualDataModel *m_model; + QHash m_packaged; + QString m_part; + QString m_filterGroup; + QList m_watchedRoles; + Compositor::Group m_compositorGroup; + bool m_inheritGroup; +}; + +class QQuickVisualDataModelPartsMetaObject : public QDeclarativeOpenMetaObject +{ +public: + QQuickVisualDataModelPartsMetaObject(QObject *parent) + : QDeclarativeOpenMetaObject(parent) {} + + virtual void propertyCreated(int, QMetaPropertyBuilder &); + virtual QVariant initialValue(int); +}; + +class QQuickVisualDataModelParts : public QObject +{ +Q_OBJECT +public: + QQuickVisualDataModelParts(QQuickVisualDataModel *parent); + + QQuickVisualDataModel *model; + QList models; +}; + +void QQuickVisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); +} + +QVariant QQuickVisualDataModelPartsMetaObject::initialValue(int id) +{ + QQuickVisualDataModelParts *parts = static_cast(object()); + QQuickVisualPartsModel *m = new QQuickVisualPartsModel( + parts->model, QString::fromUtf8(name(id)), parts); + parts->models.append(m); + return QVariant::fromValue(static_cast(m)); +} + +QQuickVisualDataModelParts::QQuickVisualDataModelParts(QQuickVisualDataModel *parent) +: QObject(parent), model(parent) +{ + new QQuickVisualDataModelPartsMetaObject(this); +} + +//--------------------------------------------------------------------------- + +class QQuickVisualDataModelCacheMetaType : public QDeclarativeRefCount +{ +public: + QQuickVisualDataModelCacheMetaType(QV8Engine *engine, QQuickVisualDataModel *model, const QStringList &groupNames); + ~QQuickVisualDataModelCacheMetaType(); + + int parseGroups(const QStringList &groupNames) const; + int parseGroups(QV8Engine *engine, const v8::Local &groupNames) const; + + static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); + static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); + static void set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); + static void set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); + + QDeclarativeGuard model; + const int groupCount; + const int memberPropertyOffset; + const int indexPropertyOffset; + QV8Engine * const v8Engine; + QMetaObject *metaObject; + const QStringList groupNames; + v8::Persistent constructor; +}; + +class QQuickVisualDataModelCacheItem : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(VisualDataItemType) +public: + QQuickVisualDataModelCacheItem(QQuickVisualDataModelCacheMetaType *metaType) + : QV8ObjectResource(metaType->v8Engine) + , metaType(metaType) + , object(0) + , attached(0) + , objectRef(0) + , scriptRef(0) + , groups(0) + { + metaType->addref(); + } + + ~QQuickVisualDataModelCacheItem() + { + Q_ASSERT(scriptRef == 0); + Q_ASSERT(objectRef == 0); + Q_ASSERT(!object); + + metaType->release(); + } + + void referenceObject() { ++objectRef; } + bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } + bool isObjectReferenced() const { return objectRef == 0 && !(groups & Compositor::PersistedFlag); } + + bool isReferenced() const { return objectRef || scriptRef || (groups & Compositor::PersistedFlag); } + + void Dispose(); + + QQuickVisualDataModelCacheMetaType * const metaType; + QDeclarativeGuard object; + QQuickVisualDataModelAttached *attached; + int objectRef; + int scriptRef; + int groups; + int index[Compositor::MaximumGroupCount]; +}; + +class QQuickVisualDataModelAttachedMetaObject : public QAbstractDynamicMetaObject +{ +public: + QQuickVisualDataModelAttachedMetaObject( + QQuickVisualDataModelAttached *attached, QQuickVisualDataModelCacheMetaType *metaType); + ~QQuickVisualDataModelAttachedMetaObject(); + + int metaCall(QMetaObject::Call, int _id, void **); + +private: + QQuickVisualDataModelAttached *attached; + QQuickVisualDataModelCacheMetaType *metaType; +}; + +//--------------------------------------------------------------------------- + +QHash QQuickVisualDataModelAttached::attachedProperties; + +/*! + \qmlclass VisualDataModel QQuickVisualDataModel + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The VisualDataModel encapsulates a model and delegate + + A VisualDataModel encapsulates a model and the delegate that will + be instantiated for items in the model. + + It is usually not necessary to create VisualDataModel elements. + However, it can be useful for manipulating and accessing the \l modelIndex + when a QAbstractItemModel subclass is used as the + model. Also, VisualDataModel is used together with \l Package to + provide delegates to multiple views. + + The example below illustrates using a VisualDataModel with a ListView. + + \snippet doc/src/snippets/declarative/visualdatamodel.qml 0 +*/ + +QQuickVisualDataModelPrivate::QQuickVisualDataModelPrivate(QDeclarativeContext *ctxt) + : m_adaptorModel(0) + , m_delegate(0) + , m_cacheMetaType(0) + , m_context(ctxt) + , m_parts(0) + , m_compositorGroup(Compositor::Cache) + , m_complete(false) + , m_delegateValidated(false) + , m_completePending(false) + , m_reset(false) + , m_transaction(false) + , m_filterGroup(QStringLiteral("items")) + , m_cacheItems(0) + , m_items(0) + , m_groupCount(3) +{ +} + +void QQuickVisualDataModelPrivate::connectModel(QQuickVisualAdaptorModel *model) +{ + Q_Q(QQuickVisualDataModel); + + QObject::connect(model, SIGNAL(itemsInserted(int,int)), q, SLOT(_q_itemsInserted(int,int))); + QObject::connect(model, SIGNAL(itemsRemoved(int,int)), q, SLOT(_q_itemsRemoved(int,int))); + QObject::connect(model, SIGNAL(itemsMoved(int,int,int)), q, SLOT(_q_itemsMoved(int,int,int))); + QObject::connect(model, SIGNAL(itemsChanged(int,int)), q, SLOT(_q_itemsChanged(int,int))); + QObject::connect(model, SIGNAL(modelReset(int,int)), q, SLOT(_q_modelReset(int,int))); +} + +void QQuickVisualDataModelPrivate::init() +{ + Q_Q(QQuickVisualDataModel); + m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); + + m_adaptorModel = new QQuickVisualAdaptorModel; + QObject::connect(m_adaptorModel, SIGNAL(rootIndexChanged()), q, SIGNAL(rootIndexChanged())); + + m_items = new QQuickVisualDataGroup(QStringLiteral("items"), q, Compositor::Default, q); + m_items->setDefaultInclude(true); + m_persistedItems = new QQuickVisualDataGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); + QQuickVisualDataGroupPrivate::get(m_items)->emitters.insert(this); +} + +QQuickVisualDataModel::QQuickVisualDataModel() +: QQuickVisualModel(*(new QQuickVisualDataModelPrivate(0))) +{ + Q_D(QQuickVisualDataModel); + d->init(); +} + +QQuickVisualDataModel::QQuickVisualDataModel(QDeclarativeContext *ctxt, QObject *parent) +: QQuickVisualModel(*(new QQuickVisualDataModelPrivate(ctxt)), parent) +{ + Q_D(QQuickVisualDataModel); + d->init(); +} + +QQuickVisualDataModel::~QQuickVisualDataModel() +{ + Q_D(QQuickVisualDataModel); + foreach (QQuickVisualDataModelCacheItem *cacheItem, d->m_cache) { + cacheItem->object = 0; + cacheItem->objectRef = 0; + if (!cacheItem->isReferenced()) + delete cacheItem; + } + + delete d->m_adaptorModel; + if (d->m_cacheMetaType) + d->m_cacheMetaType->release(); +} + + +void QQuickVisualDataModel::classBegin() +{ +} + +void QQuickVisualDataModel::componentComplete() +{ + Q_D(QQuickVisualDataModel); + d->m_complete = true; + + int defaultGroups = 0; + QStringList groupNames; + groupNames.append(QStringLiteral("items")); + groupNames.append(QStringLiteral("persistedItems")); + if (QQuickVisualDataGroupPrivate::get(d->m_items)->defaultInclude) + defaultGroups |= Compositor::DefaultFlag; + if (QQuickVisualDataGroupPrivate::get(d->m_persistedItems)->defaultInclude) + defaultGroups |= Compositor::PersistedFlag; + for (int i = 3; i < d->m_groupCount; ++i) { + QString name = d->m_groups[i]->name(); + if (name.isEmpty()) { + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else if (name.at(0).isUpper()) { + qmlInfo(d->m_groups[i]) << QQuickVisualDataGroup::tr("Group names must start with a lower case letter"); + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else { + groupNames.append(name); + + QQuickVisualDataGroupPrivate *group = QQuickVisualDataGroupPrivate::get(d->m_groups[i]); + group->setModel(this, Compositor::Group(i)); + if (group->defaultInclude) + defaultGroups |= (1 << i); + } + } + if (!d->m_context) + d->m_context = qmlContext(this); + + d->m_cacheMetaType = new QQuickVisualDataModelCacheMetaType( + QDeclarativeEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); + + d->m_compositor.setGroupCount(d->m_groupCount); + d->m_compositor.setDefaultGroups(defaultGroups); + d->updateFilterGroup(); + + while (!d->m_pendingParts.isEmpty()) + static_cast(d->m_pendingParts.first())->updateFilterGroup(); + + d->connectModel(d->m_adaptorModel); + QVector inserts; + d->m_reset = true; + d->m_compositor.append( + d->m_adaptorModel, + 0, + qMax(0, d->m_adaptorModel->count()), + defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, + &inserts); + d->itemsInserted(inserts); + d->emitChanges(); + + if (d->m_adaptorModel->canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); +} + +/*! + \qmlproperty model QtQuick2::VisualDataModel::model + This property holds the model providing data for the VisualDataModel. + + The model provides a set of data that is used to create the items + for a view. For large or dynamic datasets the model is usually + provided by a C++ model object. The C++ model object must be a \l + {QAbstractItemModel} subclass or a simple list. + + Models can also be created directly in QML, using a \l{ListModel} or + \l{XmlListModel}. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QQuickVisualDataModel::model() const +{ + Q_D(const QQuickVisualDataModel); + return d->m_adaptorModel->model(); +} + +void QQuickVisualDataModel::setModel(const QVariant &model) +{ + Q_D(QQuickVisualDataModel); + d->m_adaptorModel->setModel(model, d->m_context ? d->m_context->engine() : qmlEngine(this)); + if (d->m_complete && d->m_adaptorModel->canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); +} + +/*! + \qmlproperty Component QtQuick2::VisualDataModel::delegate + + The delegate provides a template defining each item instantiated by a view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. +*/ +QDeclarativeComponent *QQuickVisualDataModel::delegate() const +{ + Q_D(const QQuickVisualDataModel); + return d->m_delegate; +} + +void QQuickVisualDataModel::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QQuickVisualDataModel); + if (d->m_transaction) { + qmlInfo(this) << tr("The delegate of a VisualDataModel cannot be changed within onUpdated."); + return; + } + bool wasValid = d->m_delegate != 0; + d->m_delegate = delegate; + d->m_delegateValidated = false; + if (wasValid && d->m_complete) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQuickVisualDataGroupPrivate::get(d->m_groups[i])->changeSet.remove( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + if (d->m_complete && d->m_delegate) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQuickVisualDataGroupPrivate::get(d->m_groups[i])->changeSet.insert( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + d->emitChanges(); +} + +/*! + \qmlproperty QModelIndex QtQuick2::VisualDataModel::rootIndex + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. \c rootIndex allows the children of + any node in a QAbstractItemModel to be provided by this model. + + This property only affects models of type QAbstractItemModel that + are hierarchical (e.g, a tree model). + + For example, here is a simple interactive file system browser. + When a directory name is clicked, the view's \c rootIndex is set to the + QModelIndex node of the clicked directory, thus updating the view to show + the new directory's contents. + + \c main.cpp: + \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/main.cpp 0 + + \c view.qml: + \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/view.qml 0 + + If the \l model is a QAbstractItemModel subclass, the delegate can also + reference a \c hasModelChildren property (optionally qualified by a + \e model. prefix) that indicates whether the delegate's model item has + any child nodes. + + + \sa modelIndex(), parentModelIndex() +*/ +QVariant QQuickVisualDataModel::rootIndex() const +{ + Q_D(const QQuickVisualDataModel); + return d->m_adaptorModel->rootIndex(); +} + +void QQuickVisualDataModel::setRootIndex(const QVariant &root) +{ + Q_D(QQuickVisualDataModel); + d->m_adaptorModel->setRootIndex(root); +} + +/*! + \qmlmethod QModelIndex QtQuick2::VisualDataModel::modelIndex(int index) + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the specified index. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQuickVisualDataModel::modelIndex(int idx) const +{ + Q_D(const QQuickVisualDataModel); + return d->m_adaptorModel->modelIndex(idx); +} + +/*! + \qmlmethod QModelIndex QtQuick2::VisualDataModel::parentModelIndex() + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the parent of the current rootIndex. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQuickVisualDataModel::parentModelIndex() const +{ + Q_D(const QQuickVisualDataModel); + return d->m_adaptorModel->parentModelIndex(); +} + +/*! + \qmlproperty int QtQuick2::VisualDataModel::count +*/ + +int QQuickVisualDataModel::count() const +{ + Q_D(const QQuickVisualDataModel); + if (!d->m_delegate) + return 0; + return d->m_compositor.count(d->m_compositorGroup); +} + +void QQuickVisualDataModelPrivate::destroy(QObject *object) +{ + QObjectPrivate *p = QObjectPrivate::get(object); + Q_ASSERT(p->declarativeData); + QDeclarativeData *data = static_cast(p->declarativeData); + if (data->ownContext && data->context) + data->context->clearContext(); + object->deleteLater(); +} + +QQuickVisualDataModel::ReleaseFlags QQuickVisualDataModelPrivate::release(QObject *object) +{ + QQuickVisualDataModel::ReleaseFlags stat = 0; + if (!object) + return stat; + + int cacheIndex = cacheIndexOf(object); + if (cacheIndex != -1) { + QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + if (cacheItem->releaseObject()) { + destroy(object); + cacheItem->object = 0; + stat |= QQuickVisualModel::Destroyed; + if (!cacheItem->isReferenced()) { + m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + m_cache.removeAt(cacheIndex); + delete cacheItem; + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } + } else { + stat |= QQuickVisualDataModel::Referenced; + } + } + return stat; +} + +/* + Returns ReleaseStatus flags. +*/ + +QQuickVisualDataModel::ReleaseFlags QQuickVisualDataModel::release(QQuickItem *item) +{ + Q_D(QQuickVisualDataModel); + QQuickVisualModel::ReleaseFlags stat = d->release(item); + if (stat & Destroyed) + item->setParentItem(0); + return stat; +} + +void QQuickVisualDataModelPrivate::group_append( + QDeclarativeListProperty *property, QQuickVisualDataGroup *group) +{ + QQuickVisualDataModelPrivate *d = static_cast(property->data); + if (d->m_complete) + return; + if (d->m_groupCount == 11) { + qmlInfo(d->q_func()) << QQuickVisualDataModel::tr("The maximum number of supported VisualDataGroups is 8"); + return; + } + d->m_groups[d->m_groupCount] = group; + d->m_groupCount += 1; +} + +int QQuickVisualDataModelPrivate::group_count( + QDeclarativeListProperty *property) +{ + QQuickVisualDataModelPrivate *d = static_cast(property->data); + return d->m_groupCount - 1; +} + +QQuickVisualDataGroup *QQuickVisualDataModelPrivate::group_at( + QDeclarativeListProperty *property, int index) +{ + QQuickVisualDataModelPrivate *d = static_cast(property->data); + return index >= 0 && index < d->m_groupCount - 1 + ? d->m_groups[index - 1] + : 0; +} + +/*! + \qmlproperty list QtQuick2::VisualDataModel::groups + + This property holds a visual data model's group definitions. + + Groups define a sub-set of the items in a visual data model and can be used to filter + a model. + + For every group defined in a VisualDataModel two attached properties are added to each + delegate item. The first of the form VisualDataModel.in\e{GroupName} holds whether the + item belongs to the group and the second VisualDataModel.\e{groupName}Index holds the + index of the item in that group. + + The following example illustrates using groups to select items in a model. + + \snippet doc/src/snippets/declarative/visualdatagroup.qml 0 +*/ + +QDeclarativeListProperty QQuickVisualDataModel::groups() +{ + Q_D(QQuickVisualDataModel); + return QDeclarativeListProperty( + this, + d, + QQuickVisualDataModelPrivate::group_append, + QQuickVisualDataModelPrivate::group_count, + QQuickVisualDataModelPrivate::group_at); +} + +/*! + \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::items + + This property holds visual data model's default group to which all new items are added. +*/ + +QQuickVisualDataGroup *QQuickVisualDataModel::items() +{ + Q_D(QQuickVisualDataModel); + return d->m_items; +} + +/*! + \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::persistedItems + + This property holds visual data model's persisted items group. + + Items in this group are not destroyed when released by a view, instead they are persisted + until removed from the group. + + An item can be removed from the persistedItems group by setting the + VisualDataModel.inPersistedItems property to false. If the item is not referenced by a view + at that time it will be destroyed. Adding an item to this group will not create a new + instance. + + Items returned by the \l QtQuick2::VisualDataGroup::create() function are automatically added + to this group. +*/ + +QQuickVisualDataGroup *QQuickVisualDataModel::persistedItems() +{ + Q_D(QQuickVisualDataModel); + return d->m_persistedItems; +} + +/*! + \qmlproperty string QtQuick2::VisualDataModel::filterOnGroup + + This property holds the name of the group used to filter the visual data model. + + Only items which belong to this group are visible to a view. + + By default this is the \l items group. +*/ + +QString QQuickVisualDataModel::filterGroup() const +{ + Q_D(const QQuickVisualDataModel); + return d->m_filterGroup; +} + +void QQuickVisualDataModel::setFilterGroup(const QString &group) +{ + Q_D(QQuickVisualDataModel); + + if (d->m_transaction) { + qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); + return; + } + + if (d->m_filterGroup != group) { + d->m_filterGroup = group; + d->updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQuickVisualDataModel::resetFilterGroup() +{ + setFilterGroup(QStringLiteral("items")); +} + +void QQuickVisualDataModelPrivate::updateFilterGroup() +{ + Q_Q(QQuickVisualDataModel); + if (!m_cacheMetaType) + return; + + QDeclarativeListCompositor::Group previousGroup = m_compositorGroup; + m_compositorGroup = Compositor::Default; + for (int i = 1; i < m_groupCount; ++i) { + if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQuickVisualDataGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QDeclarativeChangeSet changeSet; + changeSet.apply(removes, inserts); + emit q->modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit q->countChanged(); + + if (m_parts) { + foreach (QQuickVisualPartsModel *model, m_parts->models) + model->updateFilterGroup(m_compositorGroup, changeSet); + } + } +} + +/*! + \qmlproperty object QtQuick2::VisualDataModel::parts + + The \a parts property selects a VisualDataModel which creates + delegates from the part named. This is used in conjunction with + the \l Package element. + + For example, the code below selects a model which creates + delegates named \e list from a \l Package: + + \code + VisualDataModel { + id: visualModel + delegate: Package { + Item { Package.name: "list" } + } + model: myModel + } + + ListView { + width: 200; height:200 + model: visualModel.parts.list + } + \endcode + + \sa Package +*/ + +QObject *QQuickVisualDataModel::parts() +{ + Q_D(QQuickVisualDataModel); + if (!d->m_parts) + d->m_parts = new QQuickVisualDataModelParts(this); + return d->m_parts; +} + +void QQuickVisualDataModelPrivate::emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->createdPackage(at.index[i], package); +} + +void QQuickVisualDataModelPrivate::emitDestroyingPackage(QDeclarativePackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->destroyingPackage(package); +} + +QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index, bool complete, bool reference) +{ + Q_Q(QQuickVisualDataModel); + + Compositor::iterator it = m_compositor.find(group, index); + QQuickVisualDataModelCacheItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; + + if (!cacheItem) { + cacheItem = new QQuickVisualDataModelCacheItem(m_cacheMetaType); + for (int i = 0; i < m_groupCount; ++i) + cacheItem->index[i] = it.index[i]; + cacheItem->groups = it->flags & Compositor::GroupMask; + } + + if (!cacheItem->object) { + QObject *data = m_adaptorModel->data(it.modelIndex()); + + QDeclarativeContext *creationContext = m_delegate->creationContext(); + QDeclarativeContext *rootContext = new QDeclarativeContext( + creationContext ? creationContext : m_context.data()); + QDeclarativeContext *ctxt = rootContext; + if (m_adaptorModel->flags() & QQuickVisualAdaptorModel::ProxiedObject) { + if (QQuickVisualAdaptorModelProxyInterface *proxy = qobject_cast(data)) { + ctxt->setContextObject(proxy->proxiedObject()); + ctxt = new QDeclarativeContext(ctxt, ctxt); + } + } + + QDeclarative_setParent_noEvent(data, ctxt); + ctxt->setContextProperty(QLatin1String("model"), data); + ctxt->setContextObject(data); + + m_completePending = false; + cacheItem->object = m_delegate->beginCreate(ctxt); + + if (cacheItem->object) { + QDeclarative_setParent_noEvent(rootContext, cacheItem->object); + if (!it->inCache()) { + m_cache.insert(it.cacheIndex, cacheItem); + m_compositor.setFlags(it, 1, Compositor::CacheFlag); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } + + cacheItem->attached = QQuickVisualDataModelAttached::properties(cacheItem->object); + cacheItem->attached->m_cacheItem = cacheItem; + new QQuickVisualDataModelAttachedMetaObject(cacheItem->attached, m_cacheMetaType); + cacheItem->attached->emitChanges(); + + if (QDeclarativePackage *package = qobject_cast(cacheItem->object)) { + emitCreatedPackage(it, package); + } else if (!reference) { + if (QQuickItem *item = qobject_cast(cacheItem->object)) + emitCreatedItem(it, item); + } + + m_completePending = !complete; + if (complete) + m_delegate->completeCreate(); + } else { + delete rootContext; + if (!it->inCache()) + delete cacheItem; + qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; + return 0; + } + } + + if (index == m_compositor.count(group) - 1 && m_adaptorModel->canFetchMore()) + QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); + if (reference) + cacheItem->referenceObject(); + return cacheItem->object; +} + +QQuickItem *QQuickVisualDataModel::item(int index, bool complete) +{ + Q_D(QQuickVisualDataModel); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "VisualDataModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return 0; + } + + QObject *object = d->object(d->m_compositorGroup, index, complete, true); + if (QQuickItem *item = qobject_cast(object)) + return item; + if (d->m_completePending) + completeItem(); + d->release(object); + if (!d->m_delegateValidated) { + if (object) + qmlInfo(d->m_delegate) << QQuickVisualDataModel::tr("Delegate component must be Item type."); + d->m_delegateValidated = true; + } + return 0; +} + +bool QQuickVisualDataModel::completePending() const +{ + Q_D(const QQuickVisualDataModel); + return d->m_completePending; +} + +void QQuickVisualDataModel::completeItem() +{ + Q_D(QQuickVisualDataModel); + d->m_delegate->completeCreate(); + d->m_completePending = false; +} + +QString QQuickVisualDataModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) +{ + Compositor::iterator it = m_compositor.find(group, index); + if (QQuickVisualAdaptorModel *model = it.list()) { + return model->stringValue(it.modelIndex(), name); + } + return QString(); +} + +QString QQuickVisualDataModel::stringValue(int index, const QString &name) +{ + Q_D(QQuickVisualDataModel); + return d->stringValue(d->m_compositorGroup, index, name); +} + +int QQuickVisualDataModelPrivate::cacheIndexOf(QObject *object) const +{ + for (int cacheIndex = 0; cacheIndex < m_cache.count(); ++cacheIndex) { + if (m_cache.at(cacheIndex)->object == object) + return cacheIndex; + } + return -1; +} + +int QQuickVisualDataModel::indexOf(QQuickItem *item, QObject *) const +{ + Q_D(const QQuickVisualDataModel); + const int cacheIndex = d->cacheIndexOf(item); + return cacheIndex != -1 + ? d->m_cache.at(cacheIndex)->index[d->m_compositorGroup] + : -1; +} + +void QQuickVisualDataModel::setWatchedRoles(QList roles) +{ + Q_D(QQuickVisualDataModel); + d->m_adaptorModel->replaceWatchedRoles(d->watchedRoles, roles); + d->watchedRoles = roles; +} + +void QQuickVisualDataModelPrivate::addGroups(Compositor::Group group, int index, int count, int groupFlags) +{ + QVector inserts; + m_compositor.setFlags(group, index, count, groupFlags, &inserts); + itemsInserted(inserts); + emitChanges(); +} + +void QQuickVisualDataModelPrivate::removeGroups(Compositor::Group group, int index, int count, int groupFlags) +{ + QVector removes; + m_compositor.clearFlags(group, index, count, groupFlags, &removes); + itemsRemoved(removes); + emitChanges(); +} + +void QQuickVisualDataModelPrivate::setGroups(Compositor::Group group, int index, int count, int groupFlags) +{ + QVector inserts; + m_compositor.setFlags(group, index, count, groupFlags, &inserts); + itemsInserted(inserts); + + const int removeFlags = ~groupFlags & Compositor::GroupMask; + QVector removes; + m_compositor.clearFlags(group, index, count, removeFlags, &removes); + itemsRemoved(removes); + + emitChanges(); +} + +bool QQuickVisualDataModel::event(QEvent *e) +{ + Q_D(QQuickVisualDataModel); + if (e->type() == QEvent::UpdateRequest) + d->m_adaptorModel->fetchMore(); + return QQuickVisualModel::event(e); +} + +void QQuickVisualDataModelPrivate::itemsChanged(const QVector &changes) +{ + if (!m_delegate) + return; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); + + foreach (const Compositor::Change &change, changes) { + for (int i = 1; i < m_groupCount; ++i) { + if (change.inGroup(i)) { + translatedChanges[i].append( + QDeclarativeChangeSet::Change(change.index[i], change.count)); + } + } + } + + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedChanges.at(i)); +} + +void QQuickVisualDataModel::_q_itemsChanged(int index, int count) +{ + Q_D(QQuickVisualDataModel); + if (count <= 0) + return; + QVector changes; + d->m_compositor.listItemsChanged(d->m_adaptorModel, index, count, &changes); + d->itemsChanged(changes); + d->emitChanges(); +} + +void QQuickVisualDataModelPrivate::itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems) +{ + int cacheIndex = 0; + + int inserted[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + inserted[i] = 0; + + foreach (const Compositor::Insert &insert, inserts) { + for (; cacheIndex < insert.cacheIndex; ++cacheIndex) { + QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + if (!cacheItem->groups) + continue; + for (int i = 1; i < m_groupCount; ++i) + cacheItem->index[i] += inserted[i]; + } + for (int i = 1; i < m_groupCount; ++i) { + if (insert.inGroup(i)) { + (*translatedInserts)[i].append( + QDeclarativeChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); + inserted[i] += insert.count; + } + } + + if (!insert.inCache()) + continue; + + if (movedItems && insert.isMove()) { + QList items = movedItems->take(insert.moveId); + Q_ASSERT(items.count() == insert.count); + m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); + } + if (insert.inGroup()) { + for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { + QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + cacheItem->groups |= insert.flags & Compositor::GroupMask; + for (int i = 1; i < m_groupCount; ++i) { + cacheItem->index[i] = cacheItem->groups & (1 << i) + ? insert.index[i] + offset + : insert.index[i]; + } + } + } else { + cacheIndex = insert.cacheIndex + insert.count; + } + } + for (; cacheIndex < m_cache.count(); ++cacheIndex) { + QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + if (!cacheItem->groups) + continue; + for (int i = 1; i < m_groupCount; ++i) + cacheItem->index[i] += inserted[i]; + } +} + +void QQuickVisualDataModelPrivate::itemsInserted(const QVector &inserts) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedInserts.at(i)); +} + +void QQuickVisualDataModel::_q_itemsInserted(int index, int count) +{ + + Q_D(QQuickVisualDataModel); + if (count <= 0) + return; + QVector inserts; + d->m_compositor.listItemsInserted(d->m_adaptorModel, index, count, &inserts); + d->itemsInserted(inserts); + d->emitChanges(); +} + +void QQuickVisualDataModelPrivate::itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems) +{ + int cacheIndex = 0; + int removedCache = 0; + + int removed[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + removed[i] = 0; + + foreach (const Compositor::Remove &remove, removes) { + for (; cacheIndex < remove.cacheIndex; ++cacheIndex) { + QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + if (!cacheItem->groups) + continue; + for (int i = 1; i < m_groupCount; ++i) + cacheItem->index[i] -= removed[i]; + } + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) { + (*translatedRemoves)[i].append( + QDeclarativeChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); + removed[i] += remove.count; + } + } + + if (!remove.inCache()) + continue; + + if (movedItems && remove.isMove()) { + movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); + QList::iterator begin = m_cache.begin() + remove.cacheIndex; + QList::iterator end = begin + remove.count; + m_cache.erase(begin, end); + } else { + for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { + QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0) { + destroy(cacheItem->object); + if (QDeclarativePackage *package = qobject_cast(cacheItem->object)) + emitDestroyingPackage(package); + else if (QQuickItem *item = qobject_cast(cacheItem->object)) + emitDestroyingItem(item); + cacheItem->object = 0; + } + if (remove.groups() == cacheItem->groups && !cacheItem->isReferenced()) { + m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + m_cache.removeAt(cacheIndex); + delete cacheItem; + --cacheIndex; + ++removedCache; + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } else if (remove.groups() == cacheItem->groups) { + cacheItem->groups = 0; + for (int i = 1; i < m_groupCount; ++i) + cacheItem->index[i] = -1; + } else { + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) + cacheItem->index[i] = remove.index[i]; + } + cacheItem->groups &= ~remove.flags & Compositor::GroupMask; + } + } + } + } + + for (; cacheIndex < m_cache.count(); ++cacheIndex) { + QQuickVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); + if (!cacheItem->groups) + continue; + for (int i = 1; i < m_groupCount; ++i) + cacheItem->index[i] -= removed[i]; + } +} + +void QQuickVisualDataModelPrivate::itemsRemoved(const QVector &removes) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedRemoves.at(i)); +} + +void QQuickVisualDataModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QQuickVisualDataModel); + if (count <= 0) + return; + + QVector removes; + d->m_compositor.listItemsRemoved(d->m_adaptorModel, index, count, &removes); + d->itemsRemoved(removes); + d->emitChanges(); +} + +void QQuickVisualDataModelPrivate::itemsMoved( + const QVector &removes, const QVector &inserts) +{ + QHash > movedItems; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves, &movedItems); + + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts, &movedItems); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + Q_ASSERT(movedItems.isEmpty()); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) { + QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply( + translatedRemoves.at(i), + translatedInserts.at(i)); + } +} + +void QQuickVisualDataModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QQuickVisualDataModel); + if (count <= 0) + return; + + QVector removes; + QVector inserts; + d->m_compositor.listItemsMoved(d->m_adaptorModel, from, to, count, &removes, &inserts); + d->itemsMoved(removes, inserts); + d->emitChanges(); +} + +template v8::Local +QQuickVisualDataModelPrivate::buildChangeList(const QVector &changes) +{ + v8::Local indexes = v8::Array::New(changes.count()); + v8::Local indexKey = v8::String::New("index"); + v8::Local countKey = v8::String::New("count"); + v8::Local moveIdKey = v8::String::New("moveId"); + + for (int i = 0; i < changes.count(); ++i) { + v8::Local object = v8::Object::New(); + object->Set(indexKey, v8::Integer::New(changes.at(i).index)); + object->Set(countKey, v8::Integer::New(changes.at(i).count)); + object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); + indexes->Set(i, object); + } + return indexes; +} + +void QQuickVisualDataModelPrivate::emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) +{ + Q_Q(QQuickVisualDataModel); + emit q->modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQuickVisualDataModelPrivate::emitChanges() +{ + if (m_transaction || !m_complete) + return; + + m_transaction = true; + QV8Engine *engine = QDeclarativeEnginePrivate::getV8Engine(m_context->engine()); + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->emitChanges(engine); + m_transaction = false; + + const bool reset = m_reset; + m_reset = false; + for (int i = 1; i < m_groupCount; ++i) + QQuickVisualDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); + + foreach (QQuickVisualDataModelCacheItem *cacheItem, m_cache) { + if (cacheItem->object) + cacheItem->attached->emitChanges(); + } +} + +void QQuickVisualDataModel::_q_modelReset(int oldCount, int newCount) +{ + Q_D(QQuickVisualDataModel); + if (!d->m_delegate) + return; + + QVector removes; + QVector inserts; + if (oldCount) + d->m_compositor.listItemsRemoved(d->m_adaptorModel, 0, oldCount, &removes); + if (newCount) + d->m_compositor.listItemsInserted(d->m_adaptorModel, 0, newCount, &inserts); + d->itemsMoved(removes, inserts); + d->m_reset = true; + d->emitChanges(); +} + +QQuickVisualDataModelAttached *QQuickVisualDataModel::qmlAttachedProperties(QObject *obj) +{ + return QQuickVisualDataModelAttached::properties(obj); +} + +//============================================================================ + +QQuickVisualDataModelCacheMetaType::QQuickVisualDataModelCacheMetaType( + QV8Engine *engine, QQuickVisualDataModel *model, const QStringList &groupNames) + : model(model) + , groupCount(groupNames.count() + 1) + , memberPropertyOffset(QQuickVisualDataModelAttached::staticMetaObject.propertyCount()) + , indexPropertyOffset(QQuickVisualDataModelAttached::staticMetaObject.propertyCount() + groupNames.count()) + , v8Engine(engine) + , metaObject(0) + , groupNames(groupNames) +{ + QMetaObjectBuilder builder; + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + builder.setClassName(QQuickVisualDataModelAttached::staticMetaObject.className()); + builder.setSuperClass(&QQuickVisualDataModelAttached::staticMetaObject); + + v8::HandleScope handleScope; + v8::Context::Scope contextScope(engine->context()); + v8::Local ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("model"), get_model); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("groups"), get_groups, set_groups); + + int notifierId = 0; + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + QString propertyName = QStringLiteral("in") + groupNames.at(i); + propertyName.replace(2, 1, propertyName.at(2).toUpper()); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "bool", notifierId); + propertyBuilder.setWritable(true); + + ft->PrototypeTemplate()->SetAccessor( + engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); + } + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "int", notifierId); + propertyBuilder.setWritable(true); + + ft->PrototypeTemplate()->SetAccessor( + engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); + } + + metaObject = builder.toMetaObject(); + + constructor = qPersistentNew(ft->GetFunction()); +} + +QQuickVisualDataModelCacheMetaType::~QQuickVisualDataModelCacheMetaType() +{ + qFree(metaObject); + qPersistentDispose(constructor); +} + +int QQuickVisualDataModelCacheMetaType::parseGroups(const QStringList &groups) const +{ + int groupFlags = 0; + foreach (const QString &groupName, groups) { + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + return groupFlags; +} + +int QQuickVisualDataModelCacheMetaType::parseGroups(QV8Engine *engine, const v8::Local &groups) const +{ + int groupFlags = 0; + if (groups->IsString()) { + const QString groupName = engine->toString(groups); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } else if (groups->IsArray()) { + v8::Local array = v8::Local::Cast(groups); + for (uint i = 0; i < array->Length(); ++i) { + const QString groupName = engine->toString(array->Get(i)); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + } + return groupFlags; +} + +v8::Handle QQuickVisualDataModelCacheMetaType::get_model( + v8::Local, const v8::AccessorInfo &info) +{ + QQuickVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + if (!cacheItem->metaType->model) + return v8::Undefined(); + QObject *data = 0; + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(cacheItem->metaType->model); + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) { + Compositor::iterator it = model->m_compositor.find( + Compositor::Group(i), cacheItem->index[i]); + if (QQuickVisualAdaptorModel *list = it.list()) + data = list->data(it.modelIndex()); + break; + } + } + if (!data) + return v8::Undefined(); + return cacheItem->engine->newQObject(data); +} + +v8::Handle QQuickVisualDataModelCacheMetaType::get_groups( + v8::Local, const v8::AccessorInfo &info) +{ + QQuickVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + + QStringList groups; + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) + groups.append(cacheItem->metaType->groupNames.at(i - 1)); + } + + return cacheItem->engine->fromVariant(groups); +} + +void QQuickVisualDataModelCacheMetaType::set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQuickVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR_SETTER("Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(cacheItem->engine, value); + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) { + model->setGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlags); + break; + } + } +} + +v8::Handle QQuickVisualDataModelCacheMetaType::get_member( + v8::Local, const v8::AccessorInfo &info) +{ + QQuickVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + + return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); +} + +void QQuickVisualDataModelCacheMetaType::set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQuickVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR_SETTER("Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(cacheItem->metaType->model); + + Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); + const bool member = value->BooleanValue(); + const int groupFlag = (1 << group); + if (member == ((cacheItem->groups & groupFlag) != 0)) + return; + + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) { + if (member) + model->addGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag); + else + model->removeGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag); + break; + } + } +} + +v8::Handle QQuickVisualDataModelCacheMetaType::get_index( + v8::Local, const v8::AccessorInfo &info) +{ + QQuickVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); + if (!cacheItem) + V8THROW_ERROR("Not a valid VisualData object"); + + return v8::Integer::New(cacheItem->index[info.Data()->Int32Value()]); +} + + +//--------------------------------------------------------------------------- + +void QQuickVisualDataModelCacheItem::Dispose() +{ + --scriptRef; + if (isReferenced()) + return; + + if (metaType->model) { + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(metaType->model); + const int cacheIndex = model->m_cache.indexOf(this); + if (cacheIndex != -1) { + model->m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + model->m_cache.removeAt(cacheIndex); + } + } + delete this; +} + +//--------------------------------------------------------------------------- + +QQuickVisualDataModelAttachedMetaObject::QQuickVisualDataModelAttachedMetaObject( + QQuickVisualDataModelAttached *attached, QQuickVisualDataModelCacheMetaType *metaType) + : attached(attached) + , metaType(metaType) +{ + metaType->addref(); + *static_cast(this) = *metaType->metaObject; + QObjectPrivate::get(attached)->metaObject = this; +} + +QQuickVisualDataModelAttachedMetaObject::~QQuickVisualDataModelAttachedMetaObject() +{ + metaType->release(); +} + +int QQuickVisualDataModelAttachedMetaObject::metaCall(QMetaObject::Call call, int _id, void **arguments) +{ + if (call == QMetaObject::ReadProperty) { + if (_id >= metaType->indexPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - metaType->indexPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_cacheItem->index[group]; + return -1; + } else if (_id >= metaType->memberPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - metaType->memberPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); + return -1; + } + } else if (call == QMetaObject::WriteProperty) { + if (_id >= metaType->memberPropertyOffset) { + if (!metaType->model) + return -1; + Compositor::Group group = Compositor::Group(_id - metaType->memberPropertyOffset + 1); + const bool member = attached->m_cacheItem->groups & (1 << group); + if (member != *static_cast(arguments[0])) { + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(metaType->model); + const int cacheIndex = model->m_cache.indexOf(attached->m_cacheItem); + if (member) + model->removeGroups(Compositor::Cache, cacheIndex, 1, (1 << group)); + else + model->addGroups(Compositor::Cache, cacheIndex, 1, (1 << group)); + } + return -1; + } + } + return attached->qt_metacall(call, _id, arguments); +} + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::model + + This attached property holds the visual data model this delegate instance belongs to. + + It is attached to each instance of the delegate. +*/ + +QQuickVisualDataModel *QQuickVisualDataModelAttached::model() const +{ + return m_cacheItem ? m_cacheItem->metaType->model : 0; +} + +/*! + \qmlattachedproperty stringlist QtQuick2::VisualDataModel::groups + + This attached property holds the name of VisualDataGroups the item belongs to. + + It is attached to each instance of the delegate. +*/ + +QStringList QQuickVisualDataModelAttached::groups() const +{ + QStringList groups; + + if (!m_cacheItem) + return groups; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_cacheItem->groups & (1 << i)) + groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); + } + return groups; +} + +void QQuickVisualDataModelAttached::setGroups(const QStringList &groups) +{ + if (!m_cacheItem) + return; + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_cacheItem->metaType->model); + + const int cacheIndex = model->m_cache.indexOf(m_cacheItem); + const int groupFlags = model->m_cacheMetaType->parseGroups(groups); + model->setGroups(Compositor::Cache, cacheIndex, 1, groupFlags); +} + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::inItems + + This attached property holds whether the item belongs to the default \l items VisualDataGroup. + + Changing this property will add or remove the item from the items group. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::itemsIndex + + This attached property holds the index of the item in the default \l items VisualDataGroup. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::inPersistedItems + + This attached property holds whether the item belongs to the \l persistedItems VisualDataGroup. + + Changing this property will add or remove the item from the items group. Change with caution + as removing an item from the persistedItems group will destroy the current instance if it is + not referenced by a model. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::persistedItemsIndex + + This attached property holds the index of the item in the \l persistedItems VisualDataGroup. + + It is attached to each instance of the delegate. +*/ + +void QQuickVisualDataModelAttached::emitChanges() +{ + if (m_modelChanged) { + m_modelChanged = false; + emit modelChanged(); + } + + const int groupChanges = m_previousGroups ^ m_cacheItem->groups; + m_previousGroups = m_cacheItem->groups; + + int indexChanges = 0; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_previousIndex[i] != m_cacheItem->index[i]) { + m_previousIndex[i] = m_cacheItem->index[i]; + indexChanges |= (1 << i); + } + } + + int notifierId = 0; + const QMetaObject *meta = metaObject(); + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (groupChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (indexChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + + if (groupChanges) + emit groupsChanged(); +} + +//============================================================================ + +void QQuickVisualDataGroupPrivate::setModel(QQuickVisualDataModel *m, Compositor::Group g) +{ + Q_ASSERT(!model); + model = m; + group = g; +} + +void QQuickVisualDataGroupPrivate::emitChanges(QV8Engine *engine) +{ + Q_Q(QQuickVisualDataGroup); + static int idx = signalIndex("changed(QDeclarativeV8Handle,QDeclarativeV8Handle)"); + if (isSignalConnected(idx)) { + v8::HandleScope handleScope; + v8::Context::Scope contextScope(engine->context()); + v8::Local removed = QQuickVisualDataModelPrivate::buildChangeList(changeSet.removes()); + v8::Local inserted = QQuickVisualDataModelPrivate::buildChangeList(changeSet.inserts()); + emit q->changed( + QDeclarativeV8Handle::fromHandle(removed), QDeclarativeV8Handle::fromHandle(inserted)); + } + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQuickVisualDataGroupPrivate::emitModelUpdated(bool reset) +{ + for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->emitModelUpdated(changeSet, reset); + changeSet.clear(); +} + +void QQuickVisualDataGroupPrivate::createdPackage(int index, QDeclarativePackage *package) +{ + for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->createdPackage(index, package); +} + +void QQuickVisualDataGroupPrivate::destroyingPackage(QDeclarativePackage *package) +{ + for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->destroyingPackage(package); +} + +/*! + \qmlclass VisualDataGroup QQuickVisualDataGroup + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The VisualDataGroup encapsulates a filtered set of visual data items. + +*/ + +QQuickVisualDataGroup::QQuickVisualDataGroup(QObject *parent) + : QObject(*new QQuickVisualDataGroupPrivate, parent) +{ +} + +QQuickVisualDataGroup::QQuickVisualDataGroup( + const QString &name, QQuickVisualDataModel *model, int index, QObject *parent) + : QObject(*new QQuickVisualDataGroupPrivate, parent) +{ + Q_D(QQuickVisualDataGroup); + d->name = name; + d->setModel(model, Compositor::Group(index)); +} + +QQuickVisualDataGroup::~QQuickVisualDataGroup() +{ +} + +/*! + \qmlproperty string QtQuick2::VisualDataGroup::name + + This property holds the name of the group. + + Each group in a model must have a unique name starting with a lower case letter. +*/ + +QString QQuickVisualDataGroup::name() const +{ + Q_D(const QQuickVisualDataGroup); + return d->name; +} + +void QQuickVisualDataGroup::setName(const QString &name) +{ + Q_D(QQuickVisualDataGroup); + if (d->model) + return; + if (d->name != name) { + d->name = name; + emit nameChanged(); + } +} + +/*! + \qmlproperty int QtQuick2::VisualDataGroup::count + + This property holds the number of items in the group. +*/ + +int QQuickVisualDataGroup::count() const +{ + Q_D(const QQuickVisualDataGroup); + if (!d->model) + return 0; + return QQuickVisualDataModelPrivate::get(d->model)->m_compositor.count(d->group); +} + +/*! + \qmlproperty bool QtQuick2::VisualDataGroup::includeByDefault + + This property holds whether new items are assigned to this group by default. +*/ + +bool QQuickVisualDataGroup::defaultInclude() const +{ + Q_D(const QQuickVisualDataGroup); + return d->defaultInclude; +} + +void QQuickVisualDataGroup::setDefaultInclude(bool include) +{ + Q_D(QQuickVisualDataGroup); + if (d->defaultInclude != include) { + d->defaultInclude = include; + + if (d->model) { + if (include) + QQuickVisualDataModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); + else + QQuickVisualDataModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); + } + emit defaultIncludeChanged(); + } +} + +/*! + \qmlmethod var QtQuick2::VisualDataGroup::get(int index) + + Returns a javascript object describing the item at \a index in the group. + + The returned object contains the same information that is available to a delegate from the + VisualDataModel attached as well as the model for that item. It has the properties: + + \list + \o \b model The model data of the item. This is the same as the model context property in + a delegate + \o \b groups A list the of names of groups the item is a member of. This property can be + written to change the item's membership. + \o \b inItems Whether the item belongs to the \l {QtQuick2::VisualDataModel::items}{items} group. + Writing to this property will add or remove the item from the group. + \o \b itemsIndex The index of the item within the \l {QtQuick2::VisualDataModel::items}{items} group. + \o \b {in\i{GroupName}} Whether the item belongs to the dynamic group \i groupName. Writing to + this property will add or remove the item from the group. + \o \b {\i{groupName}Index} The index of the item within the dynamic group \i groupName. + \endlist +*/ + +QDeclarativeV8Handle QQuickVisualDataGroup::get(int index) +{ + Q_D(QQuickVisualDataGroup); + if (!d->model) + return QDeclarativeV8Handle::fromHandle(v8::Undefined());; + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("get: index out of range"); + return QDeclarativeV8Handle::fromHandle(v8::Undefined()); + } + + Compositor::iterator it = model->m_compositor.find(d->group, index); + QQuickVisualDataModelCacheItem *cacheItem = it->inCache() + ? model->m_cache.at(it.cacheIndex) + : 0; + + if (!cacheItem) { + cacheItem = new QQuickVisualDataModelCacheItem(model->m_cacheMetaType); + for (int i = 0; i < model->m_groupCount; ++i) + cacheItem->index[i] = it.index[i]; + cacheItem->groups = it->flags & Compositor::GroupMask; + + model->m_cache.insert(it.cacheIndex, cacheItem); + model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); + } + + ++cacheItem->scriptRef; + + v8::Local rv = model->m_cacheMetaType->constructor->NewInstance(); + rv->SetExternalResource(cacheItem); + return QDeclarativeV8Handle::fromHandle(rv); +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::create(int index) + + Returns a reference to the instantiated item at \a index in the group. + + All items returned by create are added to the persistedItems group. Items in this + group remain instantiated when not referenced by any view. +*/ + +QObject *QQuickVisualDataGroup::create(int index) +{ + Q_D(QQuickVisualDataGroup); + if (!d->model) + return 0; + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("create: index out of range"); + return 0; + } + + QObject *object = model->object(d->group, index, true, false); + if (object) + model->addGroups(d->group, index, 1, Compositor::PersistedFlag); + return object; +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count) + + Removes \a count items starting at \a index from the group. +*/ + +void QQuickVisualDataGroup::remove(QDeclarativeV8Function *args) +{ + Q_D(QQuickVisualDataGroup); + if (!d->model) + return; + int index = -1; + int count = 1; + + if (args->Length() == 0) + return; + + int i = 0; + v8::Local v = (*args)[i]; + if (!v->IsInt32()) + return; + index = v->Int32Value(); + + if (++i < args->Length()) { + v = (*args)[i]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); + if (count < 0) { + qmlInfo(this) << tr("remove: invalid count"); + } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("remove: index out of range"); + } else if (count > 0) { + model->removeGroups(d->group, index, count, 1 << d->group); + } +} + +bool QQuickVisualDataGroupPrivate::parseGroupArgs( + QDeclarativeV8Function *args, int *index, int *count, int *groups) const +{ + if (!model) + return false; + + if (args->Length() < 2) + return false; + + int i = 0; + v8::Local v = (*args)[i]; + if (!v->IsInt32()) + return false; + *index = v->Int32Value(); + + v = (*args)[++i]; + if (v->IsInt32()) { + *count = v->Int32Value(); + + if (++i == args->Length()) + return false; + v = (*args)[i]; + } + + *groups = QQuickVisualDataModelPrivate::get(model)->m_cacheMetaType->parseGroups(args->engine(), v); + + return true; +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::addGroups(int index, int count, stringlist groups) + + Adds \a count items starting at \a index to \a groups. +*/ + +void QQuickVisualDataGroup::addGroups(QDeclarativeV8Function *args) +{ + Q_D(QQuickVisualDataGroup); + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &index, &count, &groups)) + return; + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); + if (count < 0) { + qmlInfo(this) << tr("addGroups: invalid count"); + } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("addGroups: index out of range"); + } else if (count > 0 && groups) { + model->addGroups(d->group, index, count, groups); + } +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::removeGroups(int index, int count, stringlist groups) + + Removes \a count items starting at \a index from \a groups. +*/ + +void QQuickVisualDataGroup::removeGroups(QDeclarativeV8Function *args) +{ + Q_D(QQuickVisualDataGroup); + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &index, &count, &groups)) + return; + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); + if (count < 0) { + qmlInfo(this) << tr("removeGroups: invalid count"); + } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("removeGroups: index out of range"); + } else if (count > 0 && groups) { + model->removeGroups(d->group, index, count, groups); + } +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +void QQuickVisualDataGroup::setGroups(QDeclarativeV8Function *args) +{ + Q_D(QQuickVisualDataGroup); + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &index, &count, &groups)) + return; + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); + if (count < 0) { + qmlInfo(this) << tr("setGroups: invalid count"); + } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("setGroups: index out of range"); + } else if (count > 0) { + model->setGroups(d->group, index, count, groups); + } +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +/*! + \qmlmethod QtQuick2::VisualDataGroup::move(int from, int to, int count) + + Moves \a count at \a from in a group \a to a new position. +*/ + +void QQuickVisualDataGroup::move(QDeclarativeV8Function *args) +{ + Q_D(QQuickVisualDataGroup); + + if (args->Length() < 2) + return; + + Compositor::Group fromGroup = d->group; + Compositor::Group toGroup = d->group; + int from = -1; + int to = -1; + int count = 1; + + int i = 0; + v8::Local v = (*args)[i]; + if (QQuickVisualDataGroup *group = qobject_cast(args->engine()->toQObject(v))) { + QQuickVisualDataGroupPrivate *g_d = QQuickVisualDataGroupPrivate::get(group); + if (g_d->model != d->model) + return; + fromGroup = g_d->group; + v = (*args)[++i]; + } + + if (!v->IsInt32()) + return; + from = v->Int32Value(); + + if (++i == args->Length()) + return; + v = (*args)[i]; + + if (QQuickVisualDataGroup *group = qobject_cast(args->engine()->toQObject(v))) { + QQuickVisualDataGroupPrivate *g_d = QQuickVisualDataGroupPrivate::get(group); + if (g_d->model != d->model) + return; + toGroup = g_d->group; + + if (++i == args->Length()) + return; + v = (*args)[i]; + } + + if (!v->IsInt32()) + return; + to = v->Int32Value(); + + if (++i < args->Length()) { + v = (*args)[i]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); + + if (count < 0) { + qmlInfo(this) << tr("move: invalid count"); + } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { + qmlInfo(this) << tr("move: from index out of range"); + } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count)) { + qmlInfo(this) << tr("move: to index out of range"); + } else if (count > 0) { + QVector removes; + QVector inserts; + + model->m_compositor.move(fromGroup, from, toGroup, to, count, &removes, &inserts); + model->itemsMoved(removes, inserts); + model->emitChanges(); + } +} + +/*! + \qmlsignal QtQuick2::VisualDataGroup::onChanged(array removed, array inserted) + + This handler is called when items have been removed from or inserted into the group. + + Each object in the \a removed and \a inserted arrays has two values; the \e index of the first + item inserted or removed and a \e count of the number of consecutive items inserted or removed. + + Each index is adjusted for previous changes with all removed items preceding any inserted + items. +*/ + +//============================================================================ + +QQuickVisualPartsModel::QQuickVisualPartsModel(QQuickVisualDataModel *model, const QString &part, QObject *parent) + : QQuickVisualModel(*new QObjectPrivate, parent) + , m_model(model) + , m_part(part) + , m_compositorGroup(Compositor::Cache) + , m_inheritGroup(true) +{ + QQuickVisualDataModelPrivate *d = QQuickVisualDataModelPrivate::get(m_model); + if (d->m_cacheMetaType) { + QQuickVisualDataGroupPrivate::get(d->m_groups[1])->emitters.insert(this); + m_compositorGroup = Compositor::Default; + } else { + d->m_pendingParts.insert(this); + } +} + +QQuickVisualPartsModel::~QQuickVisualPartsModel() +{ +} + +QString QQuickVisualPartsModel::filterGroup() const +{ + if (m_inheritGroup) + return m_model->filterGroup(); + return m_filterGroup; +} + +void QQuickVisualPartsModel::setFilterGroup(const QString &group) +{ + if (QQuickVisualDataModelPrivate::get(m_model)->m_transaction) { + qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); + return; + } + + if (m_filterGroup != group || m_inheritGroup) { + m_filterGroup = group; + m_inheritGroup = false; + updateFilterGroup(); + + emit filterGroupChanged(); + } +} + +void QQuickVisualPartsModel::resetFilterGroup() +{ + if (!m_inheritGroup) { + m_inheritGroup = true; + updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQuickVisualPartsModel::updateFilterGroup() +{ + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); + if (!model->m_cacheMetaType) + return; + + if (m_inheritGroup) + return; + + QDeclarativeListCompositor::Group previousGroup = model->m_compositorGroup; + m_compositorGroup = Compositor::Default; + QQuickVisualDataGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); + for (int i = 1; i < model->m_groupCount; ++i) { + if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQuickVisualDataGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QDeclarativeChangeSet changeSet; + changeSet.apply(removes, inserts); + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + } +} + +void QQuickVisualPartsModel::updateFilterGroup( + Compositor::Group group, const QDeclarativeChangeSet &changeSet) +{ + if (!m_inheritGroup) + return; + + m_compositorGroup = group; + QQuickVisualDataGroupPrivate::get(QQuickVisualDataModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); + + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + + emit filterGroupChanged(); +} + +int QQuickVisualPartsModel::count() const +{ + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); + return model->m_delegate + ? model->m_compositor.count(m_compositorGroup) + : 0; +} + +bool QQuickVisualPartsModel::isValid() const +{ + return m_model->isValid(); +} + +QQuickItem *QQuickVisualPartsModel::item(int index, bool complete) +{ + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); + + if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { + qWarning() << "VisualDataModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); + return 0; + } + + QObject *object = model->object(m_compositorGroup, index, complete, true); + + if (QDeclarativePackage *package = qobject_cast(object)) { + QObject *part = package->part(m_part); + if (!part) + return 0; + if (QQuickItem *item = qobject_cast(part)) { + m_packaged.insertMulti(item, package); + return item; + } + } + + if (m_model->completePending()) + m_model->completeItem(); + model->release(object); + if (!model->m_delegateValidated) { + if (object) + qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); + model->m_delegateValidated = true; + } + + return 0; +} + +QQuickVisualModel::ReleaseFlags QQuickVisualPartsModel::release(QQuickItem *item) +{ + QQuickVisualModel::ReleaseFlags flags = 0; + + QHash::iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + QDeclarativePackage *package = *it; + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); + flags = model->release(package); + m_packaged.erase(it); + if (!m_packaged.contains(item)) + flags &= ~Referenced; + if (flags & Destroyed) + QQuickVisualDataModelPrivate::get(m_model)->emitDestroyingPackage(package); + } + return flags; +} + +bool QQuickVisualPartsModel::completePending() const +{ + return m_model->completePending(); +} + +void QQuickVisualPartsModel::completeItem() +{ + m_model->completeItem(); +} + +QString QQuickVisualPartsModel::stringValue(int index, const QString &role) +{ + return QQuickVisualDataModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); +} + +void QQuickVisualPartsModel::setWatchedRoles(QList roles) +{ + QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); + model->m_adaptorModel->replaceWatchedRoles(m_watchedRoles, roles); + m_watchedRoles = roles; +} + +int QQuickVisualPartsModel::indexOf(QQuickItem *item, QObject *) const +{ + QHash::const_iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + const QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); + const int cacheIndex = model->cacheIndexOf(*it); + return cacheIndex != -1 + ? model->m_cache.at(cacheIndex)->index[m_compositorGroup] + : -1; + } + return -1; +} + +void QQuickVisualPartsModel::createdPackage(int index, QDeclarativePackage *package) +{ + if (QQuickItem *item = qobject_cast(package->part(m_part))) + emit createdItem(index, item); +} + +void QQuickVisualPartsModel::destroyingPackage(QDeclarativePackage *package) +{ + if (QQuickItem *item = qobject_cast(package->part(m_part))) { + Q_ASSERT(!m_packaged.contains(item)); + emit destroyingItem(item); + } +} + +void QQuickVisualPartsModel::emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) +{ + emit modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit countChanged(); +} + + +QT_END_NAMESPACE + +#include diff --git a/src/declarative/items/qquickvisualdatamodel_p.h b/src/declarative/items/qquickvisualdatamodel_p.h new file mode 100644 index 0000000000..331255199e --- /dev/null +++ b/src/declarative/items/qquickvisualdatamodel_p.h @@ -0,0 +1,242 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKVISUALDATAMODEL_P_H +#define QQUICKVISUALDATAMODEL_P_H + +#include +#include + + +#include +#include + +#include + +QT_BEGIN_HEADER + +Q_DECLARE_METATYPE(QModelIndex) + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QDeclarativeChangeSet; +class QDeclarativeComponent; +class QDeclarativePackage; +class QDeclarativeV8Function; +class QQuickVisualDataGroup; +class QQuickVisualDataModelAttached; +class QQuickVisualDataModelPrivate; + + +class Q_DECLARATIVE_EXPORT QQuickVisualDataModel : public QQuickVisualModel, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickVisualDataModel) + + Q_PROPERTY(QVariant model READ model WRITE setModel) + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate) + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) + Q_PROPERTY(QQuickVisualDataGroup *items READ items CONSTANT) + Q_PROPERTY(QQuickVisualDataGroup *persistedItems READ persistedItems CONSTANT) + Q_PROPERTY(QDeclarativeListProperty groups READ groups CONSTANT) + Q_PROPERTY(QObject *parts READ parts CONSTANT) + Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) + Q_CLASSINFO("DefaultProperty", "delegate") + Q_INTERFACES(QDeclarativeParserStatus) +public: + QQuickVisualDataModel(); + QQuickVisualDataModel(QDeclarativeContext *, QObject *parent=0); + virtual ~QQuickVisualDataModel(); + + void classBegin(); + void componentComplete(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + QVariant rootIndex() const; + void setRootIndex(const QVariant &root); + + Q_INVOKABLE QVariant modelIndex(int idx) const; + Q_INVOKABLE QVariant parentModelIndex() const; + + int count() const; + bool isValid() const { return delegate() != 0; } + QQuickItem *item(int index, bool complete=true); + ReleaseFlags release(QQuickItem *item); + bool completePending() const; + void completeItem(); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList roles); + + int indexOf(QQuickItem *item, QObject *objectContext) const; + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + + QQuickVisualDataGroup *items(); + QQuickVisualDataGroup *persistedItems(); + QDeclarativeListProperty groups(); + QObject *parts(); + + bool event(QEvent *); + + static QQuickVisualDataModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void filterGroupChanged(); + void defaultGroupsChanged(); + void rootIndexChanged(); + +private Q_SLOTS: + void _q_itemsChanged(int index, int count); + void _q_itemsInserted(int index, int count); + void _q_itemsRemoved(int index, int count); + void _q_itemsMoved(int from, int to, int count); + void _q_modelReset(int oldCount, int newCount); +private: + Q_DISABLE_COPY(QQuickVisualDataModel) +}; + +class QQuickVisualDataGroupPrivate; +class Q_AUTOTEST_EXPORT QQuickVisualDataGroup : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) +public: + QQuickVisualDataGroup(QObject *parent = 0); + QQuickVisualDataGroup(const QString &name, QQuickVisualDataModel *model, int compositorType, QObject *parent = 0); + ~QQuickVisualDataGroup(); + + QString name() const; + void setName(const QString &name); + + int count() const; + + bool defaultInclude() const; + void setDefaultInclude(bool include); + + Q_INVOKABLE QDeclarativeV8Handle get(int index); + Q_INVOKABLE QObject *create(int index); + +public Q_SLOTS: + void remove(QDeclarativeV8Function *); + void addGroups(QDeclarativeV8Function *); + void removeGroups(QDeclarativeV8Function *); + void setGroups(QDeclarativeV8Function *); + void move(QDeclarativeV8Function *); + +Q_SIGNALS: + void countChanged(); + void nameChanged(); + void defaultIncludeChanged(); + void changed(const QDeclarativeV8Handle &removed, const QDeclarativeV8Handle &inserted); +private: + Q_DECLARE_PRIVATE(QQuickVisualDataGroup) +}; + +class QQuickVisualDataModelCacheItem; +class QQuickVisualDataModelAttachedMetaObject; +class QQuickVisualDataModelAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuickVisualDataModel *model READ model NOTIFY modelChanged) + Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) +public: + QQuickVisualDataModelAttached(QObject *parent) + : QObject(parent) + , m_cacheItem(0) + , m_previousGroups(0) + , m_modelChanged(false) + {} + ~QQuickVisualDataModelAttached() { attachedProperties.remove(parent()); } + + QQuickVisualDataModel *model() const; + + QStringList groups() const; + void setGroups(const QStringList &groups); + + void emitChanges(); + + static QQuickVisualDataModelAttached *properties(QObject *obj) + { + QQuickVisualDataModelAttached *rv = attachedProperties.value(obj); + if (!rv) { + rv = new QQuickVisualDataModelAttached(obj); + attachedProperties.insert(obj, rv); + } + return rv; + } + +Q_SIGNALS: + void modelChanged(); + void groupsChanged(); + +public: + QQuickVisualDataModelCacheItem *m_cacheItem; + int m_previousGroups; + int m_previousIndex[QDeclarativeListCompositor::MaximumGroupCount]; + bool m_modelChanged; + + static QHash attachedProperties; + + friend class QQuickVisualDataModelAttachedMetaObject; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickVisualDataModel) +QML_DECLARE_TYPEINFO(QQuickVisualDataModel, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQuickVisualDataGroup) + +QT_END_HEADER + +#endif // QQUICKVISUALDATAMODEL_P_H diff --git a/src/declarative/items/qquickvisualitemmodel.cpp b/src/declarative/items/qquickvisualitemmodel.cpp new file mode 100644 index 0000000000..31d06f68c8 --- /dev/null +++ b/src/declarative/items/qquickvisualitemmodel.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickvisualitemmodel_p.h" +#include "qquickitem.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QHash QQuickVisualItemModelAttached::attachedProperties; + + +class QQuickVisualItemModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQuickVisualItemModel) +public: + QQuickVisualItemModelPrivate() : QObjectPrivate() {} + + static void children_append(QDeclarativeListProperty *prop, QQuickItem *item) { + QDeclarative_setParent_noEvent(item, prop->object); + static_cast(prop->data)->children.append(Item(item)); + static_cast(prop->data)->itemAppended(); + static_cast(prop->data)->emitChildrenChanged(); + } + + static int children_count(QDeclarativeListProperty *prop) { + return static_cast(prop->data)->children.count(); + } + + static QQuickItem *children_at(QDeclarativeListProperty *prop, int index) { + return static_cast(prop->data)->children.at(index).item; + } + + void itemAppended() { + Q_Q(QQuickVisualItemModel); + QQuickVisualItemModelAttached *attached = QQuickVisualItemModelAttached::properties(children.last().item); + attached->setIndex(children.count()-1); + QDeclarativeChangeSet changeSet; + changeSet.insert(children.count() - 1, 1); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); + } + + void emitChildrenChanged() { + Q_Q(QQuickVisualItemModel); + emit q->childrenChanged(); + } + + int indexOf(QQuickItem *item) const { + for (int i = 0; i < children.count(); ++i) + if (children.at(i).item == item) + return i; + return -1; + } + + class Item { + public: + Item(QQuickItem *i) : item(i), ref(0) {} + + void addRef() { ++ref; } + bool deref() { return --ref == 0; } + + QQuickItem *item; + int ref; + }; + + QList children; +}; + + +/*! + \qmlclass VisualItemModel QQuickVisualItemModel + \inqmlmodule QtQuick 2 + \ingroup qml-working-with-data + \brief The VisualItemModel allows items to be provided to a view. + + A VisualItemModel contains the visual items to be used in a view. + When a VisualItemModel is used in a view, the view does not require + a delegate since the VisualItemModel already contains the visual + delegate (items). + + An item can determine its index within the + model via the \l{VisualItemModel::index}{index} attached property. + + The example below places three colored rectangles in a ListView. + \code + import QtQuick 1.0 + + Rectangle { + VisualItemModel { + id: itemModel + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + + ListView { + anchors.fill: parent + model: itemModel + } + } + \endcode + + \image visualitemmodel.png + + \sa {declarative/modelviews/visualitemmodel}{VisualItemModel example} +*/ +QQuickVisualItemModel::QQuickVisualItemModel(QObject *parent) + : QQuickVisualModel(*(new QQuickVisualItemModelPrivate), parent) +{ +} + +/*! + \qmlattachedproperty int QtQuick2::VisualItemModel::index + This attached property holds the index of this delegate's item within the model. + + It is attached to each instance of the delegate. +*/ + +QDeclarativeListProperty QQuickVisualItemModel::children() +{ + Q_D(QQuickVisualItemModel); + return QDeclarativeListProperty(this, d, d->children_append, + d->children_count, d->children_at); +} + +/*! + \qmlproperty int QtQuick2::VisualItemModel::count + + The number of items in the model. This property is readonly. +*/ +int QQuickVisualItemModel::count() const +{ + Q_D(const QQuickVisualItemModel); + return d->children.count(); +} + +bool QQuickVisualItemModel::isValid() const +{ + return true; +} + +QQuickItem *QQuickVisualItemModel::item(int index, bool) +{ + Q_D(QQuickVisualItemModel); + QQuickVisualItemModelPrivate::Item &item = d->children[index]; + item.addRef(); + return item.item; +} + +QQuickVisualModel::ReleaseFlags QQuickVisualItemModel::release(QQuickItem *item) +{ + Q_D(QQuickVisualItemModel); + int idx = d->indexOf(item); + if (idx >= 0) { + if (d->children[idx].deref()) { + // XXX todo - the original did item->scene()->removeItem(). Why? + item->setParentItem(0); + QDeclarative_setParent_noEvent(item, this); + } + } + return 0; +} + +bool QQuickVisualItemModel::completePending() const +{ + return false; +} + +void QQuickVisualItemModel::completeItem() +{ + // Nothing to do +} + +QString QQuickVisualItemModel::stringValue(int index, const QString &name) +{ + Q_D(QQuickVisualItemModel); + if (index < 0 || index >= d->children.count()) + return QString(); + return QDeclarativeEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); +} + +int QQuickVisualItemModel::indexOf(QQuickItem *item, QObject *) const +{ + Q_D(const QQuickVisualItemModel); + return d->indexOf(item); +} + +QQuickVisualItemModelAttached *QQuickVisualItemModel::qmlAttachedProperties(QObject *obj) +{ + return QQuickVisualItemModelAttached::properties(obj); +} + +QT_END_NAMESPACE + diff --git a/src/declarative/items/qquickvisualitemmodel_p.h b/src/declarative/items/qquickvisualitemmodel_p.h new file mode 100644 index 0000000000..9fc3d5767c --- /dev/null +++ b/src/declarative/items/qquickvisualitemmodel_p.h @@ -0,0 +1,178 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKVISUALITEMMODEL_P_H +#define QQUICKVISUALITEMMODEL_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QQuickItem; +class QDeclarativeChangeSet; + +class Q_DECLARATIVE_EXPORT QQuickVisualModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + virtual ~QQuickVisualModel() {} + + enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; + Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) + + virtual int count() const = 0; + virtual bool isValid() const = 0; + virtual QQuickItem *item(int index, bool complete=true) = 0; + virtual ReleaseFlags release(QQuickItem *item) = 0; + virtual bool completePending() const = 0; + virtual void completeItem() = 0; + virtual QString stringValue(int, const QString &) = 0; + virtual void setWatchedRoles(QList roles) = 0; + + virtual int indexOf(QQuickItem *item, QObject *objectContext) const = 0; + +Q_SIGNALS: + void countChanged(); + void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); + void createdItem(int index, QQuickItem *item); + void destroyingItem(QQuickItem *item); + +protected: + QQuickVisualModel(QObjectPrivate &dd, QObject *parent = 0) + : QObject(dd, parent) {} + +private: + Q_DISABLE_COPY(QQuickVisualModel) +}; + +class QQuickVisualItemModelAttached; +class QQuickVisualItemModelPrivate; +class Q_DECLARATIVE_EXPORT QQuickVisualItemModel : public QQuickVisualModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickVisualItemModel) + + Q_PROPERTY(QDeclarativeListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "children") + +public: + QQuickVisualItemModel(QObject *parent=0); + virtual ~QQuickVisualItemModel() {} + + virtual int count() const; + virtual bool isValid() const; + virtual QQuickItem *item(int index, bool complete=true); + virtual ReleaseFlags release(QQuickItem *item); + virtual bool completePending() const; + virtual void completeItem(); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList) {} + + virtual int indexOf(QQuickItem *item, QObject *objectContext) const; + + QDeclarativeListProperty children(); + + static QQuickVisualItemModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void childrenChanged(); + +private: + Q_DISABLE_COPY(QQuickVisualItemModel) +}; + +class QQuickVisualItemModelAttached : public QObject +{ + Q_OBJECT + +public: + QQuickVisualItemModelAttached(QObject *parent) + : QObject(parent), m_index(0) {} + ~QQuickVisualItemModelAttached() { + attachedProperties.remove(parent()); + } + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + int index() const { return m_index; } + void setIndex(int idx) { + if (m_index != idx) { + m_index = idx; + emit indexChanged(); + } + } + + static QQuickVisualItemModelAttached *properties(QObject *obj) { + QQuickVisualItemModelAttached *rv = attachedProperties.value(obj); + if (!rv) { + rv = new QQuickVisualItemModelAttached(obj); + attachedProperties.insert(obj, rv); + } + return rv; + } + +Q_SIGNALS: + void indexChanged(); + +public: + int m_index; + + static QHash attachedProperties; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickVisualModel) +QML_DECLARE_TYPE(QQuickVisualItemModel) +QML_DECLARE_TYPEINFO(QQuickVisualItemModel, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QQUICKVISUALITEMMODEL_P_H diff --git a/src/declarative/items/qsganchors.cpp b/src/declarative/items/qsganchors.cpp deleted file mode 100644 index 67ca0317c4..0000000000 --- a/src/declarative/items/qsganchors.cpp +++ /dev/null @@ -1,1110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsganchors_p_p.h" - -#include "qsgitem.h" -#include "qsgitem_p.h" - -#include - -QT_BEGIN_NAMESPACE - -//TODO: should we cache relationships, so we don't have to check each time (parent-child or sibling)? -//TODO: support non-parent, non-sibling (need to find lowest common ancestor) - -static qreal hcenter(QSGItem *item) -{ - qreal width = item->width(); - int iw = width; - if (iw % 2) - return (width + 1) / 2; - else - return width / 2; -} - -static qreal vcenter(QSGItem *item) -{ - qreal height = item->height(); - int ih = height; - if (ih % 2) - return (height + 1) / 2; - else - return height / 2; -} - -//### const item? -//local position -static qreal position(QSGItem *item, QSGAnchorLine::AnchorLine anchorLine) -{ - qreal ret = 0.0; - switch (anchorLine) { - case QSGAnchorLine::Left: - ret = item->x(); - break; - case QSGAnchorLine::Right: - ret = item->x() + item->width(); - break; - case QSGAnchorLine::Top: - ret = item->y(); - break; - case QSGAnchorLine::Bottom: - ret = item->y() + item->height(); - break; - case QSGAnchorLine::HCenter: - ret = item->x() + hcenter(item); - break; - case QSGAnchorLine::VCenter: - ret = item->y() + vcenter(item); - break; - case QSGAnchorLine::Baseline: - ret = item->y() + item->baselineOffset(); - break; - default: - break; - } - - return ret; -} - -//position when origin is 0,0 -static qreal adjustedPosition(QSGItem *item, QSGAnchorLine::AnchorLine anchorLine) -{ - qreal ret = 0.0; - switch (anchorLine) { - case QSGAnchorLine::Left: - ret = 0.0; - break; - case QSGAnchorLine::Right: - ret = item->width(); - break; - case QSGAnchorLine::Top: - ret = 0.0; - break; - case QSGAnchorLine::Bottom: - ret = item->height(); - break; - case QSGAnchorLine::HCenter: - ret = hcenter(item); - break; - case QSGAnchorLine::VCenter: - ret = vcenter(item); - break; - case QSGAnchorLine::Baseline: - ret = item->baselineOffset(); - break; - default: - break; - } - - return ret; -} - -QSGAnchors::QSGAnchors(QSGItem *item, QObject *parent) -: QObject(*new QSGAnchorsPrivate(item), parent) -{ -} - -QSGAnchors::~QSGAnchors() -{ - Q_D(QSGAnchors); - d->remDepend(d->fill); - d->remDepend(d->centerIn); - d->remDepend(d->left.item); - d->remDepend(d->right.item); - d->remDepend(d->top.item); - d->remDepend(d->bottom.item); - d->remDepend(d->vCenter.item); - d->remDepend(d->hCenter.item); - d->remDepend(d->baseline.item); -} - -void QSGAnchorsPrivate::fillChanged() -{ - Q_Q(QSGAnchors); - if (!fill || !isItemComplete()) - return; - - if (updatingFill < 2) { - ++updatingFill; - - qreal horizontalMargin = q->mirrored() ? rightMargin : leftMargin; - - if (fill == item->parentItem()) { //child-parent - setItemPos(QPointF(horizontalMargin, topMargin)); - } else if (fill->parentItem() == item->parentItem()) { //siblings - setItemPos(QPointF(fill->x()+horizontalMargin, fill->y()+topMargin)); - } - setItemSize(QSizeF(fill->width()-leftMargin-rightMargin, fill->height()-topMargin-bottomMargin)); - - --updatingFill; - } else { - // ### Make this certain :) - qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on fill."); - } - -} - -void QSGAnchorsPrivate::centerInChanged() -{ - Q_Q(QSGAnchors); - if (!centerIn || fill || !isItemComplete()) - return; - - if (updatingCenterIn < 2) { - ++updatingCenterIn; - - qreal effectiveHCenterOffset = q->mirrored() ? -hCenterOffset : hCenterOffset; - if (centerIn == item->parentItem()) { - QPointF p(hcenter(item->parentItem()) - hcenter(item) + effectiveHCenterOffset, - vcenter(item->parentItem()) - vcenter(item) + vCenterOffset); - setItemPos(p); - - } else if (centerIn->parentItem() == item->parentItem()) { - QPointF p(centerIn->x() + hcenter(centerIn) - hcenter(item) + effectiveHCenterOffset, - centerIn->y() + vcenter(centerIn) - vcenter(item) + vCenterOffset); - setItemPos(p); - } - - --updatingCenterIn; - } else { - // ### Make this certain :) - qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on centerIn."); - } -} - -void QSGAnchorsPrivate::clearItem(QSGItem *item) -{ - if (!item) - return; - if (fill == item) - fill = 0; - if (centerIn == item) - centerIn = 0; - if (left.item == item) { - left.item = 0; - usedAnchors &= ~QSGAnchors::LeftAnchor; - } - if (right.item == item) { - right.item = 0; - usedAnchors &= ~QSGAnchors::RightAnchor; - } - if (top.item == item) { - top.item = 0; - usedAnchors &= ~QSGAnchors::TopAnchor; - } - if (bottom.item == item) { - bottom.item = 0; - usedAnchors &= ~QSGAnchors::BottomAnchor; - } - if (vCenter.item == item) { - vCenter.item = 0; - usedAnchors &= ~QSGAnchors::VCenterAnchor; - } - if (hCenter.item == item) { - hCenter.item = 0; - usedAnchors &= ~QSGAnchors::HCenterAnchor; - } - if (baseline.item == item) { - baseline.item = 0; - usedAnchors &= ~QSGAnchors::BaselineAnchor; - } -} - -void QSGAnchorsPrivate::addDepend(QSGItem *item) -{ - if (!item) - return; - - QSGItemPrivate *p = QSGItemPrivate::get(item); - p->addItemChangeListener(this, QSGItemPrivate::Geometry); -} - -void QSGAnchorsPrivate::remDepend(QSGItem *item) -{ - if (!item) - return; - - QSGItemPrivate *p = QSGItemPrivate::get(item); - p->removeItemChangeListener(this, QSGItemPrivate::Geometry); -} - -bool QSGAnchors::mirrored() -{ - Q_D(QSGAnchors); - return QSGItemPrivate::get(d->item)->effectiveLayoutMirror; -} - -bool QSGAnchorsPrivate::isItemComplete() const -{ - return componentComplete; -} - -void QSGAnchors::classBegin() -{ - Q_D(QSGAnchors); - d->componentComplete = false; -} - -void QSGAnchors::componentComplete() -{ - Q_D(QSGAnchors); - d->componentComplete = true; -} - -void QSGAnchorsPrivate::setItemHeight(qreal v) -{ - updatingMe = true; - item->setHeight(v); - updatingMe = false; -} - -void QSGAnchorsPrivate::setItemWidth(qreal v) -{ - updatingMe = true; - item->setWidth(v); - updatingMe = false; -} - -void QSGAnchorsPrivate::setItemX(qreal v) -{ - updatingMe = true; - item->setX(v); - updatingMe = false; -} - -void QSGAnchorsPrivate::setItemY(qreal v) -{ - updatingMe = true; - item->setY(v); - updatingMe = false; -} - -void QSGAnchorsPrivate::setItemPos(const QPointF &v) -{ - updatingMe = true; - item->setPos(v); - updatingMe = false; -} - -void QSGAnchorsPrivate::setItemSize(const QSizeF &v) -{ - updatingMe = true; - item->setSize(v); - updatingMe = false; -} - -void QSGAnchorsPrivate::updateMe() -{ - if (updatingMe) { - updatingMe = false; - return; - } - - fillChanged(); - centerInChanged(); - updateHorizontalAnchors(); - updateVerticalAnchors(); -} - -void QSGAnchorsPrivate::updateOnComplete() -{ - fillChanged(); - centerInChanged(); - updateHorizontalAnchors(); - updateVerticalAnchors(); -} - -void QSGAnchorsPrivate::itemGeometryChanged(QSGItem *, const QRectF &newG, const QRectF &oldG) -{ - fillChanged(); - centerInChanged(); - if (newG.x() != oldG.x() || newG.width() != oldG.width()) - updateHorizontalAnchors(); - if (newG.y() != oldG.y() || newG.height() != oldG.height()) - updateVerticalAnchors(); -} - -QSGItem *QSGAnchors::fill() const -{ - Q_D(const QSGAnchors); - return d->fill; -} - -void QSGAnchors::setFill(QSGItem *f) -{ - Q_D(QSGAnchors); - if (d->fill == f) - return; - - if (!f) { - d->remDepend(d->fill); - d->fill = f; - emit fillChanged(); - return; - } - if (f != d->item->parentItem() && f->parentItem() != d->item->parentItem()){ - qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); - return; - } - d->remDepend(d->fill); - d->fill = f; - d->addDepend(d->fill); - emit fillChanged(); - d->fillChanged(); -} - -void QSGAnchors::resetFill() -{ - setFill(0); -} - -QSGItem *QSGAnchors::centerIn() const -{ - Q_D(const QSGAnchors); - return d->centerIn; -} - -void QSGAnchors::setCenterIn(QSGItem* c) -{ - Q_D(QSGAnchors); - if (d->centerIn == c) - return; - - if (!c) { - d->remDepend(d->centerIn); - d->centerIn = c; - emit centerInChanged(); - return; - } - if (c != d->item->parentItem() && c->parentItem() != d->item->parentItem()){ - qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); - return; - } - - d->remDepend(d->centerIn); - d->centerIn = c; - d->addDepend(d->centerIn); - emit centerInChanged(); - d->centerInChanged(); -} - -void QSGAnchors::resetCenterIn() -{ - setCenterIn(0); -} - -bool QSGAnchorsPrivate::calcStretch(const QSGAnchorLine &edge1, - const QSGAnchorLine &edge2, - qreal offset1, - qreal offset2, - QSGAnchorLine::AnchorLine line, - qreal &stretch) -{ - bool edge1IsParent = (edge1.item == item->parentItem()); - bool edge2IsParent = (edge2.item == item->parentItem()); - bool edge1IsSibling = (edge1.item->parentItem() == item->parentItem()); - bool edge2IsSibling = (edge2.item->parentItem() == item->parentItem()); - - bool invalid = false; - if ((edge2IsParent && edge1IsParent) || (edge2IsSibling && edge1IsSibling)) { - stretch = (position(edge2.item, edge2.anchorLine) + offset2) - - (position(edge1.item, edge1.anchorLine) + offset1); - } else if (edge2IsParent && edge1IsSibling) { - stretch = (position(edge2.item, edge2.anchorLine) + offset2) - - (position(item->parentItem(), line) - + position(edge1.item, edge1.anchorLine) + offset1); - } else if (edge2IsSibling && edge1IsParent) { - stretch = (position(item->parentItem(), line) + position(edge2.item, edge2.anchorLine) + offset2) - - (position(edge1.item, edge1.anchorLine) + offset1); - } else - invalid = true; - - return invalid; -} - -void QSGAnchorsPrivate::updateVerticalAnchors() -{ - if (fill || centerIn || !isItemComplete()) - return; - - if (updatingVerticalAnchor < 2) { - ++updatingVerticalAnchor; - if (usedAnchors & QSGAnchors::TopAnchor) { - //Handle stretching - bool invalid = true; - qreal height = 0.0; - if (usedAnchors & QSGAnchors::BottomAnchor) { - invalid = calcStretch(top, bottom, topMargin, -bottomMargin, QSGAnchorLine::Top, height); - } else if (usedAnchors & QSGAnchors::VCenterAnchor) { - invalid = calcStretch(top, vCenter, topMargin, vCenterOffset, QSGAnchorLine::Top, height); - height *= 2; - } - if (!invalid) - setItemHeight(height); - - //Handle top - if (top.item == item->parentItem()) { - setItemY(adjustedPosition(top.item, top.anchorLine) + topMargin); - } else if (top.item->parentItem() == item->parentItem()) { - setItemY(position(top.item, top.anchorLine) + topMargin); - } - } else if (usedAnchors & QSGAnchors::BottomAnchor) { - //Handle stretching (top + bottom case is handled above) - if (usedAnchors & QSGAnchors::VCenterAnchor) { - qreal height = 0.0; - bool invalid = calcStretch(vCenter, bottom, vCenterOffset, -bottomMargin, - QSGAnchorLine::Top, height); - if (!invalid) - setItemHeight(height*2); - } - - //Handle bottom - if (bottom.item == item->parentItem()) { - setItemY(adjustedPosition(bottom.item, bottom.anchorLine) - item->height() - bottomMargin); - } else if (bottom.item->parentItem() == item->parentItem()) { - setItemY(position(bottom.item, bottom.anchorLine) - item->height() - bottomMargin); - } - } else if (usedAnchors & QSGAnchors::VCenterAnchor) { - //(stetching handled above) - - //Handle vCenter - if (vCenter.item == item->parentItem()) { - setItemY(adjustedPosition(vCenter.item, vCenter.anchorLine) - - vcenter(item) + vCenterOffset); - } else if (vCenter.item->parentItem() == item->parentItem()) { - setItemY(position(vCenter.item, vCenter.anchorLine) - vcenter(item) + vCenterOffset); - } - } else if (usedAnchors & QSGAnchors::BaselineAnchor) { - //Handle baseline - if (baseline.item == item->parentItem()) { - setItemY(adjustedPosition(baseline.item, baseline.anchorLine) - item->baselineOffset() + baselineOffset); - } else if (baseline.item->parentItem() == item->parentItem()) { - setItemY(position(baseline.item, baseline.anchorLine) - item->baselineOffset() + baselineOffset); - } - } - --updatingVerticalAnchor; - } else { - // ### Make this certain :) - qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on vertical anchor."); - } -} - -inline QSGAnchorLine::AnchorLine reverseAnchorLine(QSGAnchorLine::AnchorLine anchorLine) -{ - if (anchorLine == QSGAnchorLine::Left) { - return QSGAnchorLine::Right; - } else if (anchorLine == QSGAnchorLine::Right) { - return QSGAnchorLine::Left; - } else { - return anchorLine; - } -} - -void QSGAnchorsPrivate::updateHorizontalAnchors() -{ - Q_Q(QSGAnchors); - if (fill || centerIn || !isItemComplete()) - return; - - if (updatingHorizontalAnchor < 3) { - ++updatingHorizontalAnchor; - qreal effectiveRightMargin, effectiveLeftMargin, effectiveHorizontalCenterOffset; - QSGAnchorLine effectiveLeft, effectiveRight, effectiveHorizontalCenter; - QSGAnchors::Anchor effectiveLeftAnchor, effectiveRightAnchor; - if (q->mirrored()) { - effectiveLeftAnchor = QSGAnchors::RightAnchor; - effectiveRightAnchor = QSGAnchors::LeftAnchor; - effectiveLeft.item = right.item; - effectiveLeft.anchorLine = reverseAnchorLine(right.anchorLine); - effectiveRight.item = left.item; - effectiveRight.anchorLine = reverseAnchorLine(left.anchorLine); - effectiveHorizontalCenter.item = hCenter.item; - effectiveHorizontalCenter.anchorLine = reverseAnchorLine(hCenter.anchorLine); - effectiveLeftMargin = rightMargin; - effectiveRightMargin = leftMargin; - effectiveHorizontalCenterOffset = -hCenterOffset; - } else { - effectiveLeftAnchor = QSGAnchors::LeftAnchor; - effectiveRightAnchor = QSGAnchors::RightAnchor; - effectiveLeft = left; - effectiveRight = right; - effectiveHorizontalCenter = hCenter; - effectiveLeftMargin = leftMargin; - effectiveRightMargin = rightMargin; - effectiveHorizontalCenterOffset = hCenterOffset; - } - - if (usedAnchors & effectiveLeftAnchor) { - //Handle stretching - bool invalid = true; - qreal width = 0.0; - if (usedAnchors & effectiveRightAnchor) { - invalid = calcStretch(effectiveLeft, effectiveRight, effectiveLeftMargin, -effectiveRightMargin, QSGAnchorLine::Left, width); - } else if (usedAnchors & QSGAnchors::HCenterAnchor) { - invalid = calcStretch(effectiveLeft, effectiveHorizontalCenter, effectiveLeftMargin, effectiveHorizontalCenterOffset, QSGAnchorLine::Left, width); - width *= 2; - } - if (!invalid) - setItemWidth(width); - - //Handle left - if (effectiveLeft.item == item->parentItem()) { - setItemX(adjustedPosition(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin); - } else if (effectiveLeft.item->parentItem() == item->parentItem()) { - setItemX(position(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin); - } - } else if (usedAnchors & effectiveRightAnchor) { - //Handle stretching (left + right case is handled in updateLeftAnchor) - if (usedAnchors & QSGAnchors::HCenterAnchor) { - qreal width = 0.0; - bool invalid = calcStretch(effectiveHorizontalCenter, effectiveRight, effectiveHorizontalCenterOffset, -effectiveRightMargin, - QSGAnchorLine::Left, width); - if (!invalid) - setItemWidth(width*2); - } - - //Handle right - if (effectiveRight.item == item->parentItem()) { - setItemX(adjustedPosition(effectiveRight.item, effectiveRight.anchorLine) - item->width() - effectiveRightMargin); - } else if (effectiveRight.item->parentItem() == item->parentItem()) { - setItemX(position(effectiveRight.item, effectiveRight.anchorLine) - item->width() - effectiveRightMargin); - } - } else if (usedAnchors & QSGAnchors::HCenterAnchor) { - //Handle hCenter - if (effectiveHorizontalCenter.item == item->parentItem()) { - setItemX(adjustedPosition(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset); - } else if (effectiveHorizontalCenter.item->parentItem() == item->parentItem()) { - setItemX(position(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset); - } - } - --updatingHorizontalAnchor; - } else { - // ### Make this certain :) - qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on horizontal anchor."); - } -} - -QSGAnchorLine QSGAnchors::top() const -{ - Q_D(const QSGAnchors); - return d->top; -} - -void QSGAnchors::setTop(const QSGAnchorLine &edge) -{ - Q_D(QSGAnchors); - if (!d->checkVAnchorValid(edge) || d->top == edge) - return; - - d->usedAnchors |= TopAnchor; - - if (!d->checkVValid()) { - d->usedAnchors &= ~TopAnchor; - return; - } - - d->remDepend(d->top.item); - d->top = edge; - d->addDepend(d->top.item); - emit topChanged(); - d->updateVerticalAnchors(); -} - -void QSGAnchors::resetTop() -{ - Q_D(QSGAnchors); - d->usedAnchors &= ~TopAnchor; - d->remDepend(d->top.item); - d->top = QSGAnchorLine(); - emit topChanged(); - d->updateVerticalAnchors(); -} - -QSGAnchorLine QSGAnchors::bottom() const -{ - Q_D(const QSGAnchors); - return d->bottom; -} - -void QSGAnchors::setBottom(const QSGAnchorLine &edge) -{ - Q_D(QSGAnchors); - if (!d->checkVAnchorValid(edge) || d->bottom == edge) - return; - - d->usedAnchors |= BottomAnchor; - - if (!d->checkVValid()) { - d->usedAnchors &= ~BottomAnchor; - return; - } - - d->remDepend(d->bottom.item); - d->bottom = edge; - d->addDepend(d->bottom.item); - emit bottomChanged(); - d->updateVerticalAnchors(); -} - -void QSGAnchors::resetBottom() -{ - Q_D(QSGAnchors); - d->usedAnchors &= ~BottomAnchor; - d->remDepend(d->bottom.item); - d->bottom = QSGAnchorLine(); - emit bottomChanged(); - d->updateVerticalAnchors(); -} - -QSGAnchorLine QSGAnchors::verticalCenter() const -{ - Q_D(const QSGAnchors); - return d->vCenter; -} - -void QSGAnchors::setVerticalCenter(const QSGAnchorLine &edge) -{ - Q_D(QSGAnchors); - if (!d->checkVAnchorValid(edge) || d->vCenter == edge) - return; - - d->usedAnchors |= VCenterAnchor; - - if (!d->checkVValid()) { - d->usedAnchors &= ~VCenterAnchor; - return; - } - - d->remDepend(d->vCenter.item); - d->vCenter = edge; - d->addDepend(d->vCenter.item); - emit verticalCenterChanged(); - d->updateVerticalAnchors(); -} - -void QSGAnchors::resetVerticalCenter() -{ - Q_D(QSGAnchors); - d->usedAnchors &= ~VCenterAnchor; - d->remDepend(d->vCenter.item); - d->vCenter = QSGAnchorLine(); - emit verticalCenterChanged(); - d->updateVerticalAnchors(); -} - -QSGAnchorLine QSGAnchors::baseline() const -{ - Q_D(const QSGAnchors); - return d->baseline; -} - -void QSGAnchors::setBaseline(const QSGAnchorLine &edge) -{ - Q_D(QSGAnchors); - if (!d->checkVAnchorValid(edge) || d->baseline == edge) - return; - - d->usedAnchors |= BaselineAnchor; - - if (!d->checkVValid()) { - d->usedAnchors &= ~BaselineAnchor; - return; - } - - d->remDepend(d->baseline.item); - d->baseline = edge; - d->addDepend(d->baseline.item); - emit baselineChanged(); - d->updateVerticalAnchors(); -} - -void QSGAnchors::resetBaseline() -{ - Q_D(QSGAnchors); - d->usedAnchors &= ~BaselineAnchor; - d->remDepend(d->baseline.item); - d->baseline = QSGAnchorLine(); - emit baselineChanged(); - d->updateVerticalAnchors(); -} - -QSGAnchorLine QSGAnchors::left() const -{ - Q_D(const QSGAnchors); - return d->left; -} - -void QSGAnchors::setLeft(const QSGAnchorLine &edge) -{ - Q_D(QSGAnchors); - if (!d->checkHAnchorValid(edge) || d->left == edge) - return; - - d->usedAnchors |= LeftAnchor; - - if (!d->checkHValid()) { - d->usedAnchors &= ~LeftAnchor; - return; - } - - d->remDepend(d->left.item); - d->left = edge; - d->addDepend(d->left.item); - emit leftChanged(); - d->updateHorizontalAnchors(); -} - -void QSGAnchors::resetLeft() -{ - Q_D(QSGAnchors); - d->usedAnchors &= ~LeftAnchor; - d->remDepend(d->left.item); - d->left = QSGAnchorLine(); - emit leftChanged(); - d->updateHorizontalAnchors(); -} - -QSGAnchorLine QSGAnchors::right() const -{ - Q_D(const QSGAnchors); - return d->right; -} - -void QSGAnchors::setRight(const QSGAnchorLine &edge) -{ - Q_D(QSGAnchors); - if (!d->checkHAnchorValid(edge) || d->right == edge) - return; - - d->usedAnchors |= RightAnchor; - - if (!d->checkHValid()) { - d->usedAnchors &= ~RightAnchor; - return; - } - - d->remDepend(d->right.item); - d->right = edge; - d->addDepend(d->right.item); - emit rightChanged(); - d->updateHorizontalAnchors(); -} - -void QSGAnchors::resetRight() -{ - Q_D(QSGAnchors); - d->usedAnchors &= ~RightAnchor; - d->remDepend(d->right.item); - d->right = QSGAnchorLine(); - emit rightChanged(); - d->updateHorizontalAnchors(); -} - -QSGAnchorLine QSGAnchors::horizontalCenter() const -{ - Q_D(const QSGAnchors); - return d->hCenter; -} - -void QSGAnchors::setHorizontalCenter(const QSGAnchorLine &edge) -{ - Q_D(QSGAnchors); - if (!d->checkHAnchorValid(edge) || d->hCenter == edge) - return; - - d->usedAnchors |= HCenterAnchor; - - if (!d->checkHValid()) { - d->usedAnchors &= ~HCenterAnchor; - return; - } - - d->remDepend(d->hCenter.item); - d->hCenter = edge; - d->addDepend(d->hCenter.item); - emit horizontalCenterChanged(); - d->updateHorizontalAnchors(); -} - -void QSGAnchors::resetHorizontalCenter() -{ - Q_D(QSGAnchors); - d->usedAnchors &= ~HCenterAnchor; - d->remDepend(d->hCenter.item); - d->hCenter = QSGAnchorLine(); - emit horizontalCenterChanged(); - d->updateHorizontalAnchors(); -} - -qreal QSGAnchors::leftMargin() const -{ - Q_D(const QSGAnchors); - return d->leftMargin; -} - -void QSGAnchors::setLeftMargin(qreal offset) -{ - Q_D(QSGAnchors); - if (d->leftMargin == offset) - return; - d->leftMargin = offset; - if (d->fill) - d->fillChanged(); - else - d->updateHorizontalAnchors(); - emit leftMarginChanged(); -} - -qreal QSGAnchors::rightMargin() const -{ - Q_D(const QSGAnchors); - return d->rightMargin; -} - -void QSGAnchors::setRightMargin(qreal offset) -{ - Q_D(QSGAnchors); - if (d->rightMargin == offset) - return; - d->rightMargin = offset; - if (d->fill) - d->fillChanged(); - else - d->updateHorizontalAnchors(); - emit rightMarginChanged(); -} - -qreal QSGAnchors::margins() const -{ - Q_D(const QSGAnchors); - return d->margins; -} - -void QSGAnchors::setMargins(qreal offset) -{ - Q_D(QSGAnchors); - if (d->margins == offset) - return; - //###Is it significantly faster to set them directly so we can call fillChanged only once? - if (!d->rightMargin || d->rightMargin == d->margins) - setRightMargin(offset); - if (!d->leftMargin || d->leftMargin == d->margins) - setLeftMargin(offset); - if (!d->topMargin || d->topMargin == d->margins) - setTopMargin(offset); - if (!d->bottomMargin || d->bottomMargin == d->margins) - setBottomMargin(offset); - d->margins = offset; - emit marginsChanged(); - -} - -qreal QSGAnchors::horizontalCenterOffset() const -{ - Q_D(const QSGAnchors); - return d->hCenterOffset; -} - -void QSGAnchors::setHorizontalCenterOffset(qreal offset) -{ - Q_D(QSGAnchors); - if (d->hCenterOffset == offset) - return; - d->hCenterOffset = offset; - if (d->centerIn) - d->centerInChanged(); - else - d->updateHorizontalAnchors(); - emit horizontalCenterOffsetChanged(); -} - -qreal QSGAnchors::topMargin() const -{ - Q_D(const QSGAnchors); - return d->topMargin; -} - -void QSGAnchors::setTopMargin(qreal offset) -{ - Q_D(QSGAnchors); - if (d->topMargin == offset) - return; - d->topMargin = offset; - if (d->fill) - d->fillChanged(); - else - d->updateVerticalAnchors(); - emit topMarginChanged(); -} - -qreal QSGAnchors::bottomMargin() const -{ - Q_D(const QSGAnchors); - return d->bottomMargin; -} - -void QSGAnchors::setBottomMargin(qreal offset) -{ - Q_D(QSGAnchors); - if (d->bottomMargin == offset) - return; - d->bottomMargin = offset; - if (d->fill) - d->fillChanged(); - else - d->updateVerticalAnchors(); - emit bottomMarginChanged(); -} - -qreal QSGAnchors::verticalCenterOffset() const -{ - Q_D(const QSGAnchors); - return d->vCenterOffset; -} - -void QSGAnchors::setVerticalCenterOffset(qreal offset) -{ - Q_D(QSGAnchors); - if (d->vCenterOffset == offset) - return; - d->vCenterOffset = offset; - if (d->centerIn) - d->centerInChanged(); - else - d->updateVerticalAnchors(); - emit verticalCenterOffsetChanged(); -} - -qreal QSGAnchors::baselineOffset() const -{ - Q_D(const QSGAnchors); - return d->baselineOffset; -} - -void QSGAnchors::setBaselineOffset(qreal offset) -{ - Q_D(QSGAnchors); - if (d->baselineOffset == offset) - return; - d->baselineOffset = offset; - d->updateVerticalAnchors(); - emit baselineOffsetChanged(); -} - -QSGAnchors::Anchors QSGAnchors::usedAnchors() const -{ - Q_D(const QSGAnchors); - return d->usedAnchors; -} - -bool QSGAnchorsPrivate::checkHValid() const -{ - if (usedAnchors & QSGAnchors::LeftAnchor && - usedAnchors & QSGAnchors::RightAnchor && - usedAnchors & QSGAnchors::HCenterAnchor) { - qmlInfo(item) << QSGAnchors::tr("Cannot specify left, right, and hcenter anchors."); - return false; - } - - return true; -} - -bool QSGAnchorsPrivate::checkHAnchorValid(QSGAnchorLine anchor) const -{ - if (!anchor.item) { - qmlInfo(item) << QSGAnchors::tr("Cannot anchor to a null item."); - return false; - } else if (anchor.anchorLine & QSGAnchorLine::Vertical_Mask) { - qmlInfo(item) << QSGAnchors::tr("Cannot anchor a horizontal edge to a vertical edge."); - return false; - } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){ - qmlInfo(item) << QSGAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); - return false; - } else if (anchor.item == item) { - qmlInfo(item) << QSGAnchors::tr("Cannot anchor item to self."); - return false; - } - - return true; -} - -bool QSGAnchorsPrivate::checkVValid() const -{ - if (usedAnchors & QSGAnchors::TopAnchor && - usedAnchors & QSGAnchors::BottomAnchor && - usedAnchors & QSGAnchors::VCenterAnchor) { - qmlInfo(item) << QSGAnchors::tr("Cannot specify top, bottom, and vcenter anchors."); - return false; - } else if (usedAnchors & QSGAnchors::BaselineAnchor && - (usedAnchors & QSGAnchors::TopAnchor || - usedAnchors & QSGAnchors::BottomAnchor || - usedAnchors & QSGAnchors::VCenterAnchor)) { - qmlInfo(item) << QSGAnchors::tr("Baseline anchor cannot be used in conjunction with top, bottom, or vcenter anchors."); - return false; - } - - return true; -} - -bool QSGAnchorsPrivate::checkVAnchorValid(QSGAnchorLine anchor) const -{ - if (!anchor.item) { - qmlInfo(item) << QSGAnchors::tr("Cannot anchor to a null item."); - return false; - } else if (anchor.anchorLine & QSGAnchorLine::Horizontal_Mask) { - qmlInfo(item) << QSGAnchors::tr("Cannot anchor a vertical edge to a horizontal edge."); - return false; - } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){ - qmlInfo(item) << QSGAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); - return false; - } else if (anchor.item == item){ - qmlInfo(item) << QSGAnchors::tr("Cannot anchor item to self."); - return false; - } - - return true; -} - -QT_END_NAMESPACE - -#include - diff --git a/src/declarative/items/qsganchors_p.h b/src/declarative/items/qsganchors_p.h deleted file mode 100644 index a80c279d90..0000000000 --- a/src/declarative/items/qsganchors_p.h +++ /dev/null @@ -1,201 +0,0 @@ -// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGANCHORS_P_H -#define QSGANCHORS_P_H - -#include - -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGItem; -class QSGAnchorsPrivate; -class QSGAnchorLine; -class Q_DECLARATIVE_PRIVATE_EXPORT QSGAnchors : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QSGAnchorLine left READ left WRITE setLeft RESET resetLeft NOTIFY leftChanged) - Q_PROPERTY(QSGAnchorLine right READ right WRITE setRight RESET resetRight NOTIFY rightChanged) - Q_PROPERTY(QSGAnchorLine horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter NOTIFY horizontalCenterChanged) - Q_PROPERTY(QSGAnchorLine top READ top WRITE setTop RESET resetTop NOTIFY topChanged) - Q_PROPERTY(QSGAnchorLine bottom READ bottom WRITE setBottom RESET resetBottom NOTIFY bottomChanged) - Q_PROPERTY(QSGAnchorLine verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter NOTIFY verticalCenterChanged) - Q_PROPERTY(QSGAnchorLine baseline READ baseline WRITE setBaseline RESET resetBaseline NOTIFY baselineChanged) - Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) - Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) - Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) - Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged) - Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) - Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) - Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged) - Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged) - Q_PROPERTY(QSGItem *fill READ fill WRITE setFill RESET resetFill NOTIFY fillChanged) - Q_PROPERTY(QSGItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged) - Q_PROPERTY(bool mirrored READ mirrored NOTIFY mirroredChanged) - -public: - QSGAnchors(QSGItem *item, QObject *parent=0); - virtual ~QSGAnchors(); - - enum Anchor { - LeftAnchor = 0x01, - RightAnchor = 0x02, - TopAnchor = 0x04, - BottomAnchor = 0x08, - HCenterAnchor = 0x10, - VCenterAnchor = 0x20, - BaselineAnchor = 0x40, - Horizontal_Mask = LeftAnchor | RightAnchor | HCenterAnchor, - Vertical_Mask = TopAnchor | BottomAnchor | VCenterAnchor | BaselineAnchor - }; - Q_DECLARE_FLAGS(Anchors, Anchor) - - QSGAnchorLine left() const; - void setLeft(const QSGAnchorLine &edge); - void resetLeft(); - - QSGAnchorLine right() const; - void setRight(const QSGAnchorLine &edge); - void resetRight(); - - QSGAnchorLine horizontalCenter() const; - void setHorizontalCenter(const QSGAnchorLine &edge); - void resetHorizontalCenter(); - - QSGAnchorLine top() const; - void setTop(const QSGAnchorLine &edge); - void resetTop(); - - QSGAnchorLine bottom() const; - void setBottom(const QSGAnchorLine &edge); - void resetBottom(); - - QSGAnchorLine verticalCenter() const; - void setVerticalCenter(const QSGAnchorLine &edge); - void resetVerticalCenter(); - - QSGAnchorLine baseline() const; - void setBaseline(const QSGAnchorLine &edge); - void resetBaseline(); - - qreal leftMargin() const; - void setLeftMargin(qreal); - - qreal rightMargin() const; - void setRightMargin(qreal); - - qreal horizontalCenterOffset() const; - void setHorizontalCenterOffset(qreal); - - qreal topMargin() const; - void setTopMargin(qreal); - - qreal bottomMargin() const; - void setBottomMargin(qreal); - - qreal margins() const; - void setMargins(qreal); - - qreal verticalCenterOffset() const; - void setVerticalCenterOffset(qreal); - - qreal baselineOffset() const; - void setBaselineOffset(qreal); - - QSGItem *fill() const; - void setFill(QSGItem *); - void resetFill(); - - QSGItem *centerIn() const; - void setCenterIn(QSGItem *); - void resetCenterIn(); - - Anchors usedAnchors() const; - - bool mirrored(); - - void classBegin(); - void componentComplete(); - -Q_SIGNALS: - void leftChanged(); - void rightChanged(); - void topChanged(); - void bottomChanged(); - void verticalCenterChanged(); - void horizontalCenterChanged(); - void baselineChanged(); - void fillChanged(); - void centerInChanged(); - void leftMarginChanged(); - void rightMarginChanged(); - void topMarginChanged(); - void bottomMarginChanged(); - void marginsChanged(); - void verticalCenterOffsetChanged(); - void horizontalCenterOffsetChanged(); - void baselineOffsetChanged(); - void mirroredChanged(); - -private: - friend class QSGItemPrivate; - Q_DISABLE_COPY(QSGAnchors) - Q_DECLARE_PRIVATE(QSGAnchors) -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(QSGAnchors::Anchors) - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGAnchors) - -QT_END_HEADER - -#endif // QSGANCHORS_P_H diff --git a/src/declarative/items/qsganchors_p_p.h b/src/declarative/items/qsganchors_p_p.h deleted file mode 100644 index 0417761314..0000000000 --- a/src/declarative/items/qsganchors_p_p.h +++ /dev/null @@ -1,173 +0,0 @@ -// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGANCHORS_P_P_H -#define QSGANCHORS_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsganchors_p.h" -#include "qsgitemchangelistener_p.h" -#include - -QT_BEGIN_NAMESPACE - -class QSGAnchorLine -{ -public: - QSGAnchorLine() : item(0), anchorLine(Invalid) {} - - enum AnchorLine { - Invalid = 0x0, - Left = 0x01, - Right = 0x02, - Top = 0x04, - Bottom = 0x08, - HCenter = 0x10, - VCenter = 0x20, - Baseline = 0x40, - Horizontal_Mask = Left | Right | HCenter, - Vertical_Mask = Top | Bottom | VCenter | Baseline - }; - - QSGItem *item; - AnchorLine anchorLine; -}; - -inline bool operator==(const QSGAnchorLine& a, const QSGAnchorLine& b) -{ - return a.item == b.item && a.anchorLine == b.anchorLine; -} - -class QSGAnchorsPrivate : public QObjectPrivate, public QSGItemChangeListener -{ - Q_DECLARE_PUBLIC(QSGAnchors) -public: - QSGAnchorsPrivate(QSGItem *i) - : componentComplete(true), updatingMe(false), updatingHorizontalAnchor(0), - updatingVerticalAnchor(0), updatingFill(0), updatingCenterIn(0), item(i), usedAnchors(0), fill(0), - centerIn(0), leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), - margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0) - { - } - - void clearItem(QSGItem *); - - void addDepend(QSGItem *); - void remDepend(QSGItem *); - bool isItemComplete() const; - - bool componentComplete:1; - bool updatingMe:1; - uint updatingHorizontalAnchor:2; - uint updatingVerticalAnchor:2; - uint updatingFill:2; - uint updatingCenterIn:2; - - void setItemHeight(qreal); - void setItemWidth(qreal); - void setItemX(qreal); - void setItemY(qreal); - void setItemPos(const QPointF &); - void setItemSize(const QSizeF &); - - void updateOnComplete(); - void updateMe(); - - // QSGItemGeometryListener interface - void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &); - QSGAnchorsPrivate *anchorPrivate() { return this; } - - bool checkHValid() const; - bool checkVValid() const; - bool checkHAnchorValid(QSGAnchorLine anchor) const; - bool checkVAnchorValid(QSGAnchorLine anchor) const; - bool calcStretch(const QSGAnchorLine &edge1, const QSGAnchorLine &edge2, qreal offset1, qreal offset2, QSGAnchorLine::AnchorLine line, qreal &stretch); - - bool isMirrored() const; - void updateHorizontalAnchors(); - void updateVerticalAnchors(); - void fillChanged(); - void centerInChanged(); - - QSGItem *item; - QSGAnchors::Anchors usedAnchors; - - QSGItem *fill; - QSGItem *centerIn; - - QSGAnchorLine left; - QSGAnchorLine right; - QSGAnchorLine top; - QSGAnchorLine bottom; - QSGAnchorLine vCenter; - QSGAnchorLine hCenter; - QSGAnchorLine baseline; - - qreal leftMargin; - qreal rightMargin; - qreal topMargin; - qreal bottomMargin; - qreal margins; - qreal vCenterOffset; - qreal hCenterOffset; - qreal baselineOffset; - - static inline QSGAnchorsPrivate *get(QSGAnchors *o) { - return static_cast(QObjectPrivate::get(o)); - } -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QSGAnchorLine) - -#endif diff --git a/src/declarative/items/qsganimatedimage.cpp b/src/declarative/items/qsganimatedimage.cpp deleted file mode 100644 index 93e0284417..0000000000 --- a/src/declarative/items/qsganimatedimage.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsganimatedimage_p.h" -#include "qsganimatedimage_p_p.h" - -#ifndef QT_NO_MOVIE - -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE -/*! - \qmlclass AnimatedImage QSGAnimatedImage - \inqmlmodule QtQuick 2 - \inherits Image - \ingroup basic-visual-elements - - The AnimatedImage element extends the features of the \l Image element, providing - a way to play animations stored as images containing a series of frames, - such as those stored in GIF files. - - Information about the current frame and totla length of the animation can be - obtained using the \l currentFrame and \l frameCount properties. You can - start, pause and stop the animation by changing the values of the \l playing - and \l paused properties. - - The full list of supported formats can be determined with QMovie::supportedFormats(). - - \section1 Example Usage - - \beginfloatleft - \image animatedimageitem.gif - \endfloat - - The following QML shows how to display an animated image and obtain information - about its state, such as the current frame and total number of frames. - The result is an animated image with a simple progress indicator underneath it. - - \bold Note: Unlike images, animated images are not cached or shared internally. - - \clearfloat - \snippet doc/src/snippets/declarative/animatedimage.qml document - - \sa BorderImage, Image -*/ - -/*! - \qmlproperty url QtQuick2::AnimatedImage::source - - This property holds the URL that refers to the source image. - - AnimatedImage can handle any image format supported by Qt, loaded from any - URL scheme supported by Qt. - - \sa QDeclarativeImageProvider -*/ - -/*! - \qmlproperty bool QtQuick2::AnimatedImage::asynchronous - - Specifies that images on the local filesystem should be loaded - asynchronously in a separate thread. The default value is - false, causing the user interface thread to block while the - image is loaded. Setting \a asynchronous to true is useful where - maintaining a responsive user interface is more desirable - than having images immediately visible. - - Note that this property is only valid for images read from the - local filesystem. Images loaded via a network resource (e.g. HTTP) - are always loaded asynchonously. -*/ - -/*! - \qmlproperty bool QtQuick2::AnimatedImage::mirror - - This property holds whether the image should be horizontally inverted - (effectively displaying a mirrored image). - - The default value is false. -*/ - -QSGAnimatedImage::QSGAnimatedImage(QSGItem *parent) - : QSGImage(*(new QSGAnimatedImagePrivate), parent) -{ -} - -QSGAnimatedImage::~QSGAnimatedImage() -{ - Q_D(QSGAnimatedImage); - delete d->_movie; -} - -/*! - \qmlproperty bool QtQuick2::AnimatedImage::paused - This property holds whether the animated image is paused. - - By default, this property is false. Set it to true when you want to pause - the animation. -*/ - -bool QSGAnimatedImage::isPaused() const -{ - Q_D(const QSGAnimatedImage); - if (!d->_movie) - return false; - return d->_movie->state()==QMovie::Paused; -} - -void QSGAnimatedImage::setPaused(bool pause) -{ - Q_D(QSGAnimatedImage); - if (pause == d->paused) - return; - d->paused = pause; - if (!d->_movie) - return; - d->_movie->setPaused(pause); -} - -/*! - \qmlproperty bool QtQuick2::AnimatedImage::playing - This property holds whether the animated image is playing. - - By default, this property is true, meaning that the animation - will start playing immediately. -*/ - -bool QSGAnimatedImage::isPlaying() const -{ - Q_D(const QSGAnimatedImage); - if (!d->_movie) - return false; - return d->_movie->state()!=QMovie::NotRunning; -} - -void QSGAnimatedImage::setPlaying(bool play) -{ - Q_D(QSGAnimatedImage); - if (play == d->playing) - return; - d->playing = play; - if (!d->_movie) - return; - if (play) - d->_movie->start(); - else - d->_movie->stop(); -} - -/*! - \qmlproperty int QtQuick2::AnimatedImage::currentFrame - \qmlproperty int QtQuick2::AnimatedImage::frameCount - - currentFrame is the frame that is currently visible. By monitoring this property - for changes, you can animate other items at the same time as the image. - - frameCount is the number of frames in the animation. For some animation formats, - frameCount is unknown and has a value of zero. -*/ -int QSGAnimatedImage::currentFrame() const -{ - Q_D(const QSGAnimatedImage); - if (!d->_movie) - return d->preset_currentframe; - return d->_movie->currentFrameNumber(); -} - -void QSGAnimatedImage::setCurrentFrame(int frame) -{ - Q_D(QSGAnimatedImage); - if (!d->_movie) { - d->preset_currentframe = frame; - return; - } - d->_movie->jumpToFrame(frame); -} - -int QSGAnimatedImage::frameCount() const -{ - Q_D(const QSGAnimatedImage); - if (!d->_movie) - return 0; - return d->_movie->frameCount(); -} - -void QSGAnimatedImage::setSource(const QUrl &url) -{ - Q_D(QSGAnimatedImage); - if (url == d->url) - return; - - delete d->_movie; - d->_movie = 0; - - if (d->reply) { - d->reply->deleteLater(); - d->reply = 0; - } - - d->url = url; - emit sourceChanged(d->url); - - if (isComponentComplete()) - load(); -} - -void QSGAnimatedImage::load() -{ - Q_D(QSGAnimatedImage); - - QSGImageBase::Status oldStatus = d->status; - qreal oldProgress = d->progress; - - if (d->url.isEmpty()) { - delete d->_movie; - d->setPixmap(QPixmap()); - d->progress = 0; - d->status = Null; - if (d->status != oldStatus) - emit statusChanged(d->status); - if (d->progress != oldProgress) - emit progressChanged(d->progress); - } else { - QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); - if (!lf.isEmpty()) { - //### should be unified with movieRequestFinished - d->_movie = new QMovie(lf); - if (!d->_movie->isValid()){ - qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString(); - delete d->_movie; - d->_movie = 0; - d->status = Error; - if (d->status != oldStatus) - emit statusChanged(d->status); - return; - } - connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), - this, SLOT(playingStatusChanged())); - connect(d->_movie, SIGNAL(frameChanged(int)), - this, SLOT(movieUpdate())); - d->_movie->setCacheMode(QMovie::CacheAll); - if (d->playing) - d->_movie->start(); - else - d->_movie->jumpToFrame(0); - if (d->paused) - d->_movie->setPaused(true); - d->setPixmap(d->_movie->currentPixmap()); - d->status = Ready; - d->progress = 1.0; - if (d->status != oldStatus) - emit statusChanged(d->status); - if (d->progress != oldProgress) - emit progressChanged(d->progress); - return; - } - - d->status = Loading; - d->progress = 0; - emit statusChanged(d->status); - emit progressChanged(d->progress); - QNetworkRequest req(d->url); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - d->reply = qmlEngine(this)->networkAccessManager()->get(req); - QObject::connect(d->reply, SIGNAL(finished()), - this, SLOT(movieRequestFinished())); - QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), - this, SLOT(requestProgress(qint64,qint64))); - } -} - -#define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16 - -void QSGAnimatedImage::movieRequestFinished() -{ - Q_D(QSGAnimatedImage); - - d->redirectCount++; - if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) { - QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute); - if (redirect.isValid()) { - QUrl url = d->reply->url().resolved(redirect.toUrl()); - d->reply->deleteLater(); - d->reply = 0; - setSource(url); - return; - } - } - d->redirectCount=0; - - d->_movie = new QMovie(d->reply); - if (!d->_movie->isValid()){ -#ifndef QT_NO_DEBUG_STREAM - qmlInfo(this) << "Error Reading Animated Image File " << d->url; -#endif - delete d->_movie; - d->_movie = 0; - d->status = Error; - emit statusChanged(d->status); - return; - } - connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), - this, SLOT(playingStatusChanged())); - connect(d->_movie, SIGNAL(frameChanged(int)), - this, SLOT(movieUpdate())); - d->_movie->setCacheMode(QMovie::CacheAll); - if (d->playing) - d->_movie->start(); - if (d->paused || !d->playing) { - d->_movie->jumpToFrame(d->preset_currentframe); - d->preset_currentframe = 0; - } - if (d->paused) - d->_movie->setPaused(true); - d->setPixmap(d->_movie->currentPixmap()); - d->status = Ready; - emit statusChanged(d->status); -} - -void QSGAnimatedImage::movieUpdate() -{ - Q_D(QSGAnimatedImage); - d->setPixmap(d->_movie->currentPixmap()); - emit frameChanged(); -} - -void QSGAnimatedImage::playingStatusChanged() -{ - Q_D(QSGAnimatedImage); - if ((d->_movie->state() != QMovie::NotRunning) != d->playing) { - d->playing = (d->_movie->state() != QMovie::NotRunning); - emit playingChanged(); - } - if ((d->_movie->state() == QMovie::Paused) != d->paused) { - d->playing = (d->_movie->state() == QMovie::Paused); - emit pausedChanged(); - } -} - -void QSGAnimatedImage::componentComplete() -{ - Q_D(QSGAnimatedImage); - QSGItem::componentComplete(); // NOT QSGImage - if (d->url.isValid()) - load(); - if (!d->reply) { - setCurrentFrame(d->preset_currentframe); - d->preset_currentframe = 0; - } -} - -QT_END_NAMESPACE - -#endif // QT_NO_MOVIE diff --git a/src/declarative/items/qsganimatedimage_p.h b/src/declarative/items/qsganimatedimage_p.h deleted file mode 100644 index efbe01d802..0000000000 --- a/src/declarative/items/qsganimatedimage_p.h +++ /dev/null @@ -1,117 +0,0 @@ -// Commit: 80d0fe9cbd92288a08d5ced8767f1edb651dae37 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGANIMATEDIMAGE_P_H -#define QSGANIMATEDIMAGE_P_H - -#include "qsgimage_p.h" - -#ifndef QT_NO_MOVIE - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QMovie; -class QSGAnimatedImagePrivate; - -class Q_AUTOTEST_EXPORT QSGAnimatedImage : public QSGImage -{ - Q_OBJECT - - Q_PROPERTY(bool playing READ isPlaying WRITE setPlaying NOTIFY playingChanged) - Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) - Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY frameChanged) - Q_PROPERTY(int frameCount READ frameCount) - - // read-only for AnimatedImage - Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) - -public: - QSGAnimatedImage(QSGItem *parent=0); - ~QSGAnimatedImage(); - - bool isPlaying() const; - void setPlaying(bool play); - - bool isPaused() const; - void setPaused(bool pause); - - int currentFrame() const; - void setCurrentFrame(int frame); - - int frameCount() const; - - // Extends QSGImage's src property*/ - virtual void setSource(const QUrl&); - -Q_SIGNALS: - void playingChanged(); - void pausedChanged(); - void frameChanged(); - void sourceSizeChanged(); - -private Q_SLOTS: - void movieUpdate(); - void movieRequestFinished(); - void playingStatusChanged(); - -protected: - virtual void load(); - void componentComplete(); - -private: - Q_DISABLE_COPY(QSGAnimatedImage) - Q_DECLARE_PRIVATE(QSGAnimatedImage) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGAnimatedImage) - -QT_END_HEADER - -#endif // QT_NO_MOVIE - -#endif // QSGANIMATEDIMAGE_P_H diff --git a/src/declarative/items/qsganimatedimage_p_p.h b/src/declarative/items/qsganimatedimage_p_p.h deleted file mode 100644 index f216df2502..0000000000 --- a/src/declarative/items/qsganimatedimage_p_p.h +++ /dev/null @@ -1,88 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGANIMATEDIMAGE_P_P_H -#define QSGANIMATEDIMAGE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgimage_p_p.h" - -#ifndef QT_NO_MOVIE - -QT_BEGIN_NAMESPACE - -class QMovie; -class QNetworkReply; - -class QSGAnimatedImagePrivate : public QSGImagePrivate -{ - Q_DECLARE_PUBLIC(QSGAnimatedImage) - -public: - QSGAnimatedImagePrivate() - : playing(true), paused(false), preset_currentframe(0), _movie(0), reply(0), redirectCount(0) - { - } - - bool playing; - bool paused; - int preset_currentframe; - QMovie *_movie; - QNetworkReply *reply; - int redirectCount; -}; - -QT_END_NAMESPACE - -#endif // QT_NO_MOVIE - -#endif // QSGANIMATEDIMAGE_P_P_H diff --git a/src/declarative/items/qsganimation.cpp b/src/declarative/items/qsganimation.cpp deleted file mode 100644 index 81c83913c1..0000000000 --- a/src/declarative/items/qsganimation.cpp +++ /dev/null @@ -1,792 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsganimation_p.h" -#include "qsganimation_p_p.h" -#include "qsgstateoperations_p.h" - -#include -#include - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QSGParentAnimation::QSGParentAnimation(QObject *parent) - : QDeclarativeAnimationGroup(*(new QSGParentAnimationPrivate), parent) -{ - Q_D(QSGParentAnimation); - d->topLevelGroup = new QSequentialAnimationGroup; - QDeclarative_setParent_noEvent(d->topLevelGroup, this); - - d->startAction = new QActionAnimation; - QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup); - d->topLevelGroup->addAnimation(d->startAction); - - d->ag = new QParallelAnimationGroup; - QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup); - d->topLevelGroup->addAnimation(d->ag); - - d->endAction = new QActionAnimation; - QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup); - d->topLevelGroup->addAnimation(d->endAction); -} - -QSGParentAnimation::~QSGParentAnimation() -{ -} - -QSGItem *QSGParentAnimation::target() const -{ - Q_D(const QSGParentAnimation); - return d->target; -} - -void QSGParentAnimation::setTarget(QSGItem *target) -{ - Q_D(QSGParentAnimation); - if (target == d->target) - return; - - d->target = target; - emit targetChanged(); -} - -QSGItem *QSGParentAnimation::newParent() const -{ - Q_D(const QSGParentAnimation); - return d->newParent; -} - -void QSGParentAnimation::setNewParent(QSGItem *newParent) -{ - Q_D(QSGParentAnimation); - if (newParent == d->newParent) - return; - - d->newParent = newParent; - emit newParentChanged(); -} - -QSGItem *QSGParentAnimation::via() const -{ - Q_D(const QSGParentAnimation); - return d->via; -} - -void QSGParentAnimation::setVia(QSGItem *via) -{ - Q_D(QSGParentAnimation); - if (via == d->via) - return; - - d->via = via; - emit viaChanged(); -} - -//### mirrors same-named function in QSGItem -QPointF QSGParentAnimationPrivate::computeTransformOrigin(QSGItem::TransformOrigin origin, qreal width, qreal height) const -{ - switch (origin) { - default: - case QSGItem::TopLeft: - return QPointF(0, 0); - case QSGItem::Top: - return QPointF(width / 2., 0); - case QSGItem::TopRight: - return QPointF(width, 0); - case QSGItem::Left: - return QPointF(0, height / 2.); - case QSGItem::Center: - return QPointF(width / 2., height / 2.); - case QSGItem::Right: - return QPointF(width, height / 2.); - case QSGItem::BottomLeft: - return QPointF(0, height); - case QSGItem::Bottom: - return QPointF(width / 2., height); - case QSGItem::BottomRight: - return QPointF(width, height); - } -} - -void QSGParentAnimation::transition(QDeclarativeStateActions &actions, - QDeclarativeProperties &modified, - TransitionDirection direction) -{ - Q_D(QSGParentAnimation); - - struct QSGParentAnimationData : public QAbstractAnimationAction - { - QSGParentAnimationData() {} - ~QSGParentAnimationData() { qDeleteAll(pc); } - - QDeclarativeStateActions actions; - //### reverse should probably apply on a per-action basis - bool reverse; - QList pc; - virtual void doAction() - { - for (int ii = 0; ii < actions.count(); ++ii) { - const QDeclarativeAction &action = actions.at(ii); - if (reverse) - action.event->reverse(); - else - action.event->execute(); - } - } - }; - - QSGParentAnimationData *data = new QSGParentAnimationData; - QSGParentAnimationData *viaData = new QSGParentAnimationData; - - bool hasExplicit = false; - if (d->target && d->newParent) { - data->reverse = false; - QDeclarativeAction myAction; - QSGParentChange *pc = new QSGParentChange; - pc->setObject(d->target); - pc->setParent(d->newParent); - myAction.event = pc; - data->pc << pc; - data->actions << myAction; - hasExplicit = true; - if (d->via) { - viaData->reverse = false; - QDeclarativeAction myVAction; - QSGParentChange *vpc = new QSGParentChange; - vpc->setObject(d->target); - vpc->setParent(d->via); - myVAction.event = vpc; - viaData->pc << vpc; - viaData->actions << myVAction; - } - //### once actions have concept of modified, - // loop to match appropriate ParentChanges and mark as modified - } - - if (!hasExplicit) - for (int i = 0; i < actions.size(); ++i) { - QDeclarativeAction &action = actions[i]; - if (action.event && action.event->typeName() == QLatin1String("ParentChange") - && (!d->target || static_cast(action.event)->object() == d->target)) { - - QSGParentChange *pc = static_cast(action.event); - QDeclarativeAction myAction = action; - data->reverse = action.reverseEvent; - - //### this logic differs from PropertyAnimation - // (probably a result of modified vs. done) - if (d->newParent) { - QSGParentChange *epc = new QSGParentChange; - epc->setObject(static_cast(action.event)->object()); - epc->setParent(d->newParent); - myAction.event = epc; - data->pc << epc; - data->actions << myAction; - pc = epc; - } else { - action.actionDone = true; - data->actions << myAction; - } - - if (d->via) { - viaData->reverse = false; - QDeclarativeAction myAction; - QSGParentChange *vpc = new QSGParentChange; - vpc->setObject(pc->object()); - vpc->setParent(d->via); - myAction.event = vpc; - viaData->pc << vpc; - viaData->actions << myAction; - QDeclarativeAction dummyAction; - QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; - QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; - QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; - QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; - QSGItem *target = pc->object(); - QSGItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent(); - - //### this mirrors the logic in QSGParentChange. - bool ok; - const QTransform &transform = targetParent->itemTransform(d->via, &ok); - if (transform.type() >= QTransform::TxShear || !ok) { - qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under complex transform"); - ok = false; - } - - qreal scale = 1; - qreal rotation = 0; - bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); - if (ok && !isRotate) { - if (transform.m11() == transform.m22()) - scale = transform.m11(); - else { - qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); - ok = false; - } - } else if (ok && isRotate) { - if (transform.m11() == transform.m22()) - scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); - else { - qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); - ok = false; - } - - if (scale != 0) - rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; - else { - qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under scale of 0"); - ok = false; - } - } - - const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal())); - qreal x = point.x(); - qreal y = point.y(); - if (ok && target->transformOrigin() != QSGItem::TopLeft) { - qreal w = target->width(); - qreal h = target->height(); - if (pc->widthIsSet() && i < actions.size() - 1) - w = actions[++i].toValue.toReal(); - if (pc->heightIsSet() && i < actions.size() - 1) - h = actions[++i].toValue.toReal(); - const QPointF &transformOrigin - = d->computeTransformOrigin(target->transformOrigin(), w,h); - qreal tempxt = transformOrigin.x(); - qreal tempyt = transformOrigin.y(); - QTransform t; - t.translate(-tempxt, -tempyt); - t.rotate(rotation); - t.scale(scale, scale); - t.translate(tempxt, tempyt); - const QPointF &offset = t.map(QPointF(0,0)); - x += offset.x(); - y += offset.y(); - } - - if (ok) { - //qDebug() << x << y << rotation << scale; - xAction.toValue = x; - yAction.toValue = y; - sAction.toValue = sAction.toValue.toReal() * scale; - rAction.toValue = rAction.toValue.toReal() + rotation; - } - } - } - } - - if (data->actions.count()) { - if (direction == QDeclarativeAbstractAnimation::Forward) { - d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); - d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); - } else { - d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); - d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); - } - } else { - delete data; - delete viaData; - } - - //take care of any child animations - bool valid = d->defaultProperty.isValid(); - for (int ii = 0; ii < d->animations.count(); ++ii) { - if (valid) - d->animations.at(ii)->setDefaultTarget(d->defaultProperty); - d->animations.at(ii)->transition(actions, modified, direction); - } - -} - -QAbstractAnimation *QSGParentAnimation::qtAnimation() -{ - Q_D(QSGParentAnimation); - return d->topLevelGroup; -} - -QSGAnchorAnimation::QSGAnchorAnimation(QObject *parent) -: QDeclarativeAbstractAnimation(*(new QSGAnchorAnimationPrivate), parent) -{ - Q_D(QSGAnchorAnimation); - d->va = new QDeclarativeBulkValueAnimator; - QDeclarative_setParent_noEvent(d->va, this); -} - -QSGAnchorAnimation::~QSGAnchorAnimation() -{ -} - -QAbstractAnimation *QSGAnchorAnimation::qtAnimation() -{ - Q_D(QSGAnchorAnimation); - return d->va; -} - -QDeclarativeListProperty QSGAnchorAnimation::targets() -{ - Q_D(QSGAnchorAnimation); - return QDeclarativeListProperty(this, d->targets); -} - -int QSGAnchorAnimation::duration() const -{ - Q_D(const QSGAnchorAnimation); - return d->va->duration(); -} - -void QSGAnchorAnimation::setDuration(int duration) -{ - if (duration < 0) { - qmlInfo(this) << tr("Cannot set a duration of < 0"); - return; - } - - Q_D(QSGAnchorAnimation); - if (d->va->duration() == duration) - return; - d->va->setDuration(duration); - emit durationChanged(duration); -} - -QEasingCurve QSGAnchorAnimation::easing() const -{ - Q_D(const QSGAnchorAnimation); - return d->va->easingCurve(); -} - -void QSGAnchorAnimation::setEasing(const QEasingCurve &e) -{ - Q_D(QSGAnchorAnimation); - if (d->va->easingCurve() == e) - return; - - d->va->setEasingCurve(e); - emit easingChanged(e); -} - -void QSGAnchorAnimation::transition(QDeclarativeStateActions &actions, - QDeclarativeProperties &modified, - TransitionDirection direction) -{ - Q_UNUSED(modified); - Q_D(QSGAnchorAnimation); - QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; - data->interpolatorType = QMetaType::QReal; - data->interpolator = d->interpolator; - - data->reverse = direction == Backward ? true : false; - data->fromSourced = false; - data->fromDefined = false; - - for (int ii = 0; ii < actions.count(); ++ii) { - QDeclarativeAction &action = actions[ii]; - if (action.event && action.event->typeName() == QLatin1String("AnchorChanges") - && (d->targets.isEmpty() || d->targets.contains(static_cast(action.event)->object()))) { - data->actions << static_cast(action.event)->additionalActions(); - } - } - - if (data->actions.count()) { - if (!d->rangeIsSet) { - d->va->setStartValue(qreal(0)); - d->va->setEndValue(qreal(1)); - d->rangeIsSet = true; - } - d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); - d->va->setFromSourcedValue(&data->fromSourced); - } else { - delete data; - } -} - -QSGPathAnimation::QSGPathAnimation(QObject *parent) -: QDeclarativeAbstractAnimation(*(new QSGPathAnimationPrivate), parent) -{ - Q_D(QSGPathAnimation); - d->pa = new QDeclarativeBulkValueAnimator; - QDeclarative_setParent_noEvent(d->pa, this); -} - -QSGPathAnimation::~QSGPathAnimation() -{ -} - -int QSGPathAnimation::duration() const -{ - Q_D(const QSGPathAnimation); - return d->pa->duration(); -} - -void QSGPathAnimation::setDuration(int duration) -{ - if (duration < 0) { - qmlInfo(this) << tr("Cannot set a duration of < 0"); - return; - } - - Q_D(QSGPathAnimation); - if (d->pa->duration() == duration) - return; - d->pa->setDuration(duration); - emit durationChanged(duration); -} - -QEasingCurve QSGPathAnimation::easing() const -{ - Q_D(const QSGPathAnimation); - return d->pa->easingCurve(); -} - -void QSGPathAnimation::setEasing(const QEasingCurve &e) -{ - Q_D(QSGPathAnimation); - if (d->pa->easingCurve() == e) - return; - - d->pa->setEasingCurve(e); - emit easingChanged(e); -} - -QDeclarativePath *QSGPathAnimation::path() const -{ - Q_D(const QSGPathAnimation); - return d->path; -} - -void QSGPathAnimation::setPath(QDeclarativePath *path) -{ - Q_D(QSGPathAnimation); - if (d->path == path) - return; - - d->path = path; - emit pathChanged(); -} - -QSGItem *QSGPathAnimation::target() const -{ - Q_D(const QSGPathAnimation); - return d->target; -} - -void QSGPathAnimation::setTarget(QSGItem *target) -{ - Q_D(QSGPathAnimation); - if (d->target == target) - return; - - d->target = target; - emit targetChanged(); -} - -QSGPathAnimation::Orientation QSGPathAnimation::orientation() const -{ - Q_D(const QSGPathAnimation); - return d->orientation; -} - -void QSGPathAnimation::setOrientation(Orientation orientation) -{ - Q_D(QSGPathAnimation); - if (d->orientation == orientation) - return; - - d->orientation = orientation; - emit orientationChanged(d->orientation); -} - -QPointF QSGPathAnimation::anchorPoint() const -{ - Q_D(const QSGPathAnimation); - return d->anchorPoint; -} - -void QSGPathAnimation::setAnchorPoint(const QPointF &point) -{ - Q_D(QSGPathAnimation); - if (d->anchorPoint == point) - return; - - d->anchorPoint = point; - emit anchorPointChanged(point); -} - -qreal QSGPathAnimation::orientationEntryInterval() const -{ - Q_D(const QSGPathAnimation); - return d->entryInterval; -} - -void QSGPathAnimation::setOrientationEntryInterval(qreal interval) -{ - Q_D(QSGPathAnimation); - if (d->entryInterval == interval) - return; - d->entryInterval = interval; - emit orientationEntryIntervalChanged(interval); -} - -qreal QSGPathAnimation::orientationExitInterval() const -{ - Q_D(const QSGPathAnimation); - return d->exitInterval; -} - -void QSGPathAnimation::setOrientationExitInterval(qreal interval) -{ - Q_D(QSGPathAnimation); - if (d->exitInterval == interval) - return; - d->exitInterval = interval; - emit orientationExitIntervalChanged(interval); -} - -qreal QSGPathAnimation::endRotation() const -{ - Q_D(const QSGPathAnimation); - return d->endRotation.isNull ? qreal(0) : d->endRotation.value; -} - -void QSGPathAnimation::setEndRotation(qreal rotation) -{ - Q_D(QSGPathAnimation); - if (!d->endRotation.isNull && d->endRotation == rotation) - return; - - d->endRotation = rotation; - emit endRotationChanged(d->endRotation); -} - - -QAbstractAnimation *QSGPathAnimation::qtAnimation() -{ - Q_D(QSGPathAnimation); - return d->pa; -} - -void QSGPathAnimation::transition(QDeclarativeStateActions &actions, - QDeclarativeProperties &modified, - TransitionDirection direction) -{ - Q_D(QSGPathAnimation); - QSGPathAnimationUpdater *data = new QSGPathAnimationUpdater; - - data->orientation = d->orientation; - data->anchorPoint = d->anchorPoint; - data->entryInterval = d->entryInterval; - data->exitInterval = d->exitInterval; - data->endRotation = d->endRotation; - data->reverse = direction == Backward ? true : false; - data->fromSourced = false; - data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false; - data->toDefined = d->path ? d->path->hasEnd() : false; - int origModifiedSize = modified.count(); - - for (int i = 0; i < actions.count(); ++i) { - QDeclarativeAction &action = actions[i]; - if (action.event) - continue; - if (action.specifiedObject == d->target && action.property.name() == QLatin1String("x")) { - data->toX = action.toValue.toReal(); - modified << action.property; - action.fromValue = action.toValue; - } - if (action.specifiedObject == d->target && action.property.name() == QLatin1String("y")) { - data->toY = action.toValue.toReal(); - modified << action.property; - action.fromValue = action.toValue; - } - } - - if (d->target && d->path && - (modified.count() > origModifiedSize || data->toDefined)) { - data->target = d->target; - data->path = d->path; - if (!d->rangeIsSet) { - d->pa->setStartValue(qreal(0)); - d->pa->setEndValue(qreal(1)); - d->rangeIsSet = true; - } - /* - NOTE: The following block relies on the fact that the previous value hasn't - yet been deleted, and has the same target, etc, which may be a bit fragile. - */ - if (d->pa->getAnimValue()) { - QSGPathAnimationUpdater *prevData = static_cast(d->pa->getAnimValue()); - - // get the original start angle that was used (so we can exactly reverse). - data->startRotation = prevData->startRotation; - - // treat interruptions specially, otherwise we end up with strange paths - if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) { - if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) { - QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV); - if (!prevData->anchorPoint.isNull()) - pathPos -= prevData->anchorPoint; - if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself - data->painterPath = prevData->painterPath; - data->toDefined = data->fromDefined = data->fromSourced = true; - data->prevBez.isValid = false; - data->interruptStart = prevData->currentV; - data->startRotation = prevData->startRotation; - data->pathLength = prevData->pathLength; - data->attributePoints = prevData->attributePoints; - } - } - } - } - d->pa->setFromSourcedValue(&data->fromSourced); - d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped); - } else { - d->pa->setFromSourcedValue(0); - d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped); - delete data; - } -} - -void QSGPathAnimationUpdater::setValue(qreal v) -{ - if (interruptStart.isValid()) { - if (reverse) - v = 1 - v; - qreal end = reverse ? 0.0 : 1.0; - v = interruptStart + v * (end-interruptStart); - } - currentV = v; - bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0)); - if (!fromSourced && (!fromDefined || !toDefined)) { - qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x(); - qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y(); - qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x(); - qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y(); - - prevBez.isValid = false; - painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints); - fromSourced = true; - } - - qreal angle; - bool fixed = orientation == QSGPathAnimation::Fixed; - QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle); - - //adjust position according to anchor point - if (!anchorPoint.isNull()) { - currentPos -= anchorPoint; - if (atStart) { - if (!anchorPoint.isNull() && !fixed) - target->setTransformOriginPoint(anchorPoint); - } - } - - //### could cache properties rather than reconstructing each time - QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("x")), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); - QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("y")), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); - - //adjust angle according to orientation - if (!fixed) { - switch (orientation) { - case QSGPathAnimation::RightFirst: - angle = -angle; - break; - case QSGPathAnimation::TopFirst: - angle = -angle + 90; - break; - case QSGPathAnimation::LeftFirst: - angle = -angle + 180; - break; - case QSGPathAnimation::BottomFirst: - angle = -angle + 270; - break; - default: - angle = 0; - break; - } - - if (atStart && !reverse) { - startRotation = target->rotation(); - - //shortest distance to correct orientation - qreal diff = angle - startRotation; - while (diff > 180.0) { - startRotation.value += 360.0; - diff -= 360.0; - } - while (diff < -180.0) { - startRotation.value -= 360.0; - diff += 360.0; - } - } - - //smoothly transition to the desired orientation - if (startRotation.isValid()) { - if (reverse && v == 0.0) - angle = startRotation; - else if (v < entryInterval) - angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval; - } - if (endRotation.isValid()) { - qreal exitStart = 1 - exitInterval; - if (!reverse && v == 1.0) - angle = endRotation; - else if (v > exitStart) - angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval; - } - QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("rotation")), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); - } - - /* - NOTE: we don't always reset the transform origin, as it can cause a - visual jump if ending on an angle. This means that in some cases - (anchor point and orientation both specified, and ending at an angle) - the transform origin will always be set after running the path animation. - */ - if ((reverse && v == 0.0) || (!reverse && v == 1.0)) { - if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle)) - target->setTransformOriginPoint(QPointF()); - } -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsganimation_p.h b/src/declarative/items/qsganimation_p.h deleted file mode 100644 index af6279aad0..0000000000 --- a/src/declarative/items/qsganimation_p.h +++ /dev/null @@ -1,209 +0,0 @@ -// Commit: e39a2e39451bf106a9845f8a60fc571faaa4dde5 -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGANIMATION_H -#define QSGANIMATION_H - -#include "qsgitem.h" - -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGParentAnimationPrivate; -class QSGParentAnimation : public QDeclarativeAnimationGroup -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGParentAnimation) - - Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged) - Q_PROPERTY(QSGItem *newParent READ newParent WRITE setNewParent NOTIFY newParentChanged) - Q_PROPERTY(QSGItem *via READ via WRITE setVia NOTIFY viaChanged) - -public: - QSGParentAnimation(QObject *parent=0); - virtual ~QSGParentAnimation(); - - QSGItem *target() const; - void setTarget(QSGItem *); - - QSGItem *newParent() const; - void setNewParent(QSGItem *); - - QSGItem *via() const; - void setVia(QSGItem *); - -Q_SIGNALS: - void targetChanged(); - void newParentChanged(); - void viaChanged(); - -protected: - virtual void transition(QDeclarativeStateActions &actions, - QDeclarativeProperties &modified, - TransitionDirection direction); - virtual QAbstractAnimation *qtAnimation(); -}; - -class QSGAnchorAnimationPrivate; -class QSGAnchorAnimation : public QDeclarativeAbstractAnimation -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGAnchorAnimation) - Q_PROPERTY(QDeclarativeListProperty targets READ targets) - Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) - Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) - -public: - QSGAnchorAnimation(QObject *parent=0); - virtual ~QSGAnchorAnimation(); - - QDeclarativeListProperty targets(); - - int duration() const; - void setDuration(int); - - QEasingCurve easing() const; - void setEasing(const QEasingCurve &); - -Q_SIGNALS: - void durationChanged(int); - void easingChanged(const QEasingCurve&); - -protected: - virtual void transition(QDeclarativeStateActions &actions, - QDeclarativeProperties &modified, - TransitionDirection direction); - virtual QAbstractAnimation *qtAnimation(); -}; - -class QSGItem; -class QDeclarativePath; -class QSGPathAnimationPrivate; -class Q_AUTOTEST_EXPORT QSGPathAnimation : public QDeclarativeAbstractAnimation -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGPathAnimation) - - Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) - Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) - Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged) - Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged) - Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) - Q_PROPERTY(QPointF anchorPoint READ anchorPoint WRITE setAnchorPoint NOTIFY anchorPointChanged) - Q_PROPERTY(qreal orientationEntryInterval READ orientationEntryInterval WRITE setOrientationEntryInterval NOTIFY orientationEntryIntervalChanged) - Q_PROPERTY(qreal orientationExitInterval READ orientationExitInterval WRITE setOrientationExitInterval NOTIFY orientationExitIntervalChanged) - Q_PROPERTY(qreal endRotation READ endRotation WRITE setEndRotation NOTIFY endRotationChanged) - -public: - QSGPathAnimation(QObject *parent=0); - virtual ~QSGPathAnimation(); - - enum Orientation { - Fixed, - RightFirst, - LeftFirst, - BottomFirst, - TopFirst - }; - Q_ENUMS(Orientation) - - int duration() const; - void setDuration(int); - - QEasingCurve easing() const; - void setEasing(const QEasingCurve &); - - QDeclarativePath *path() const; - void setPath(QDeclarativePath *); - - QSGItem *target() const; - void setTarget(QSGItem *); - - Orientation orientation() const; - void setOrientation(Orientation orientation); - - QPointF anchorPoint() const; - void setAnchorPoint(const QPointF &point); - - qreal orientationEntryInterval() const; - void setOrientationEntryInterval(qreal); - - qreal orientationExitInterval() const; - void setOrientationExitInterval(qreal); - - qreal endRotation() const; - void setEndRotation(qreal); - -protected: - virtual void transition(QDeclarativeStateActions &actions, - QDeclarativeProperties &modified, - TransitionDirection direction); - virtual QAbstractAnimation *qtAnimation(); - -Q_SIGNALS: - void durationChanged(int); - void easingChanged(const QEasingCurve &); - void pathChanged(); - void targetChanged(); - void orientationChanged(Orientation); - void anchorPointChanged(const QPointF &); - void orientationEntryIntervalChanged(qreal); - void orientationExitIntervalChanged(qreal); - void endRotationChanged(qreal); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGParentAnimation) -QML_DECLARE_TYPE(QSGAnchorAnimation) -QML_DECLARE_TYPE(QSGPathAnimation) - -QT_END_HEADER - -#endif // QSGANIMATION_H diff --git a/src/declarative/items/qsganimation_p_p.h b/src/declarative/items/qsganimation_p_p.h deleted file mode 100644 index 276efc5eb4..0000000000 --- a/src/declarative/items/qsganimation_p_p.h +++ /dev/null @@ -1,153 +0,0 @@ -// Commit: 0ade09152067324f74678f2de4d447b6e0280600 -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGANIMATION_P_H -#define QSGANIMATION_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsganimation_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGParentAnimationPrivate : public QDeclarativeAnimationGroupPrivate -{ - Q_DECLARE_PUBLIC(QSGParentAnimation) -public: - QSGParentAnimationPrivate() - : QDeclarativeAnimationGroupPrivate(), target(0), newParent(0), - via(0), topLevelGroup(0), startAction(0), endAction(0) {} - - QSGItem *target; - QSGItem *newParent; - QSGItem *via; - - QSequentialAnimationGroup *topLevelGroup; - QActionAnimation *startAction; - QActionAnimation *endAction; - - QPointF computeTransformOrigin(QSGItem::TransformOrigin origin, qreal width, qreal height) const; -}; - -class QSGAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate -{ - Q_DECLARE_PUBLIC(QSGAnchorAnimation) -public: - QSGAnchorAnimationPrivate() : rangeIsSet(false), va(0), - interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {} - - bool rangeIsSet; - QDeclarativeBulkValueAnimator *va; - QVariantAnimation::Interpolator interpolator; - QList targets; -}; - -class QSGPathAnimationUpdater : public QDeclarativeBulkValueUpdater -{ -public: - QSGPathAnimationUpdater() : path(0), target(0), reverse(false), - fromSourced(false), fromDefined(false), toDefined(false), - toX(0), toY(0), currentV(0), orientation(QSGPathAnimation::Fixed), - entryInterval(0), exitInterval(0) {} - ~QSGPathAnimationUpdater() {} - - void setValue(qreal v); - - QDeclarativePath *path; - - QPainterPath painterPath; - QDeclarativeCachedBezier prevBez; - qreal pathLength; - QList attributePoints; - - QSGItem *target; - bool reverse; - bool fromSourced; - bool fromDefined; - bool toDefined; - qreal toX; - qreal toY; - qreal currentV; - QDeclarativeNullableValue interruptStart; - //TODO: bundle below into common struct - QSGPathAnimation::Orientation orientation; - QPointF anchorPoint; - qreal entryInterval; - qreal exitInterval; - QDeclarativeNullableValue endRotation; - QDeclarativeNullableValue startRotation; -}; - -class QSGPathAnimationPrivate : public QDeclarativeAbstractAnimationPrivate -{ - Q_DECLARE_PUBLIC(QSGPathAnimation) -public: - QSGPathAnimationPrivate() : path(0), target(0), - rangeIsSet(false), orientation(QSGPathAnimation::Fixed), entryInterval(0), exitInterval(0), pa(0) {} - - QDeclarativePath *path; - QSGItem *target; - bool rangeIsSet; - QSGPathAnimation::Orientation orientation; - QPointF anchorPoint; - qreal entryInterval; - qreal exitInterval; - QDeclarativeNullableValue endRotation; - QDeclarativeBulkValueAnimator *pa; -}; - - -QT_END_NAMESPACE - -#endif // QSGANIMATION_P_H diff --git a/src/declarative/items/qsgborderimage.cpp b/src/declarative/items/qsgborderimage.cpp deleted file mode 100644 index fb407d7e2c..0000000000 --- a/src/declarative/items/qsgborderimage.cpp +++ /dev/null @@ -1,601 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgborderimage_p.h" -#include "qsgborderimage_p_p.h" -#include "qsgninepatchnode_p.h" - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - - -/*! - \qmlclass BorderImage QSGBorderImage - \inqmlmodule QtQuick 2 - \brief The BorderImage element provides an image that can be used as a border. - \inherits Item - \ingroup qml-basic-visual-elements - - The BorderImage element is used to create borders out of images by scaling or tiling - parts of each image. - - A BorderImage element breaks a source image, specified using the \l url property, - into 9 regions, as shown below: - - \image declarative-scalegrid.png - - When the image is scaled, regions of the source image are scaled or tiled to - create the displayed border image in the following way: - - \list - \i The corners (regions 1, 3, 7, and 9) are not scaled at all. - \i Regions 2 and 8 are scaled according to - \l{BorderImage::horizontalTileMode}{horizontalTileMode}. - \i Regions 4 and 6 are scaled according to - \l{BorderImage::verticalTileMode}{verticalTileMode}. - \i The middle (region 5) is scaled according to both - \l{BorderImage::horizontalTileMode}{horizontalTileMode} and - \l{BorderImage::verticalTileMode}{verticalTileMode}. - \endlist - - The regions of the image are defined using the \l border property group, which - describes the distance from each edge of the source image to use as a border. - - \section1 Example Usage - - The following examples show the effects of the different modes on an image. - Guide lines are overlaid onto the image to show the different regions of the - image as described above. - - \beginfloatleft - \image qml-borderimage-normal-image.png - \endfloat - - An unscaled image is displayed using an Image element. The \l border property is - used to determine the parts of the image that will lie inside the unscaled corner - areas and the parts that will be stretched horizontally and vertically. - - \snippet doc/src/snippets/declarative/borderimage/normal-image.qml normal image - - \clearfloat - \beginfloatleft - \image qml-borderimage-scaled.png - \endfloat - - A BorderImage element is used to display the image, and it is given a size that is - larger than the original image. Since the \l horizontalTileMode property is set to - \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in - regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property - is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image - in regions 4 and 6 are stretched vertically. - - \snippet doc/src/snippets/declarative/borderimage/borderimage-scaled.qml scaled border image - - \clearfloat - \beginfloatleft - \image qml-borderimage-tiled.png - \endfloat - - Again, a large BorderImage element is used to display the image. With the - \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat}, - the parts of image in regions 2 and 8 are tiled so that they fill the space at the - top and bottom of the element. Similarly, the \l verticalTileMode property is set to - \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions - 4 and 6 are tiled so that they fill the space at the left and right of the element. - - \snippet doc/src/snippets/declarative/borderimage/borderimage-tiled.qml tiled border image - - \clearfloat - In some situations, the width of regions 2 and 8 may not be an exact multiple of the width - of the corresponding regions in the source image. Similarly, the height of regions 4 and 6 - may not be an exact multiple of the height of the corresponding regions. It can be useful - to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of - \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these. - - The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage - can be used to simulate a shadow effect on a rectangular item. - - \section1 Quality and Performance - - By default, any scaled regions of the image are rendered without smoothing to improve - rendering speed. Setting the \l smooth property improves rendering quality of scaled - regions, but may slow down rendering. - - The source image may not be loaded instantaneously, depending on its original location. - Loading progress can be monitored with the \l progress property. - - \sa Image, AnimatedImage - */ - -/*! - \qmlproperty bool QtQuick2::BorderImage::asynchronous - - Specifies that images on the local filesystem should be loaded - asynchronously in a separate thread. The default value is - false, causing the user interface thread to block while the - image is loaded. Setting \a asynchronous to true is useful where - maintaining a responsive user interface is more desirable - than having images immediately visible. - - Note that this property is only valid for images read from the - local filesystem. Images loaded via a network resource (e.g. HTTP) - are always loaded asynchonously. -*/ -QSGBorderImage::QSGBorderImage(QSGItem *parent) -: QSGImageBase(*(new QSGBorderImagePrivate), parent) -{ -} - -QSGBorderImage::~QSGBorderImage() -{ - Q_D(QSGBorderImage); - if (d->sciReply) - d->sciReply->deleteLater(); -} - -/*! - \qmlproperty enumeration QtQuick2::BorderImage::status - - This property describes the status of image loading. It can be one of: - - \list - \o BorderImage.Null - no image has been set - \o BorderImage.Ready - the image has been loaded - \o BorderImage.Loading - the image is currently being loaded - \o BorderImage.Error - an error occurred while loading the image - \endlist - - \sa progress -*/ - -/*! - \qmlproperty real QtQuick2::BorderImage::progress - - This property holds the progress of image loading, from 0.0 (nothing loaded) - to 1.0 (finished). - - \sa status -*/ - -/*! - \qmlproperty bool QtQuick2::BorderImage::smooth - - Set this property if you want the image to be smoothly filtered when scaled or - transformed. Smooth filtering gives better visual quality, but is slower. If - the image is displayed at its natural size, this property has no visual or - performance effect. - - By default, this property is set to false. - - \note Generally scaling artifacts are only visible if the image is stationary on - the screen. A common pattern when animating an image is to disable smooth - filtering at the beginning of the animation and enable it at the conclusion. -*/ - -/*! - \qmlproperty bool QtQuick2::BorderImage::cache - - Specifies whether the image should be cached. The default value is - true. Setting \a cache to false is useful when dealing with large images, - to make sure that they aren't cached at the expense of small 'ui element' images. -*/ - -/*! - \qmlproperty bool QtQuick2::BorderImage::mirror - - This property holds whether the image should be horizontally inverted - (effectively displaying a mirrored image). - - The default value is false. -*/ - -/*! - \qmlproperty url QtQuick2::BorderImage::source - - This property holds the URL that refers to the source image. - - BorderImage can handle any image format supported by Qt, loaded from any - URL scheme supported by Qt. - - This property can also be used to refer to .sci files, which are - written in a QML-specific, text-based format that specifies the - borders, the image file and the tile rules for a given border image. - - The following .sci file sets the borders to 10 on each side for the - image \c picture.png: - - \code - border.left: 10 - border.top: 10 - border.bottom: 10 - border.right: 10 - source: "picture.png" - \endcode - - The URL may be absolute, or relative to the URL of the component. - - \sa QDeclarativeImageProvider -*/ - -/*! - \qmlproperty QSize QtQuick2::BorderImage::sourceSize - - This property holds the actual width and height of the loaded image. - - In BorderImage, this property is read-only. - - \sa Image::sourceSize -*/ -void QSGBorderImage::setSource(const QUrl &url) -{ - Q_D(QSGBorderImage); - //equality is fairly expensive, so we bypass for simple, common case - if ((d->url.isEmpty() == url.isEmpty()) && url == d->url) - return; - - if (d->sciReply) { - d->sciReply->deleteLater(); - d->sciReply = 0; - } - - d->url = url; - d->sciurl = QUrl(); - emit sourceChanged(d->url); - - if (isComponentComplete()) - load(); -} - -void QSGBorderImage::load() -{ - Q_D(QSGBorderImage); - if (d->progress != 0.0) { - d->progress = 0.0; - emit progressChanged(d->progress); - } - - if (d->url.isEmpty()) { - d->pix.clear(this); - d->status = Null; - setImplicitWidth(0); - setImplicitHeight(0); - emit statusChanged(d->status); - update(); - } else { - d->status = Loading; - if (d->url.path().endsWith(QLatin1String("sci"))) { - QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); - if (!lf.isEmpty()) { - QFile file(lf); - file.open(QIODevice::ReadOnly); - setGridScaledImage(QSGGridScaledImage(&file)); - } else { - QNetworkRequest req(d->url); - d->sciReply = qmlEngine(this)->networkAccessManager()->get(req); - FAST_CONNECT(d->sciReply, SIGNAL(finished()), this, SLOT(sciRequestFinished())) - } - } else { - - QDeclarativePixmap::Options options; - if (d->async) - options |= QDeclarativePixmap::Asynchronous; - if (d->cache) - options |= QDeclarativePixmap::Cache; - d->pix.clear(this); - d->pix.load(qmlEngine(this), d->url, options); - - if (d->pix.isLoading()) { - d->pix.connectFinished(this, SLOT(requestFinished())); - d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64))); - } else { - QSize impsize = d->pix.implicitSize(); - setImplicitWidth(impsize.width()); - setImplicitHeight(impsize.height()); - - if (d->pix.isReady()) { - d->status = Ready; - } else { - d->status = Error; - qmlInfo(this) << d->pix.error(); - } - - d->progress = 1.0; - emit statusChanged(d->status); - emit progressChanged(d->progress); - update(); - } - } - } - - emit statusChanged(d->status); -} - -/*! - \qmlproperty int QtQuick2::BorderImage::border.left - \qmlproperty int QtQuick2::BorderImage::border.right - \qmlproperty int QtQuick2::BorderImage::border.top - \qmlproperty int QtQuick2::BorderImage::border.bottom - - The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections, - as shown below: - - \image declarative-scalegrid.png - - Each border line (left, right, top, and bottom) specifies an offset in pixels - from the respective edge of the source image. By default, each border line has - a value of 0. - - For example, the following definition sets the bottom line 10 pixels up from - the bottom of the image: - - \qml - BorderImage { - border.bottom: 10 - // ... - } - \endqml - - The border lines can also be specified using a - \l {BorderImage::source}{.sci file}. -*/ - -QSGScaleGrid *QSGBorderImage::border() -{ - Q_D(QSGBorderImage); - return d->getScaleGrid(); -} - -/*! - \qmlproperty enumeration QtQuick2::BorderImage::horizontalTileMode - \qmlproperty enumeration QtQuick2::BorderImage::verticalTileMode - - This property describes how to repeat or stretch the middle parts of the border image. - - \list - \o BorderImage.Stretch - Scales the image to fit to the available area. - \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image. - \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped. - \endlist - - The default tile mode for each property is BorderImage.Stretch. -*/ -QSGBorderImage::TileMode QSGBorderImage::horizontalTileMode() const -{ - Q_D(const QSGBorderImage); - return d->horizontalTileMode; -} - -void QSGBorderImage::setHorizontalTileMode(TileMode t) -{ - Q_D(QSGBorderImage); - if (t != d->horizontalTileMode) { - d->horizontalTileMode = t; - emit horizontalTileModeChanged(); - update(); - } -} - -QSGBorderImage::TileMode QSGBorderImage::verticalTileMode() const -{ - Q_D(const QSGBorderImage); - return d->verticalTileMode; -} - -void QSGBorderImage::setVerticalTileMode(TileMode t) -{ - Q_D(QSGBorderImage); - if (t != d->verticalTileMode) { - d->verticalTileMode = t; - emit verticalTileModeChanged(); - update(); - } -} - -void QSGBorderImage::setGridScaledImage(const QSGGridScaledImage& sci) -{ - Q_D(QSGBorderImage); - if (!sci.isValid()) { - d->status = Error; - emit statusChanged(d->status); - } else { - QSGScaleGrid *sg = border(); - sg->setTop(sci.gridTop()); - sg->setBottom(sci.gridBottom()); - sg->setLeft(sci.gridLeft()); - sg->setRight(sci.gridRight()); - d->horizontalTileMode = sci.horizontalTileRule(); - d->verticalTileMode = sci.verticalTileRule(); - - d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl())); - - QDeclarativePixmap::Options options; - if (d->async) - options |= QDeclarativePixmap::Asynchronous; - if (d->cache) - options |= QDeclarativePixmap::Cache; - d->pix.clear(this); - d->pix.load(qmlEngine(this), d->sciurl, options); - - if (d->pix.isLoading()) { - static int thisRequestProgress = -1; - static int thisRequestFinished = -1; - if (thisRequestProgress == -1) { - thisRequestProgress = - QSGBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); - thisRequestFinished = - QSGBorderImage::staticMetaObject.indexOfSlot("requestFinished()"); - } - - d->pix.connectFinished(this, thisRequestFinished); - d->pix.connectDownloadProgress(this, thisRequestProgress); - - } else { - - QSize impsize = d->pix.implicitSize(); - setImplicitWidth(impsize.width()); - setImplicitHeight(impsize.height()); - - if (d->pix.isReady()) { - d->status = Ready; - } else { - d->status = Error; - qmlInfo(this) << d->pix.error(); - } - - d->progress = 1.0; - emit statusChanged(d->status); - emit progressChanged(1.0); - update(); - - } - } -} - -void QSGBorderImage::requestFinished() -{ - Q_D(QSGBorderImage); - - QSize impsize = d->pix.implicitSize(); - if (d->pix.isError()) { - d->status = Error; - qmlInfo(this) << d->pix.error(); - } else { - d->status = Ready; - } - - setImplicitWidth(impsize.width()); - setImplicitHeight(impsize.height()); - - if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height()) - emit sourceSizeChanged(); - - d->progress = 1.0; - emit statusChanged(d->status); - emit progressChanged(1.0); - update(); -} - -#define BORDERIMAGE_MAX_REDIRECT 16 - -void QSGBorderImage::sciRequestFinished() -{ - Q_D(QSGBorderImage); - - d->redirectCount++; - if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) { - QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute); - if (redirect.isValid()) { - QUrl url = d->sciReply->url().resolved(redirect.toUrl()); - setSource(url); - return; - } - } - d->redirectCount=0; - - if (d->sciReply->error() != QNetworkReply::NoError) { - d->status = Error; - d->sciReply->deleteLater(); - d->sciReply = 0; - emit statusChanged(d->status); - } else { - QSGGridScaledImage sci(d->sciReply); - d->sciReply->deleteLater(); - d->sciReply = 0; - setGridScaledImage(sci); - } -} - -void QSGBorderImage::doUpdate() -{ - update(); -} - -QSGNode *QSGBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - Q_D(QSGBorderImage); - - QSGTexture *texture = d->pix.texture(d->sceneGraphContext()); - - if (!texture || width() <= 0 || height() <= 0) { - delete oldNode; - return 0; - } - - QSGNinePatchNode *node = static_cast(oldNode); - - if (!node) { - node = new QSGNinePatchNode(); - } - - node->setTexture(texture); - - // Don't implicitly create the scalegrid in the rendering thread... - if (d->border) { - const QSGScaleGrid *border = d->getScaleGrid(); - node->setInnerRect(QRectF(border->left(), - border->top(), - qMax(1, d->pix.width() - border->right() - border->left()), - qMax(1, d->pix.height() - border->bottom() - border->top()))); - } else { - node->setInnerRect(QRectF(0, 0, width(), height())); - } - node->setRect(QRectF(0, 0, width(), height())); - node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); - node->setHorzontalTileMode(d->horizontalTileMode); - node->setVerticalTileMode(d->verticalTileMode); - node->setMirror(d->mirror); - node->update(); - - return node; -} - -void QSGBorderImage::pixmapChange() -{ - Q_D(QSGBorderImage); - - d->pixmapChanged = true; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgborderimage_p.h b/src/declarative/items/qsgborderimage_p.h deleted file mode 100644 index c88a61bfd4..0000000000 --- a/src/declarative/items/qsgborderimage_p.h +++ /dev/null @@ -1,110 +0,0 @@ -// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGBORDERIMAGE_P_H -#define QSGBORDERIMAGE_P_H - -#include "qsgimagebase_p.h" - -QT_BEGIN_HEADER -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGScaleGrid; -class QSGGridScaledImage; -class QSGBorderImagePrivate; -class Q_AUTOTEST_EXPORT QSGBorderImage : public QSGImageBase -{ - Q_OBJECT - Q_ENUMS(TileMode) - - Q_PROPERTY(QSGScaleGrid *border READ border CONSTANT) - Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged) - Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged) - // read-only for BorderImage - Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) - -public: - QSGBorderImage(QSGItem *parent=0); - ~QSGBorderImage(); - - QSGScaleGrid *border(); - - enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile }; - - TileMode horizontalTileMode() const; - void setHorizontalTileMode(TileMode); - - TileMode verticalTileMode() const; - void setVerticalTileMode(TileMode); - - void setSource(const QUrl &url); - -Q_SIGNALS: - void horizontalTileModeChanged(); - void verticalTileModeChanged(); - void sourceSizeChanged(); - -protected: - virtual void load(); - virtual void pixmapChange(); - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - -private: - void setGridScaledImage(const QSGGridScaledImage& sci); - -private Q_SLOTS: - void doUpdate(); - void requestFinished(); - void sciRequestFinished(); - -private: - Q_DISABLE_COPY(QSGBorderImage) - Q_DECLARE_PRIVATE(QSGBorderImage) -}; - -QT_END_NAMESPACE -QML_DECLARE_TYPE(QSGBorderImage) -QT_END_HEADER - -#endif // QSGBORDERIMAGE_P_H diff --git a/src/declarative/items/qsgborderimage_p_p.h b/src/declarative/items/qsgborderimage_p_p.h deleted file mode 100644 index 27816697f4..0000000000 --- a/src/declarative/items/qsgborderimage_p_p.h +++ /dev/null @@ -1,103 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGBORDERIMAGE_P_P_H -#define QSGBORDERIMAGE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgimagebase_p_p.h" -#include "qsgscalegrid_p_p.h" - -QT_BEGIN_NAMESPACE - -class QNetworkReply; -class QSGBorderImagePrivate : public QSGImageBasePrivate -{ - Q_DECLARE_PUBLIC(QSGBorderImage) - -public: - QSGBorderImagePrivate() - : border(0), sciReply(0), - horizontalTileMode(QSGBorderImage::Stretch), - verticalTileMode(QSGBorderImage::Stretch), - redirectCount(0), pixmapChanged(false) - { - } - - ~QSGBorderImagePrivate() - { - } - - - QSGScaleGrid *getScaleGrid() - { - Q_Q(QSGBorderImage); - if (!border) { - border = new QSGScaleGrid(q); - FAST_CONNECT(border, SIGNAL(borderChanged()), q, SLOT(doUpdate())) - } - return border; - } - - QSGScaleGrid *border; - QUrl sciurl; - QNetworkReply *sciReply; - QSGBorderImage::TileMode horizontalTileMode; - QSGBorderImage::TileMode verticalTileMode; - int redirectCount; - - bool pixmapChanged : 1; -}; - -QT_END_NAMESPACE - -#endif // QSGBORDERIMAGE_P_P_H diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp deleted file mode 100644 index 1e3562775f..0000000000 --- a/src/declarative/items/qsgcanvas.cpp +++ /dev/null @@ -1,2399 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgcanvas.h" -#include "qsgcanvas_p.h" - -#include "qsgitem.h" -#include "qsgitem_p.h" - -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -#define QSG_CANVAS_TIMING -#ifdef QSG_CANVAS_TIMING -static bool qsg_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty(); -static QTime threadTimer; -static int syncTime; -static int renderTime; -static int swapTime; -#endif - -DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP) -DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP) - -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - -void QSGCanvasPrivate::updateFocusItemTransform() -{ - Q_Q(QSGCanvas); - QSGItem *focus = q->activeFocusItem(); - if (focus && qApp->inputPanel()->inputItem() == focus) - qApp->inputPanel()->setInputItemTransform(QSGItemPrivate::get(focus)->itemToCanvasTransform()); -} - -class QSGCanvasIncubationController : public QObject, public QDeclarativeIncubationController -{ -public: - QSGCanvasIncubationController(QSGCanvasPrivate *canvas) - : m_canvas(canvas), m_eventSent(false) {} - -protected: - virtual bool event(QEvent *e) - { - if (e->type() == QEvent::User) { - Q_ASSERT(m_eventSent); - - bool *amtp = m_canvas->thread->allowMainThreadProcessing(); - while (incubatingObjectCount()) { - if (amtp) - incubateWhile(amtp); - else - incubateFor(5); - QCoreApplication::processEvents(); - } - - m_eventSent = false; - } - return QObject::event(e); - } - - virtual void incubatingObjectCountChanged(int count) - { - if (count && !m_eventSent) { - m_eventSent = true; - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); - } - } - -private: - QSGCanvasPrivate *m_canvas; - bool m_eventSent; -}; - -class QSGCanvasPlainRenderLoop : public QObject, public QSGCanvasRenderLoop -{ -public: - QSGCanvasPlainRenderLoop() - : updatePending(false) - , animationRunning(false) - { - qWarning("QSGCanvas: using non-threaded render loop. Be very sure to not access scene graph " - "objects outside the QSGItem::updatePaintNode() call. Failing to do so will cause " - "your code to crash on other platforms!"); - } - - virtual void paint() { - updatePending = false; - if (animationRunning && animationDriver()) - animationDriver()->advance(); - polishItems(); - syncSceneGraph(); - makeCurrent(); - glViewport(0, 0, size.width(), size.height()); - renderSceneGraph(size); - swapBuffers(); - - if (animationRunning) - maybeUpdate(); - } - - virtual QImage grab() { - return qt_gl_read_framebuffer(size, false, false); - } - - virtual void startRendering() { - if (!glContext()) { - createGLContext(); - makeCurrent(); - initializeSceneGraph(); - } else { - makeCurrent(); - } - maybeUpdate(); - } - - virtual void stopRendering() { } - - virtual void maybeUpdate() { - if (!updatePending) { - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); - updatePending = true; - } - } - - virtual void animationStarted() { - animationRunning = true; - maybeUpdate(); - } - - virtual void animationStopped() { - animationRunning = false; - } - - virtual bool isRunning() const { return glContext(); } // Event loop is always running... - virtual void resize(const QSize &s) { size = s; } - virtual void setWindowSize(const QSize &s) { size = s; } - - bool event(QEvent *e) { - if (e->type() == QEvent::User) { - paint(); - return true; - } - return QObject::event(e); - } - - QSize size; - - uint updatePending : 1; - uint animationRunning : 1; -}; - - - -/* -Focus behavior -============== - -Prior to being added to a valid canvas items can set and clear focus with no -effect. Only once items are added to a canvas (by way of having a parent set that -already belongs to a canvas) do the focus rules apply. Focus goes back to -having no effect if an item is removed from a canvas. - -When an item is moved into a new focus scope (either being added to a canvas -for the first time, or having its parent changed), if the focus scope already has -a scope focused item that takes precedence over the item being added. Otherwise, -the focus of the added tree is used. In the case of of a tree of items being -added to a canvas for the first time, which may have a conflicted focus state (two -or more items in one scope having focus set), the same rule is applied item by item - -thus the first item that has focus will get it (assuming the scope doesn't already -have a scope focused item), and the other items will have their focus cleared. -*/ - -/* - Threaded Rendering - ================== - - The threaded rendering uses a number of different variables to track potential - states used to handle resizing, initial paint, grabbing and driving animations - while ALWAYS keeping the GL context in the rendering thread and keeping the - overhead of normal one-shot paints and vblank driven animations at a minimum. - - Resize, initial show and grab suffer slightly in this model as they are locked - to the rendering in the rendering thread, but this is a necessary evil for - the system to work. - - Variables that are used: - - Private::animationRunning: This is true while the animations are running, and only - written to inside locks. - - RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the - lock. This variable is an integer to allow for recursive calls to lockInGui() - without using a recursive mutex. See isGuiBlockPending. - - RenderThread::isPaintComplete: This variable is cleared when rendering starts and - set once rendering is complete. It is monitored in the paintEvent(), - resizeEvent() and grab() functions to force them to wait for rendering to - complete. - - RenderThread::isGuiBlockPending: This variable is set in the render thread just - before the sync event is sent to the GUI thread. It is used to avoid deadlocks - in the case where render thread waits while waiting for GUI to pick up the sync - event and GUI thread gets a resizeEvent, the initial paintEvent or a grab. - When this happens, we use the - exhaustSyncEvent() function to do the sync right there and mark the coming - sync event to be discarded. There can only ever be one sync incoming. - - RenderThread::isRenderBlock: This variable is true when animations are not - running and the render thread has gone to sleep, waiting for more to do. - - RenderThread::isExternalUpdatePending: This variable is set to false during - the sync phase in the GUI thread and to true in maybeUpdate(). It is an - indication to the render thread that another render pass needs to take - place, rather than the render thread going to sleep after completing its swap. - - RenderThread::doGrab: This variable is set by the grab() function and - tells the renderer to do a grab after rendering is complete and before - swapping happens. - - RenderThread::shouldExit: This variable is used to determine if the render - thread should do a nother pass. It is typically set as a result of show() - and unset as a result of hide() or during shutdown() - - RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown - after shouldExit has been set to true. - */ - -// #define FOCUS_DEBUG -// #define MOUSE_DEBUG -// #define TOUCH_DEBUG -// #define DIRTY_DEBUG -// #define THREAD_DEBUG - -// #define FRAME_TIMING - -#ifdef FRAME_TIMING -static QTime frameTimer; -int sceneGraphRenderTime; -int readbackTime; -#endif - -QSGItem::UpdatePaintNodeData::UpdatePaintNodeData() -: transformNode(0) -{ -} - -QSGRootItem::QSGRootItem() -{ -} - -void QSGCanvas::exposeEvent(QExposeEvent *) -{ - Q_D(QSGCanvas); - d->thread->paint(); -} - -void QSGCanvas::resizeEvent(QResizeEvent *) -{ - Q_D(QSGCanvas); - d->thread->resize(size()); -} - -void QSGCanvas::animationStarted() -{ - d_func()->thread->animationStarted(); -} - -void QSGCanvas::animationStopped() -{ - d_func()->thread->animationStopped(); -} - -void QSGCanvas::showEvent(QShowEvent *) -{ - Q_D(QSGCanvas); - if (d->vsyncAnimations) { - if (!d->animationDriver) { - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection); - connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection); - } - d->animationDriver->install(); - } - - if (!d->thread->isRunning()) { - d->thread->setWindowSize(size()); - d->thread->startRendering(); - } -} - -void QSGCanvas::hideEvent(QHideEvent *) -{ - Q_D(QSGCanvas); - d->thread->stopRendering(); -} - - - -/*! - Sets weither this canvas should use vsync driven animations. - - This option can only be set on one single QSGCanvas, and that it's - vsync signal will then be used to drive all animations in the - process. - - This feature is primarily useful for single QSGCanvas, QML-only - applications. - - \warning Enabling vsync on multiple QSGCanvas instances has - undefined behavior. - */ -void QSGCanvas::setVSyncAnimations(bool enabled) -{ - Q_D(QSGCanvas); - if (visible()) { - qWarning("QSGCanvas::setVSyncAnimations: Cannot be changed when widget is shown"); - return; - } - d->vsyncAnimations = enabled; -} - - - -/*! - Returns true if this canvas should use vsync driven animations; - otherwise returns false. - */ -bool QSGCanvas::vsyncAnimations() const -{ - Q_D(const QSGCanvas); - return d->vsyncAnimations; -} - -void QSGCanvasPrivate::initializeSceneGraph() -{ - if (!context) - context = QSGContext::createDefaultContext(); - - if (context->isReady()) - return; - - QOpenGLContext *glctx = const_cast(QOpenGLContext::currentContext()); - context->initialize(glctx); - - Q_Q(QSGCanvas); - QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()), - Qt::DirectConnection); - - if (!QSGItemPrivate::get(rootItem)->itemNode()->parent()) { - context->rootNode()->appendChildNode(QSGItemPrivate::get(rootItem)->itemNode()); - } - - emit q_func()->sceneGraphInitialized(); -} - -void QSGCanvasPrivate::polishItems() -{ - while (!itemsToPolish.isEmpty()) { - QSet::Iterator iter = itemsToPolish.begin(); - QSGItem *item = *iter; - itemsToPolish.erase(iter); - QSGItemPrivate::get(item)->polishScheduled = false; - item->updatePolish(); - } - updateFocusItemTransform(); -} - - -void QSGCanvasPrivate::syncSceneGraph() -{ - updateDirtyNodes(); -} - - -void QSGCanvasPrivate::renderSceneGraph(const QSize &size) -{ - context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size)); - context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size)); - context->renderer()->setProjectionMatrixToDeviceRect(); - - context->renderNextFrame(renderTarget); - -#ifdef FRAME_TIMING - sceneGraphRenderTime = frameTimer.elapsed(); -#endif - -#ifdef FRAME_TIMING -// int pixel; -// glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); - readbackTime = frameTimer.elapsed(); -#endif -} - - -// ### Do we need this? -void QSGCanvas::sceneGraphChanged() -{ -// Q_D(QSGCanvas); -// d->needsRepaint = true; -} - -QSGCanvasPrivate::QSGCanvasPrivate() - : rootItem(0) - , activeFocusItem(0) - , mouseGrabberItem(0) - , dirtyItemList(0) - , context(0) - , vsyncAnimations(false) - , thread(0) - , animationDriver(0) - , renderTarget(0) - , incubationController(0) -{ -} - -QSGCanvasPrivate::~QSGCanvasPrivate() -{ -} - -void QSGCanvasPrivate::init(QSGCanvas *c) -{ - QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep()); - - q_ptr = c; - - Q_Q(QSGCanvas); - - rootItem = new QSGRootItem; - QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(rootItem); - rootItemPrivate->canvas = q; - rootItemPrivate->flags |= QSGItem::ItemIsFocusScope; - - // QML always has focus. It is important that this call happens after the rootItem - // has a canvas.. - rootItem->setFocus(true); - - bool threaded = !qmlNoThreadedRenderer(); - - if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) { - qWarning("QSGCanvas: platform does not support threaded rendering!"); - threaded = false; - } - - if (threaded) - thread = new QSGCanvasRenderThread(); - else - thread = new QSGCanvasPlainRenderLoop(); - - thread->renderer = q; - thread->d = this; - - context = QSGContext::createDefaultContext(); - thread->moveContextToThread(context); - - q->setSurfaceType(QWindow::OpenGLSurface); - q->setFormat(context->defaultSurfaceFormat()); -} - -void QSGCanvasPrivate::transformTouchPoints(QList &touchPoints, const QTransform &transform) -{ - for (int i=0; isetWidget(q); // ### refactor... - - QList touchPoints = touchEvent->touchPoints(); - for (int i = 0; i < touchPoints.count(); ++i) { - QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; - - touchPoint.setScreenRect(touchPoint.sceneRect()); - touchPoint.setStartScreenPos(touchPoint.startScenePos()); - touchPoint.setLastScreenPos(touchPoint.lastScenePos()); - - touchPoint.setSceneRect(touchPoint.rect()); - touchPoint.setStartScenePos(touchPoint.startPos()); - touchPoint.setLastScenePos(touchPoint.lastPos()); - - if (touchPoint.isPrimary()) - lastMousePosition = touchPoint.pos().toPoint(); - } - touchEvent->setTouchPoints(touchPoints); -} - -void QSGCanvasPrivate::setFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options) -{ - Q_Q(QSGCanvas); - - Q_ASSERT(item); - Q_ASSERT(scope || item == rootItem); - -#ifdef FOCUS_DEBUG - qWarning() << "QSGCanvasPrivate::setFocusInScope():"; - qWarning() << " scope:" << (QObject *)scope; - if (scope) - qWarning() << " scopeSubFocusItem:" << (QObject *)QSGItemPrivate::get(scope)->subFocusItem; - qWarning() << " item:" << (QObject *)item; - qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; -#endif - - QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0; - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - - QSGItem *oldActiveFocusItem = 0; - QSGItem *newActiveFocusItem = 0; - - QVarLengthArray changed; - - // Does this change the active focus? - if (item == rootItem || scopePrivate->activeFocus) { - oldActiveFocusItem = activeFocusItem; - newActiveFocusItem = item; - while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem()) - newActiveFocusItem = newActiveFocusItem->scopedFocusItem(); - - if (oldActiveFocusItem) { -#ifndef QT_NO_IM - qApp->inputPanel()->commit(); -#endif - - activeFocusItem = 0; - QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); - q->sendEvent(oldActiveFocusItem, &event); - - QSGItem *afi = oldActiveFocusItem; - while (afi != scope) { - if (QSGItemPrivate::get(afi)->activeFocus) { - QSGItemPrivate::get(afi)->activeFocus = false; - changed << afi; - } - afi = afi->parentItem(); - } - } - } - - if (item != rootItem) { - QSGItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QSGItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QSGItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - { - scopePrivate->subFocusItem = item; - QSGItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QSGItemPrivate::get(sfi)->subFocusItem = item; - sfi = sfi->parentItem(); - } - } - - if (oldSubFocusItem) { - QSGItemPrivate::get(oldSubFocusItem)->focus = false; - changed << oldSubFocusItem; - } - } - - if (!(options & DontChangeFocusProperty)) { - // if (item != rootItem || q->hasFocus()) { // ### refactor: focus handling... - itemPrivate->focus = true; - changed << item; - // } - } - - if (newActiveFocusItem) { // ### refactor: && q->hasFocus()) { - activeFocusItem = newActiveFocusItem; - - QSGItemPrivate::get(newActiveFocusItem)->activeFocus = true; - changed << newActiveFocusItem; - - QSGItem *afi = newActiveFocusItem->parentItem(); - while (afi && afi != scope) { - if (afi->isFocusScope()) { - QSGItemPrivate::get(afi)->activeFocus = true; - changed << afi; - } - afi = afi->parentItem(); - } - - updateInputMethodData(); - - QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); - q->sendEvent(newActiveFocusItem, &event); - } else { - updateInputMethodData(); - } - - if (!changed.isEmpty()) - notifyFocusChangesRecur(changed.data(), changed.count() - 1); -} - -void QSGCanvasPrivate::clearFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options) -{ - Q_Q(QSGCanvas); - - Q_UNUSED(item); - Q_ASSERT(item); - Q_ASSERT(scope || item == rootItem); - -#ifdef FOCUS_DEBUG - qWarning() << "QSGCanvasPrivate::clearFocusInScope():"; - qWarning() << " scope:" << (QObject *)scope; - qWarning() << " item:" << (QObject *)item; - qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; -#endif - - QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0; - - QSGItem *oldActiveFocusItem = 0; - QSGItem *newActiveFocusItem = 0; - - QVarLengthArray changed; - - Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem); - - // Does this change the active focus? - if (item == rootItem || scopePrivate->activeFocus) { - oldActiveFocusItem = activeFocusItem; - newActiveFocusItem = scope; - - Q_ASSERT(oldActiveFocusItem); - -#ifndef QT_NO_IM - qApp->inputPanel()->commit(); -#endif - - activeFocusItem = 0; - QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); - q->sendEvent(oldActiveFocusItem, &event); - - QSGItem *afi = oldActiveFocusItem; - while (afi != scope) { - if (QSGItemPrivate::get(afi)->activeFocus) { - QSGItemPrivate::get(afi)->activeFocus = false; - changed << afi; - } - afi = afi->parentItem(); - } - } - - if (item != rootItem) { - QSGItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QSGItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QSGItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - scopePrivate->subFocusItem = 0; - - if (oldSubFocusItem && !(options & DontChangeFocusProperty)) { - QSGItemPrivate::get(oldSubFocusItem)->focus = false; - changed << oldSubFocusItem; - } - } else if (!(options & DontChangeFocusProperty)) { - QSGItemPrivate::get(item)->focus = false; - changed << item; - } - - if (newActiveFocusItem) { - Q_ASSERT(newActiveFocusItem == scope); - activeFocusItem = scope; - - updateInputMethodData(); - - QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); - q->sendEvent(newActiveFocusItem, &event); - } else { - updateInputMethodData(); - } - - if (!changed.isEmpty()) - notifyFocusChangesRecur(changed.data(), changed.count() - 1); -} - -void QSGCanvasPrivate::notifyFocusChangesRecur(QSGItem **items, int remaining) -{ - QDeclarativeGuard item(*items); - - if (remaining) - notifyFocusChangesRecur(items + 1, remaining - 1); - - if (item) { - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - - if (itemPrivate->notifiedFocus != itemPrivate->focus) { - itemPrivate->notifiedFocus = itemPrivate->focus; - emit item->focusChanged(itemPrivate->focus); - } - - if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) { - itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus; - itemPrivate->itemChange(QSGItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus); - emit item->activeFocusChanged(itemPrivate->activeFocus); - } - } -} - -void QSGCanvasPrivate::updateInputMethodData() -{ - QSGItem *inputItem = 0; - if (activeFocusItem && activeFocusItem->flags() & QSGItem::ItemAcceptsInputMethod) - inputItem = activeFocusItem; - qApp->inputPanel()->setInputItem(inputItem); -} - -QVariant QSGCanvas::inputMethodQuery(Qt::InputMethodQuery query) const -{ - Q_D(const QSGCanvas); - if (!d->activeFocusItem || !(QSGItemPrivate::get(d->activeFocusItem)->flags & QSGItem::ItemAcceptsInputMethod)) - return QVariant(); - QVariant value = d->activeFocusItem->inputMethodQuery(query); - - //map geometry types - QVariant::Type type = value.type(); - if (type == QVariant::RectF || type == QVariant::Rect) { - const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform(); - value = transform.mapRect(value.toRectF()); - } else if (type == QVariant::PointF || type == QVariant::Point) { - const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform(); - value = transform.map(value.toPointF()); - } - return value; -} - -void QSGCanvasPrivate::dirtyItem(QSGItem *) -{ - Q_Q(QSGCanvas); - q->maybeUpdate(); -} - -void QSGCanvasPrivate::cleanup(QSGNode *n) -{ - Q_Q(QSGCanvas); - - Q_ASSERT(!cleanupNodeList.contains(n)); - cleanupNodeList.append(n); - q->maybeUpdate(); -} - - -QSGCanvas::QSGCanvas(QWindow *parent) - : QWindow(*(new QSGCanvasPrivate), parent) -{ - Q_D(QSGCanvas); - d->init(this); -} - -QSGCanvas::QSGCanvas(QSGCanvasPrivate &dd, QWindow *parent) - : QWindow(dd, parent) -{ - Q_D(QSGCanvas); - d->init(this); -} - -QSGCanvas::~QSGCanvas() -{ - Q_D(QSGCanvas); - - if (d->thread->isRunning()) { - d->thread->stopRendering(); - delete d->thread; - d->thread = 0; - } - - // ### should we change ~QSGItem to handle this better? - // manually cleanup for the root item (item destructor only handles these when an item is parented) - QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(d->rootItem); - rootItemPrivate->removeFromDirtyList(); - - delete d->incubationController; d->incubationController = 0; - - delete d->rootItem; d->rootItem = 0; - d->cleanupNodes(); -} - -QSGItem *QSGCanvas::rootItem() const -{ - Q_D(const QSGCanvas); - - return d->rootItem; -} - -QSGItem *QSGCanvas::activeFocusItem() const -{ - Q_D(const QSGCanvas); - - return d->activeFocusItem; -} - -QSGItem *QSGCanvas::mouseGrabberItem() const -{ - Q_D(const QSGCanvas); - - return d->mouseGrabberItem; -} - - -bool QSGCanvasPrivate::clearHover() -{ - if (hoverItems.isEmpty()) - return false; - - QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos()); - - bool accepted = false; - foreach (QSGItem* item, hoverItems) - accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted; - hoverItems.clear(); - return accepted; -} - - -bool QSGCanvas::event(QEvent *e) -{ - Q_D(QSGCanvas); - - switch (e->type()) { - - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - { - QTouchEvent *touch = static_cast(e); - d->translateTouchEvent(touch); - d->deliverTouchEvent(touch); - if (!touch->isAccepted()) - return false; - break; - } - case QEvent::Leave: - d->clearHover(); - d->lastMousePosition = QPoint(); - break; - case QEvent::DragEnter: - case QEvent::DragLeave: - case QEvent::DragMove: - case QEvent::Drop: - d->deliverDragEvent(&d->dragGrabber, e); - break; - case QEvent::WindowDeactivate: - rootItem()->windowDeactivateEvent(); - break; - default: - break; - } - - return QWindow::event(e); -} - -void QSGCanvas::keyPressEvent(QKeyEvent *e) -{ - Q_D(QSGCanvas); - - if (d->activeFocusItem) - sendEvent(d->activeFocusItem, e); -} - -void QSGCanvas::keyReleaseEvent(QKeyEvent *e) -{ - Q_D(QSGCanvas); - - if (d->activeFocusItem) - sendEvent(d->activeFocusItem, e); -} - -void QSGCanvas::inputMethodEvent(QInputMethodEvent *e) -{ - Q_D(QSGCanvas); - - if (d->activeFocusItem) - sendEvent(d->activeFocusItem, e); -} - -bool QSGCanvasPrivate::deliverInitialMousePressEvent(QSGItem *item, QMouseEvent *event) -{ - Q_Q(QSGCanvas); - - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(event->windowPos()); - if (!QRectF(0, 0, item->width(), item->height()).contains(p)) - return false; - } - - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QSGItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled()) - continue; - if (deliverInitialMousePressEvent(child, event)) - return true; - } - - if (itemPrivate->acceptedMouseButtons & event->button()) { - QPointF p = item->mapFromScene(event->windowPos()); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - me.accept(); - mouseGrabberItem = item; - q->sendEvent(item, &me); - event->setAccepted(me.isAccepted()); - if (me.isAccepted()) - return true; - mouseGrabberItem->ungrabMouse(); - mouseGrabberItem = 0; - } - } - - return false; -} - -bool QSGCanvasPrivate::deliverMouseEvent(QMouseEvent *event) -{ - Q_Q(QSGCanvas); - - lastMousePosition = event->windowPos(); - - if (!mouseGrabberItem && - event->type() == QEvent::MouseButtonPress && - (event->button() & event->buttons()) == event->buttons()) { - return deliverInitialMousePressEvent(rootItem, event); - } - - if (mouseGrabberItem) { - QSGItemPrivate *mgPrivate = QSGItemPrivate::get(mouseGrabberItem); - const QTransform &transform = mgPrivate->canvasToItemTransform(); - QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - me.accept(); - q->sendEvent(mouseGrabberItem, &me); - event->setAccepted(me.isAccepted()); - if (me.isAccepted()) - return true; - } - - return false; -} - -void QSGCanvas::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QSGCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - d->deliverMouseEvent(event); -} - -void QSGCanvas::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QSGCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - if (!d->mouseGrabberItem) { - QWindow::mouseReleaseEvent(event); - return; - } - - d->deliverMouseEvent(event); - d->mouseGrabberItem = 0; -} - -void QSGCanvas::mouseDoubleClickEvent(QMouseEvent *event) -{ - Q_D(QSGCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QSGCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) { - if (d->deliverInitialMousePressEvent(d->rootItem, event)) - event->accept(); - else - event->ignore(); - return; - } - - d->deliverMouseEvent(event); -} - -bool QSGCanvasPrivate::sendHoverEvent(QEvent::Type type, QSGItem *item, - const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool accepted) -{ - Q_Q(QSGCanvas); - const QTransform transform = QSGItemPrivate::get(item)->canvasToItemTransform(); - - //create copy of event - QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers); - hoverEvent.setAccepted(accepted); - - q->sendEvent(item, &hoverEvent); - - return hoverEvent.isAccepted(); -} - -void QSGCanvas::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QSGCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QSGCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - if (!d->mouseGrabberItem) { - if (d->lastMousePosition.isNull()) - d->lastMousePosition = event->windowPos(); - QPointF last = d->lastMousePosition; - d->lastMousePosition = event->windowPos(); - - bool accepted = event->isAccepted(); - bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted); - if (!delivered) { - //take care of any exits - accepted = d->clearHover(); - } - event->setAccepted(accepted); - return; - } - - d->deliverMouseEvent(event); -} - -bool QSGCanvasPrivate::deliverHoverEvent(QSGItem *item, const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool &accepted) -{ - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(scenePos); - if (!QRectF(0, 0, item->width(), item->height()).contains(p)) - return false; - } - - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QSGItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled()) - continue; - if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted)) - return true; - } - - if (itemPrivate->hoverEnabled) { - QPointF p = item->mapFromScene(scenePos); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - if (!hoverItems.isEmpty() && hoverItems[0] == item) { - //move - accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); - } else { - QList itemsToHover; - QSGItem* parent = item; - itemsToHover << item; - while ((parent = parent->parentItem())) - itemsToHover << parent; - - // Leaving from previous hovered items until we reach the item or one of its ancestors. - while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) { - sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted); - hoverItems.removeFirst(); - } - - if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item - // ### Shouldn't we send moves for the parent items as well? - accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); - } else { - // Enter items that are not entered yet. - int startIdx = -1; - if (!hoverItems.isEmpty()) - startIdx = itemsToHover.indexOf(hoverItems[0]) - 1; - if (startIdx == -1) - startIdx = itemsToHover.count() - 1; - - for (int i = startIdx; i >= 0; i--) { - QSGItem *itemToHover = itemsToHover[i]; - if (QSGItemPrivate::get(itemToHover)->hoverEnabled) { - hoverItems.prepend(itemToHover); - sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted); - } - } - } - } - return true; - } - } - - return false; -} - -bool QSGCanvasPrivate::deliverWheelEvent(QSGItem *item, QWheelEvent *event) -{ - Q_Q(QSGCanvas); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(event->posF()); - if (!QRectF(0, 0, item->width(), item->height()).contains(p)) - return false; - } - - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QSGItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled()) - continue; - if (deliverWheelEvent(child, event)) - return true; - } - - QPointF p = item->mapFromScene(event->posF()); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation()); - wheel.accept(); - q->sendEvent(item, &wheel); - if (wheel.isAccepted()) { - event->accept(); - return true; - } - } - - return false; -} - -#ifndef QT_NO_WHEELEVENT -void QSGCanvas::wheelEvent(QWheelEvent *event) -{ - Q_D(QSGCanvas); -#ifdef MOUSE_DEBUG - qWarning() << "QSGCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation(); -#endif - event->ignore(); - d->deliverWheelEvent(d->rootItem, event); -} -#endif // QT_NO_WHEELEVENT - -bool QSGCanvasPrivate::deliverTouchEvent(QTouchEvent *event) -{ -#ifdef TOUCH_DEBUG - if (event->type() == QEvent::TouchBegin) - qWarning("touchBeginEvent"); - else if (event->type() == QEvent::TouchUpdate) - qWarning("touchUpdateEvent"); - else if (event->type() == QEvent::TouchEnd) - qWarning("touchEndEvent"); -#endif - - QHash > updatedPoints; - - if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points - QSet acceptedNewPoints; - deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints); - if (acceptedNewPoints.count() > 0) - event->accept(); - return event->isAccepted(); - } - - const QList &touchPoints = event->touchPoints(); - QList newPoints; - QSGItem *item = 0; - for (int i=0; i 0 || updatedPoints.count() > 0) { - QSet acceptedNewPoints; - int prevCount = updatedPoints.count(); - deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints); - if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount) - event->accept(); - } - - if (event->touchPointStates() & Qt::TouchPointReleased) { - for (int i=0; iisAccepted(); -} - -bool QSGCanvasPrivate::deliverTouchPoints(QSGItem *item, QTouchEvent *event, const QList &newPoints, QSet *acceptedNewPoints, QHash > *updatedPoints) -{ - Q_Q(QSGCanvas); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { - QRectF bounds(0, 0, item->width(), item->height()); - for (int i=0; imapFromScene(newPoints[i].scenePos()); - if (!bounds.contains(p)) - return false; - } - } - - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QSGItem *child = children.at(ii); - if (!child->isEnabled()) - continue; - if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints)) - return true; - } - - QList matchingPoints; - if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { - QRectF bounds(0, 0, item->width(), item->height()); - for (int i=0; icontains(newPoints[i].id())) - continue; - QPointF p = item->mapFromScene(newPoints[i].scenePos()); - if (bounds.contains(p)) - matchingPoints << newPoints[i]; - } - } - - if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) { - QList &eventPoints = (*updatedPoints)[item]; - eventPoints.append(matchingPoints); - transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform()); - - Qt::TouchPointStates eventStates; - for (int i=0; ideviceType()); - touchEvent.setModifiers(event->modifiers()); - touchEvent.setTouchPointStates(eventStates); - touchEvent.setTouchPoints(eventPoints); - touchEvent.setTimestamp(event->timestamp()); - - touchEvent.accept(); - q->sendEvent(item, &touchEvent); - - if (touchEvent.isAccepted()) { - for (int i=0; iinsert(matchingPoints[i].id()); - } - } - } - } - - updatedPoints->remove(item); - if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty()) - return true; - - return false; -} - -void QSGCanvasPrivate::deliverDragEvent(QSGDragGrabber *grabber, QEvent *event) -{ - Q_Q(QSGCanvas); - grabber->resetTarget(); - QSGDragGrabber::iterator grabItem = grabber->begin(); - if (grabItem != grabber->end()) { - Q_ASSERT(event->type() != QEvent::DragEnter); - if (event->type() == QEvent::Drop) { - QDropEvent *e = static_cast(event); - for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { - QPointF p = (**grabItem)->mapFromScene(e->pos()); - QDropEvent translatedEvent( - p.toPoint(), - e->possibleActions(), - e->mimeData(), - e->mouseButtons(), - e->keyboardModifiers()); - QSGDropEventEx::copyActions(&translatedEvent, *e); - q->sendEvent(**grabItem, &translatedEvent); - e->setAccepted(translatedEvent.isAccepted()); - e->setDropAction(translatedEvent.dropAction()); - grabber->setTarget(**grabItem); - } - } - if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave. - QDragLeaveEvent leaveEvent; - for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) - q->sendEvent(**grabItem, &leaveEvent); - return; - } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { - QDragMoveEvent *moveEvent = static_cast(event); - if (deliverDragEvent(grabber, **grabItem, moveEvent)) { - moveEvent->setAccepted(true); - for (++grabItem; grabItem != grabber->end();) { - QPointF p = (**grabItem)->mapFromScene(moveEvent->pos()); - if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) { - QDragMoveEvent translatedEvent( - p.toPoint(), - moveEvent->possibleActions(), - moveEvent->mimeData(), - moveEvent->mouseButtons(), - moveEvent->keyboardModifiers()); - QSGDropEventEx::copyActions(&translatedEvent, *moveEvent); - q->sendEvent(**grabItem, &translatedEvent); - ++grabItem; - } else { - QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); - grabItem = grabber->release(grabItem); - } - } - return; - } else { - QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); - } - } - } - if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) { - QDragMoveEvent *e = static_cast(event); - QDragEnterEvent enterEvent( - e->pos(), - e->possibleActions(), - e->mimeData(), - e->mouseButtons(), - e->keyboardModifiers()); - QSGDropEventEx::copyActions(&enterEvent, *e); - event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent)); - } -} - -bool QSGCanvasPrivate::deliverDragEvent(QSGDragGrabber *grabber, QSGItem *item, QDragMoveEvent *event) -{ - Q_Q(QSGCanvas); - bool accepted = false; - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - if (itemPrivate->opacity == 0.0 || !item->isVisible() || !item->isEnabled()) - return false; - - QPointF p = item->mapFromScene(event->pos()); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - if (event->type() == QEvent::DragMove || itemPrivate->flags & QSGItem::ItemAcceptsDrops) { - QDragMoveEvent translatedEvent( - p.toPoint(), - event->possibleActions(), - event->mimeData(), - event->mouseButtons(), - event->keyboardModifiers(), - event->type()); - QSGDropEventEx::copyActions(&translatedEvent, *event); - q->sendEvent(item, &translatedEvent); - if (event->type() == QEvent::DragEnter) { - if (translatedEvent.isAccepted()) { - grabber->grab(item); - accepted = true; - } - } else { - accepted = true; - } - } - } else if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { - return false; - } - - QDragEnterEvent enterEvent( - event->pos(), - event->possibleActions(), - event->mimeData(), - event->mouseButtons(), - event->keyboardModifiers()); - QSGDropEventEx::copyActions(&enterEvent, *event); - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - if (deliverDragEvent(grabber, children.at(ii), &enterEvent)) - return true; - } - - return accepted; -} - -bool QSGCanvasPrivate::sendFilteredMouseEvent(QSGItem *target, QSGItem *item, QMouseEvent *event) -{ - if (!target) - return false; - - QSGItemPrivate *targetPrivate = QSGItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents) - if (target->childMouseEventFilter(item, event)) - return true; - - if (sendFilteredMouseEvent(target->parentItem(), item, event)) - return true; - - return false; -} - -bool QSGCanvas::sendEvent(QSGItem *item, QEvent *e) -{ - Q_D(QSGCanvas); - - if (!item) { - qWarning("QSGCanvas::sendEvent: Cannot send event to a null item"); - return false; - } - - Q_ASSERT(e); - - switch (e->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: - e->accept(); - QSGItemPrivate::get(item)->deliverKeyEvent(static_cast(e)); - while (!e->isAccepted() && (item = item->parentItem())) { - e->accept(); - QSGItemPrivate::get(item)->deliverKeyEvent(static_cast(e)); - } - break; - case QEvent::InputMethod: - e->accept(); - QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast(e)); - while (!e->isAccepted() && (item = item->parentItem())) { - e->accept(); - QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast(e)); - } - break; - case QEvent::FocusIn: - case QEvent::FocusOut: - QSGItemPrivate::get(item)->deliverFocusEvent(static_cast(e)); - break; - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? - { - QMouseEvent *se = static_cast(e); - if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) { - se->accept(); - QSGItemPrivate::get(item)->deliverMouseEvent(se); - } - } - break; - case QEvent::Wheel: - QSGItemPrivate::get(item)->deliverWheelEvent(static_cast(e)); - break; - case QEvent::HoverEnter: - case QEvent::HoverLeave: - case QEvent::HoverMove: - QSGItemPrivate::get(item)->deliverHoverEvent(static_cast(e)); - break; - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - QSGItemPrivate::get(item)->deliverTouchEvent(static_cast(e)); - break; - case QEvent::DragEnter: - case QEvent::DragMove: - case QEvent::DragLeave: - case QEvent::Drop: - QSGItemPrivate::get(item)->deliverDragEvent(e); - break; - default: - break; - } - - return false; -} - -void QSGCanvasPrivate::cleanupNodes() -{ - for (int ii = 0; ii < cleanupNodeList.count(); ++ii) - delete cleanupNodeList.at(ii); - cleanupNodeList.clear(); -} - -void QSGCanvasPrivate::updateDirtyNodes() -{ -#ifdef DIRTY_DEBUG - qWarning() << "QSGCanvasPrivate::updateDirtyNodes():"; -#endif - - cleanupNodes(); - - QSGItem *updateList = dirtyItemList; - dirtyItemList = 0; - if (updateList) QSGItemPrivate::get(updateList)->prevDirtyItem = &updateList; - - while (updateList) { - QSGItem *item = updateList; - QSGItemPrivate *itemPriv = QSGItemPrivate::get(item); - itemPriv->removeFromDirtyList(); - -#ifdef DIRTY_DEBUG - qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); -#endif - updateDirtyNode(item); - } -} - -void QSGCanvasPrivate::updateDirtyNode(QSGItem *item) -{ -#ifdef QML_RUNTIME_TESTING - bool didFlash = false; -#endif - - QSGItemPrivate *itemPriv = QSGItemPrivate::get(item); - quint32 dirty = itemPriv->dirtyAttributes; - itemPriv->dirtyAttributes = 0; - - if ((dirty & QSGItemPrivate::TransformUpdateMask) || - (dirty & QSGItemPrivate::Size && itemPriv->origin != QSGItem::TopLeft && - (itemPriv->scale != 1. || itemPriv->rotation != 0.))) { - - QMatrix4x4 matrix; - - if (itemPriv->x != 0. || itemPriv->y != 0.) - matrix.translate(itemPriv->x, itemPriv->y); - - for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii) - itemPriv->transforms.at(ii)->applyTo(&matrix); - - if (itemPriv->scale != 1. || itemPriv->rotation != 0.) { - QPointF origin = item->transformOriginPoint(); - matrix.translate(origin.x(), origin.y()); - if (itemPriv->scale != 1.) - matrix.scale(itemPriv->scale, itemPriv->scale); - if (itemPriv->rotation != 0.) - matrix.rotate(itemPriv->rotation, 0, 0, 1); - matrix.translate(-origin.x(), -origin.y()); - } - - itemPriv->itemNode()->setMatrix(matrix); - } - - bool clipEffectivelyChanged = dirty & QSGItemPrivate::Clip && - ((item->clip() == false) != (itemPriv->clipNode == 0)); - bool effectRefEffectivelyChanged = dirty & QSGItemPrivate::EffectReference && - ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0)); - - if (clipEffectivelyChanged) { - QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode(); - QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode; - - if (item->clip()) { - Q_ASSERT(itemPriv->clipNode == 0); - itemPriv->clipNode = new QSGDefaultClipNode(item->boundingRect()); - itemPriv->clipNode->update(); - - if (child) - parent->removeChildNode(child); - parent->appendChildNode(itemPriv->clipNode); - if (child) - itemPriv->clipNode->appendChildNode(child); - - } else { - Q_ASSERT(itemPriv->clipNode != 0); - parent->removeChildNode(itemPriv->clipNode); - if (child) - itemPriv->clipNode->removeChildNode(child); - delete itemPriv->clipNode; - itemPriv->clipNode = 0; - if (child) - parent->appendChildNode(child); - } - } - - if (dirty & QSGItemPrivate::ChildrenUpdateMask) - itemPriv->childContainerNode()->removeAllChildNodes(); - - if (effectRefEffectivelyChanged) { - QSGNode *parent = itemPriv->clipNode; - if (!parent) - parent = itemPriv->opacityNode; - if (!parent) - parent = itemPriv->itemNode(); - QSGNode *child = itemPriv->groupNode; - - if (itemPriv->effectRefCount) { - Q_ASSERT(itemPriv->rootNode == 0); - itemPriv->rootNode = new QSGRootNode; - - if (child) - parent->removeChildNode(child); - parent->appendChildNode(itemPriv->rootNode); - if (child) - itemPriv->rootNode->appendChildNode(child); - } else { - Q_ASSERT(itemPriv->rootNode != 0); - parent->removeChildNode(itemPriv->rootNode); - if (child) - itemPriv->rootNode->removeChildNode(child); - delete itemPriv->rootNode; - itemPriv->rootNode = 0; - if (child) - parent->appendChildNode(child); - } - } - - if (dirty & QSGItemPrivate::ChildrenUpdateMask) { - QSGNode *groupNode = itemPriv->groupNode; - if (groupNode) - groupNode->removeAllChildNodes(); - - QList orderedChildren = itemPriv->paintOrderChildItems(); - int ii = 0; - - for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) { - QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii)); - if (!childPrivate->explicitVisible && !childPrivate->effectRefCount) - continue; - if (childPrivate->itemNode()->parent()) - childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode()); - - itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode()); - } - itemPriv->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0; - - if (itemPriv->paintNode) - itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode); - - for (; ii < orderedChildren.count(); ++ii) { - QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii)); - if (!childPrivate->explicitVisible && !childPrivate->effectRefCount) - continue; - if (childPrivate->itemNode()->parent()) - childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode()); - - itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode()); - } - } - - if ((dirty & QSGItemPrivate::Size) && itemPriv->clipNode) { - itemPriv->clipNode->setRect(item->boundingRect()); - itemPriv->clipNode->update(); - } - - if (dirty & (QSGItemPrivate::OpacityValue | QSGItemPrivate::Visible | QSGItemPrivate::HideReference)) { - qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0 - ? itemPriv->opacity : qreal(0); - - if (opacity != 1 && !itemPriv->opacityNode) { - itemPriv->opacityNode = new QSGOpacityNode; - - QSGNode *parent = itemPriv->itemNode(); - QSGNode *child = itemPriv->clipNode; - if (!child) - child = itemPriv->rootNode; - if (!child) - child = itemPriv->groupNode; - - if (child) - parent->removeChildNode(child); - parent->appendChildNode(itemPriv->opacityNode); - if (child) - itemPriv->opacityNode->appendChildNode(child); - } - if (itemPriv->opacityNode) - itemPriv->opacityNode->setOpacity(opacity); - } - - if (dirty & QSGItemPrivate::ContentUpdateMask) { - - if (itemPriv->flags & QSGItem::ItemHasContents) { - updatePaintNodeData.transformNode = itemPriv->itemNode(); - itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData); - - Q_ASSERT(itemPriv->paintNode == 0 || - itemPriv->paintNode->parent() == 0 || - itemPriv->paintNode->parent() == itemPriv->childContainerNode()); - - if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) { - if (itemPriv->beforePaintNode) - itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode); - else - itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode); - } - } else if (itemPriv->paintNode) { - delete itemPriv->paintNode; - itemPriv->paintNode = 0; - } - } - -#ifndef QT_NO_DEBUG - // Check consistency. - const QSGNode *nodeChain[] = { - itemPriv->itemNodeInstance, - itemPriv->opacityNode, - itemPriv->clipNode, - itemPriv->rootNode, - itemPriv->groupNode, - itemPriv->paintNode, - }; - - int ip = 0; - for (;;) { - while (ip < 5 && nodeChain[ip] == 0) - ++ip; - if (ip == 5) - break; - int ic = ip + 1; - while (ic < 5 && nodeChain[ic] == 0) - ++ic; - const QSGNode *parent = nodeChain[ip]; - const QSGNode *child = nodeChain[ic]; - if (child == 0) { - Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0); - } else { - Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1); - Q_ASSERT(child->parent() == parent); - bool containsChild = false; - for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling()) - containsChild |= (n == child); - Q_ASSERT(containsChild); - } - ip = ic; - } -#endif - -#ifdef QML_RUNTIME_TESTING - if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) { - QSGFlashNode *flash = new QSGFlashNode(); - flash->setRect(item->boundingRect()); - itemPriv->childContainerNode()->appendChildNode(flash); - didFlash = true; - } - Q_Q(QSGCanvas); - if (didFlash) { - q->maybeUpdate(); - } -#endif - -} - -void QSGCanvas::maybeUpdate() -{ - Q_D(QSGCanvas); - - if (d->thread && d->thread->isRunning()) - d->thread->maybeUpdate(); -} - -/*! - \fn void QSGEngine::sceneGraphInitialized(); - - This signal is emitted when the scene graph has been initialized. - - This signal will be emitted from the scene graph rendering thread. - */ - -/*! - Returns the QSGEngine used for this scene. - - The engine will only be available once the scene graph has been - initialized. Register for the sceneGraphEngine() signal to get - notification about this. - */ - -QSGEngine *QSGCanvas::sceneGraphEngine() const -{ - Q_D(const QSGCanvas); - if (d->context && d->context->isReady()) - return d->context->engine(); - return 0; -} - - - -/*! - Sets the render target for this canvas to be \a fbo. - - The specified fbo must be created in the context of the canvas - or one that shares with it. - - \warning - This function can only be called from the thread doing - the rendering. - */ - -void QSGCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo) -{ - Q_D(QSGCanvas); - if (d->context && d->context && QThread::currentThread() != d->context->thread()) { - qWarning("QSGCanvas::setRenderThread: Cannot set render target from outside the rendering thread"); - return; - } - - d->renderTarget = fbo; -} - - - -/*! - Returns the render target for this canvas. - - The default is to render to the surface of the canvas, in which - case the render target is 0. - */ -QOpenGLFramebufferObject *QSGCanvas::renderTarget() const -{ - Q_D(const QSGCanvas); - return d->renderTarget; -} - - -/*! - Grabs the contents of the framebuffer and returns it as an image. - - This function might not work if the view is not visible. - - \warning Calling this function will cause performance problems. - - \warning This function can only be called from the GUI thread. - */ -QImage QSGCanvas::grabFrameBuffer() -{ - Q_D(QSGCanvas); - return d->thread ? d->thread->grab() : QImage(); -} - -/*! - Returns an incubation controller that splices incubation between frames - for this canvas. QSGView automatically installs this controller for you. - - The controller is owned by the canvas and will be destroyed when the canvas - is deleted. -*/ -QDeclarativeIncubationController *QSGCanvas::incubationController() const -{ - Q_D(const QSGCanvas); - - if (!d->incubationController) - d->incubationController = new QSGCanvasIncubationController(const_cast(d)); - return d->incubationController; -} - - -void QSGCanvasRenderLoop::createGLContext() -{ - gl = new QOpenGLContext(); - gl->setFormat(renderer->requestedFormat()); - gl->create(); -} - -void QSGCanvasRenderThread::run() -{ -#ifdef THREAD_DEBUG - qDebug("QML Rendering Thread Started"); -#endif - - if (!glContext()) { - createGLContext(); - makeCurrent(); - initializeSceneGraph(); - } else { - makeCurrent(); - } - - while (!shouldExit) { - lock(); - - bool sizeChanged = false; - isExternalUpdatePending = false; - - if (renderedSize != windowSize) { -#ifdef THREAD_DEBUG - printf(" RenderThread: window has changed size...\n"); -#endif - glViewport(0, 0, windowSize.width(), windowSize.height()); - sizeChanged = true; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: preparing to sync...\n"); -#endif - - if (!isGuiBlocked) { - isGuiBlockPending = true; - -#ifdef THREAD_DEBUG - printf(" RenderThread: aquired sync lock...\n"); -#endif - allowMainThreadProcessingFlag = false; - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); -#ifdef THREAD_DEBUG - printf(" RenderThread: going to sleep...\n"); -#endif - wait(); - - isGuiBlockPending = false; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: Doing locked sync\n"); -#endif -#ifdef QSG_CANVAS_TIMING - if (qsg_canvas_timing) - threadTimer.start(); -#endif - inSync = true; - syncSceneGraph(); - inSync = false; - - // Wake GUI after sync to let it continue animating and event processing. - allowMainThreadProcessingFlag = true; - wake(); - unlock(); -#ifdef THREAD_DEBUG - printf(" RenderThread: sync done\n"); -#endif -#ifdef QSG_CANVAS_TIMING - if (qsg_canvas_timing) - syncTime = threadTimer.elapsed(); -#endif - -#ifdef THREAD_DEBUG - printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height()); -#endif - - renderSceneGraph(windowSize); -#ifdef QSG_CANVAS_TIMING - if (qsg_canvas_timing) - renderTime = threadTimer.elapsed() - syncTime; -#endif - - // The content of the target buffer is undefined after swap() so grab needs - // to happen before swap(); - if (doGrab) { -#ifdef THREAD_DEBUG - printf(" RenderThread: doing a grab...\n"); -#endif - grabContent = qt_gl_read_framebuffer(windowSize, false, false); - doGrab = false; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: wait for swap...\n"); -#endif - - swapBuffers(); -#ifdef THREAD_DEBUG - printf(" RenderThread: swap complete...\n"); -#endif -#ifdef QSG_CANVAS_TIMING - if (qsg_canvas_timing) { - swapTime = threadTimer.elapsed() - renderTime; - qDebug() << "- Breakdown of frame time: sync:" << syncTime - << "ms render:" << renderTime << "ms swap:" << swapTime - << "ms total:" << swapTime + renderTime << "ms"; - } -#endif - - lock(); - isPaintCompleted = true; - if (sizeChanged) - renderedSize = windowSize; - - // Wake the GUI thread now that rendering is complete, to signal that painting - // is done, resizing is done or grabbing is completed. For grabbing, we're - // signalling this much later than needed (we could have done it before swap) - // but we don't want to lock an extra time. - wake(); - - if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) { -#ifdef THREAD_DEBUG - printf(" RenderThread: nothing to do, going to sleep...\n"); -#endif - isRenderBlocked = true; - wait(); - isRenderBlocked = false; - } - - unlock(); - - // Process any "deleteLater" objects... - QCoreApplication::processEvents(); - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: render loop exited... Good Night!\n"); -#endif - - doneCurrent(); - - lock(); - hasExited = true; -#ifdef THREAD_DEBUG - printf(" RenderThread: waking GUI for final sleep..\n"); -#endif - wake(); - unlock(); - -#ifdef THREAD_DEBUG - printf(" RenderThread: All done...\n"); -#endif -} - - - -bool QSGCanvasRenderThread::event(QEvent *e) -{ - Q_ASSERT(QThread::currentThread() == qApp->thread()); - - if (e->type() == QEvent::User) { - if (!syncAlreadyHappened) - sync(false); - - syncAlreadyHappened = false; - - if (animationRunning && animationDriver()) { -#ifdef THREAD_DEBUG - qDebug("GUI: Advancing animations...\n"); -#endif - - animationDriver()->advance(); - -#ifdef THREAD_DEBUG - qDebug("GUI: Animations advanced...\n"); -#endif - } - - return true; - } - - return QThread::event(e); -} - - - -void QSGCanvasRenderThread::exhaustSyncEvent() -{ - if (isGuiBlockPending) { - sync(true); - syncAlreadyHappened = true; - } -} - - - -void QSGCanvasRenderThread::sync(bool guiAlreadyLocked) -{ -#ifdef THREAD_DEBUG - printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event"); -#endif - if (!guiAlreadyLocked) - lockInGui(); - - renderThreadAwakened = false; - - polishItems(); - - wake(); - wait(); - - if (!guiAlreadyLocked) - unlockInGui(); -} - - - - -/*! - Acquires the mutex for the GUI thread. The function uses the isGuiBlocked - variable to keep track of how many recursion levels the gui is locket with. - We only actually acquire the mutex for the first level to avoid deadlocking - ourselves. - */ - -void QSGCanvasRenderThread::lockInGui() -{ - // We must avoid recursive locking in the GUI thread, hence we - // only lock when we are the first one to try to block. - if (!isGuiBlocked) - lock(); - - isGuiBlocked++; - -#ifdef THREAD_DEBUG - printf("GUI: aquired lock... %d\n", isGuiBlocked); -#endif -} - - - -void QSGCanvasRenderThread::unlockInGui() -{ -#ifdef THREAD_DEBUG - printf("GUI: releasing lock... %d\n", isGuiBlocked); -#endif - --isGuiBlocked; - if (!isGuiBlocked) - unlock(); -} - - - - -void QSGCanvasRenderThread::animationStarted() -{ -#ifdef THREAD_DEBUG - printf("GUI: animationStarted()\n"); -#endif - - lockInGui(); - - animationRunning = true; - - if (isRenderBlocked) - wake(); - - unlockInGui(); -} - - - -void QSGCanvasRenderThread::animationStopped() -{ -#ifdef THREAD_DEBUG - printf("GUI: animationStopped()...\n"); -#endif - - lockInGui(); - animationRunning = false; - unlockInGui(); -} - - -void QSGCanvasRenderThread::paint() -{ -#ifdef THREAD_DEBUG - printf("GUI: paint called..\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - - isPaintCompleted = false; - while (isRunning() && !isPaintCompleted) { - if (isRenderBlocked) - wake(); - wait(); - } - unlockInGui(); -} - - - -void QSGCanvasRenderThread::resize(const QSize &size) -{ -#ifdef THREAD_DEBUG - printf("GUI: Resize Event: %dx%d\n", size.width(), size.height()); -#endif - - if (!isRunning()) { - windowSize = size; - return; - } - - lockInGui(); - exhaustSyncEvent(); - - windowSize = size; - - while (isRunning() && renderedSize != windowSize) { - if (isRenderBlocked) - wake(); - wait(); - } - unlockInGui(); -} - - - -void QSGCanvasRenderThread::startRendering() -{ -#ifdef THREAD_DEBUG - printf("GUI: Starting Render Thread\n"); -#endif - hasExited = false; - shouldExit = false; - isGuiBlocked = 0; - isGuiBlockPending = false; - start(); -} - - - -void QSGCanvasRenderThread::stopRendering() -{ -#ifdef THREAD_DEBUG - printf("GUI: stopping render thread\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - shouldExit = true; - - if (isRenderBlocked) { -#ifdef THREAD_DEBUG - printf("GUI: waking up render thread\n"); -#endif - wake(); - } - - while (!hasExited) { -#ifdef THREAD_DEBUG - printf("GUI: waiting for render thread to have exited..\n"); -#endif - wait(); - } - - unlockInGui(); - -#ifdef THREAD_DEBUG - printf("GUI: waiting for render thread to terminate..\n"); -#endif - // Actually wait for the thread to terminate. Otherwise we can delete it - // too early and crash. - QThread::wait(); - -#ifdef THREAD_DEBUG - printf("GUI: thread has terminated and we're all good..\n"); -#endif - -} - - - -QImage QSGCanvasRenderThread::grab() -{ - if (!isRunning()) - return QImage(); - - if (QThread::currentThread() != qApp->thread()) { - qWarning("QSGCanvas::grabFrameBuffer: can only be called from the GUI thread"); - return QImage(); - } - -#ifdef THREAD_DEBUG - printf("GUI: doing a pixelwise grab..\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - - doGrab = true; - isPaintCompleted = false; - while (isRunning() && !isPaintCompleted) { - if (isRenderBlocked) - wake(); - wait(); - } - - QImage grabbed = grabContent; - grabContent = QImage(); - - unlockInGui(); - - return grabbed; -} - - - -void QSGCanvasRenderThread::maybeUpdate() -{ - Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync, - "QSGCanvas::update", - "Function can only be called from GUI thread or during QSGItem::updatePaintNode()"); - - if (inSync) { - isExternalUpdatePending = true; - - } else if (!renderThreadAwakened) { -#ifdef THREAD_DEBUG - printf("GUI: doing update...\n"); -#endif - renderThreadAwakened = true; - lockInGui(); - isExternalUpdatePending = true; - if (isRenderBlocked) - wake(); - unlockInGui(); - } -} - - -#include "moc_qsgcanvas.cpp" - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgcanvas.h b/src/declarative/items/qsgcanvas.h deleted file mode 100644 index 094fe40553..0000000000 --- a/src/declarative/items/qsgcanvas.h +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCANVAS_H -#define QSGCANVAS_H - -#include -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGItem; -class QSGEngine; -class QSGCanvasPrivate; -class QOpenGLFramebufferObject; -class QDeclarativeIncubationController; - -class Q_DECLARATIVE_EXPORT QSGCanvas : public QWindow -{ -Q_OBJECT -Q_DECLARE_PRIVATE(QSGCanvas) -public: - QSGCanvas(QWindow *parent = 0); - - virtual ~QSGCanvas(); - - QSGItem *rootItem() const; - QSGItem *activeFocusItem() const; - - QSGItem *mouseGrabberItem() const; - - bool sendEvent(QSGItem *, QEvent *); - - QVariant inputMethodQuery(Qt::InputMethodQuery query) const; - - QSGEngine *sceneGraphEngine() const; - - void setVSyncAnimations(bool enabled); - bool vsyncAnimations() const; - - QImage grabFrameBuffer(); - - void setRenderTarget(QOpenGLFramebufferObject *fbo); - QOpenGLFramebufferObject *renderTarget() const; - - QDeclarativeIncubationController *incubationController() const; - -Q_SIGNALS: - void frameSwapped(); - void sceneGraphInitialized(); - -protected: - QSGCanvas(QSGCanvasPrivate &dd, QWindow *parent = 0); - - virtual void exposeEvent(QExposeEvent *); - virtual void resizeEvent(QResizeEvent *); - - virtual void showEvent(QShowEvent *); - virtual void hideEvent(QHideEvent *); - - virtual bool event(QEvent *); - virtual void keyPressEvent(QKeyEvent *); - virtual void keyReleaseEvent(QKeyEvent *); - virtual void inputMethodEvent(QInputMethodEvent *); - virtual void mousePressEvent(QMouseEvent *); - virtual void mouseReleaseEvent(QMouseEvent *); - virtual void mouseDoubleClickEvent(QMouseEvent *); - virtual void mouseMoveEvent(QMouseEvent *); -#ifndef QT_NO_WHEELEVENT - virtual void wheelEvent(QWheelEvent *); -#endif - -private Q_SLOTS: - void sceneGraphChanged(); - void maybeUpdate(); - void animationStarted(); - void animationStopped(); - -private: - friend class QSGItem; - friend class QSGCanvasRenderLoop; - Q_DISABLE_COPY(QSGCanvas) -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QSGCanvas *) - -QT_END_HEADER - -#endif // QSGCANVAS_H - diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h deleted file mode 100644 index e2ec7a1963..0000000000 --- a/src/declarative/items/qsgcanvas_p.h +++ /dev/null @@ -1,308 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCANVAS_P_H -#define QSGCANVAS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgitem.h" -#include "qsgcanvas.h" -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -//Make it easy to identify and customize the root item if needed -class QSGRootItem : public QSGItem -{ - Q_OBJECT -public: - QSGRootItem(); -}; - -class QSGCanvasPrivate; - -class QTouchEvent; -class QSGCanvasRenderLoop; -class QSGCanvasIncubationController; - -class QSGCanvasPrivate : public QWindowPrivate -{ -public: - Q_DECLARE_PUBLIC(QSGCanvas) - - static inline QSGCanvasPrivate *get(QSGCanvas *c) { return c->d_func(); } - - QSGCanvasPrivate(); - virtual ~QSGCanvasPrivate(); - - void init(QSGCanvas *); - - QSGRootItem *rootItem; - - QSGItem *activeFocusItem; - QSGItem *mouseGrabberItem; - QSGDragGrabber dragGrabber; - - // Mouse positions are saved in widget coordinates - QPointF lastMousePosition; - void translateTouchEvent(QTouchEvent *touchEvent); - static void transformTouchPoints(QList &touchPoints, const QTransform &transform); - bool deliverInitialMousePressEvent(QSGItem *, QMouseEvent *); - bool deliverMouseEvent(QMouseEvent *); - bool sendFilteredMouseEvent(QSGItem *, QSGItem *, QMouseEvent *); - bool deliverWheelEvent(QSGItem *, QWheelEvent *); - bool deliverTouchPoints(QSGItem *, QTouchEvent *, const QList &, QSet *, - QHash > *); - bool deliverTouchEvent(QTouchEvent *); - bool deliverHoverEvent(QSGItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); - bool sendHoverEvent(QEvent::Type, QSGItem *, const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool accepted); - bool clearHover(); - void deliverDragEvent(QSGDragGrabber *, QEvent *); - bool deliverDragEvent(QSGDragGrabber *, QSGItem *, QDragMoveEvent *); - - QList hoverItems; - enum FocusOption { - DontChangeFocusProperty = 0x01, - }; - Q_DECLARE_FLAGS(FocusOptions, FocusOption) - - void setFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions = 0); - void clearFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions = 0); - void notifyFocusChangesRecur(QSGItem **item, int remaining); - - void updateInputMethodData(); - void updateFocusItemTransform(); - - void dirtyItem(QSGItem *); - void cleanup(QSGNode *); - - void initializeSceneGraph(); - void polishItems(); - void syncSceneGraph(); - void renderSceneGraph(const QSize &size); - - QSGItem::UpdatePaintNodeData updatePaintNodeData; - - QSGItem *dirtyItemList; - QList cleanupNodeList; - - QSet itemsToPolish; - - void updateDirtyNodes(); - void cleanupNodes(); - bool updateEffectiveOpacity(QSGItem *); - void updateEffectiveOpacityRoot(QSGItem *, qreal); - void updateDirtyNode(QSGItem *); - - QSGContext *context; - - uint vsyncAnimations : 1; - - QSGCanvasRenderLoop *thread; - QSize widgetSize; - QSize viewportSize; - - QAnimationDriver *animationDriver; - - QOpenGLFramebufferObject *renderTarget; - - QHash itemForTouchPointId; - - mutable QSGCanvasIncubationController *incubationController; -}; - -class QSGCanvasRenderLoop -{ -public: - QSGCanvasRenderLoop() - : d(0) - , renderer(0) - , gl(0) - { - } - virtual ~QSGCanvasRenderLoop() - { - delete gl; - } - - friend class QSGCanvasPrivate; - - virtual void paint() = 0; - virtual void resize(const QSize &size) = 0; - virtual void startRendering() = 0; - virtual void stopRendering() = 0; - virtual QImage grab() = 0; - virtual void setWindowSize(const QSize &size) = 0; - virtual void maybeUpdate() = 0; - virtual bool isRunning() const = 0; - virtual void animationStarted() = 0; - virtual void animationStopped() = 0; - virtual void moveContextToThread(QSGContext *) { } - virtual bool *allowMainThreadProcessing() { return 0; } - -protected: - void initializeSceneGraph() { d->initializeSceneGraph(); } - void syncSceneGraph() { d->syncSceneGraph(); } - void renderSceneGraph(const QSize &size) { d->renderSceneGraph(size); } - void polishItems() { d->polishItems(); } - QAnimationDriver *animationDriver() const { return d->animationDriver; } - - inline QOpenGLContext *glContext() const { return gl; } - void createGLContext(); - void makeCurrent() { gl->makeCurrent(renderer); } - void doneCurrent() { gl->doneCurrent(); } - void swapBuffers() { - gl->swapBuffers(renderer); - emit renderer->frameSwapped(); - } - -private: - QSGCanvasPrivate *d; - QSGCanvas *renderer; - - QOpenGLContext *gl; -}; - -class QSGCanvasRenderThread : public QThread, public QSGCanvasRenderLoop -{ - Q_OBJECT -public: - QSGCanvasRenderThread() - : mutex(QMutex::NonRecursive) - , allowMainThreadProcessingFlag(true) - , animationRunning(false) - , isGuiBlocked(0) - , isPaintCompleted(false) - , isGuiBlockPending(false) - , isRenderBlocked(false) - , isExternalUpdatePending(false) - , syncAlreadyHappened(false) - , inSync(false) - , doGrab(false) - , shouldExit(false) - , hasExited(false) - , renderThreadAwakened(false) - {} - - inline void lock() { mutex.lock(); } - inline void unlock() { mutex.unlock(); } - inline void wait() { condition.wait(&mutex); } - inline void wake() { condition.wakeOne(); } - - void lockInGui(); - void unlockInGui(); - - void paint(); - void resize(const QSize &size); - void startRendering(); - void stopRendering(); - void exhaustSyncEvent(); - void sync(bool guiAlreadyLocked); - bool isRunning() const { return QThread::isRunning(); } - void setWindowSize(const QSize &size) { windowSize = size; } - void maybeUpdate(); - void moveContextToThread(QSGContext *c) { c->moveToThread(this); } - bool *allowMainThreadProcessing() { return &allowMainThreadProcessingFlag; } - - bool event(QEvent *); - - QImage grab(); - -public slots: - void animationStarted(); - void animationStopped(); - -public: - QMutex mutex; - QWaitCondition condition; - - bool allowMainThreadProcessingFlag; - - QSize windowSize; - QSize renderedSize; - - uint animationRunning: 1; - int isGuiBlocked; - uint isPaintCompleted : 1; - uint isGuiBlockPending : 1; - uint isRenderBlocked : 1; - uint isExternalUpdatePending : 1; - uint syncAlreadyHappened : 1; - uint inSync : 1; - uint doGrab : 1; - uint shouldExit : 1; - uint hasExited : 1; - uint renderThreadAwakened : 1; - - QImage grabContent; - - void run(); -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QSGCanvasPrivate::FocusOptions) - -QT_END_NAMESPACE - -#endif // QSGCANVAS_P_H diff --git a/src/declarative/items/qsgclipnode.cpp b/src/declarative/items/qsgclipnode.cpp deleted file mode 100644 index 1147636a17..0000000000 --- a/src/declarative/items/qsgclipnode.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qsgclipnode_p.h" - -#include -#include - -QSGDefaultClipNode::QSGDefaultClipNode(const QRectF &rect) - : m_rect(rect) - , m_radius(0) - , m_dirty_geometry(true) - , m_geometry(QSGGeometry::defaultAttributes_Point2D(), 0) -{ - setGeometry(&m_geometry); - setIsRectangular(true); -} - -void QSGDefaultClipNode::setRect(const QRectF &rect) -{ - m_rect = rect; - m_dirty_geometry = true; -} - -void QSGDefaultClipNode::setRadius(qreal radius) -{ - m_radius = radius; - m_dirty_geometry = true; - setIsRectangular(radius == 0); -} - -void QSGDefaultClipNode::update() -{ - if (m_dirty_geometry) { - updateGeometry(); - m_dirty_geometry = false; - } -} - -void QSGDefaultClipNode::updateGeometry() -{ - QSGGeometry *g = geometry(); - - if (qFuzzyIsNull(m_radius)) { - g->allocate(4); - QSGGeometry::updateRectGeometry(g, m_rect); - - } else { - int vertexCount = 0; - - // Radius should never exceeds half of the width or half of the height - qreal radius = qMin(qMin(m_rect.width() / 2, m_rect.height() / 2), m_radius); - QRectF rect = m_rect; - rect.adjust(radius, radius, -radius, -radius); - - int segments = qMin(30, qCeil(radius)); // Number of segments per corner. - - g->allocate((segments + 1) * 2); - - QVector2D *vertices = (QVector2D *)g->vertexData(); - - for (int part = 0; part < 2; ++part) { - for (int i = 0; i <= segments; ++i) { - //### Should change to calculate sin/cos only once. - qreal angle = qreal(0.5 * M_PI) * (part + i / qreal(segments)); - qreal s = qFastSin(angle); - qreal c = qFastCos(angle); - qreal y = (part ? rect.bottom() : rect.top()) - radius * c; // current inner y-coordinate. - qreal lx = rect.left() - radius * s; // current inner left x-coordinate. - qreal rx = rect.right() + radius * s; // current inner right x-coordinate. - - vertices[vertexCount++] = QVector2D(rx, y); - vertices[vertexCount++] = QVector2D(lx, y); - } - } - - markDirty(DirtyGeometry); - } - setClipRect(m_rect); -} - diff --git a/src/declarative/items/qsgclipnode_p.h b/src/declarative/items/qsgclipnode_p.h deleted file mode 100644 index 0e6d057204..0000000000 --- a/src/declarative/items/qsgclipnode_p.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGCLIPNODE_P_H -#define QSGCLIPNODE_P_H - -#include - -class QSGDefaultClipNode : public QSGClipNode -{ -public: - QSGDefaultClipNode(const QRectF &); - - void setRect(const QRectF &); - QRectF rect() const { return m_rect; } - - void setRadius(qreal radius); - qreal radius() const { return m_radius; } - - virtual void update(); - -private: - void updateGeometry(); - QRectF m_rect; - qreal m_radius; - - uint m_dirty_geometry : 1; - uint m_reserved : 31; - - QSGGeometry m_geometry; -}; - -#endif // QSGCLIPNODE_P_H diff --git a/src/declarative/items/qsgdrag.cpp b/src/declarative/items/qsgdrag.cpp deleted file mode 100644 index b95e495477..0000000000 --- a/src/declarative/items/qsgdrag.cpp +++ /dev/null @@ -1,462 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgdrag_p.h" - -#include -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class QSGDragAttachedPrivate : public QObjectPrivate, public QSGItemChangeListener -{ - Q_DECLARE_PUBLIC(QSGDragAttached) -public: - static QSGDragAttachedPrivate *get(QSGDragAttached *attached) { - return static_cast(QObjectPrivate::get(attached)); } - - QSGDragAttachedPrivate() - : attachedItem(0) - , mimeData(0) - , proposedAction(Qt::MoveAction) - , supportedActions(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction) - , active(false) - , listening(false) - { - } - - void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &); - void start() { start(supportedActions); } - void start(Qt::DropActions supportedActions); - void setTarget(QSGItem *item); - - QSGDragGrabber dragGrabber; - - QDeclarativeGuard source; - QDeclarativeGuard target; - QSGItem *attachedItem; - QSGDragMimeData *mimeData; - Qt::DropAction proposedAction; - Qt::DropActions supportedActions; - bool active : 1; - bool listening : 1; - QPointF hotSpot; - QStringList keys; -}; - -/*! - \qmlclass Drag QSGDrag - \inqmlmodule QtQuick 2 - \brief The Drag attached property provides drag and drop events for moved Items. - - Using the Drag attached property any Item can made a source of drag and drop - events within a scene. - - When a drag is \l active on an item any change in that items position will - generate a drag events that will be sent to any DropArea that intersects - the with new position of the item. Other items which implement drag and - drop event handlers can also receive these events. - - The following snippet shows how an item can be dragged with a MouseArea. - However, dragging is not limited to mouse drags, anything that can move an item - can generate drag events, this can include touch events, animations and bindings. - - \snippet doc/src/snippets/declarative/drag.qml 0 - - A drag can be terminated either by cancelling it with Drag.cancel() or setting - Drag.active to false, or it can be terminated with a drop event by calling - Drag.drop(). If the drop event is accepted Drag.drop() will return the - \l {supportedActions}{drop action} chosen by the recipient of the event, - otherwise it will return Qt.IgnoreAction. - -*/ - -void QSGDragAttachedPrivate::itemGeometryChanged(QSGItem *, const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_Q(QSGDragAttached); - if (newGeometry.topLeft() == oldGeometry.topLeft() || !active) - return; - - if (QSGCanvas *canvas = attachedItem->canvas()) { - QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint(); - QDragMoveEvent event(scenePos, mimeData->m_supportedActions, mimeData, Qt::NoButton, Qt::NoModifier); - QSGDropEventEx::setProposedAction(&event, proposedAction); - QSGCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, &event); - if (target != dragGrabber.target()) { - target = dragGrabber.target(); - emit q->targetChanged(); - } - } -} - -QSGDragAttached::QSGDragAttached(QObject *parent) - : QObject(*new QSGDragAttachedPrivate, parent) -{ - Q_D(QSGDragAttached); - d->attachedItem = qobject_cast(parent); - d->source = d->attachedItem; -} - -QSGDragAttached::~QSGDragAttached() -{ - Q_D(QSGDragAttached); - delete d->mimeData; -} - -/*! - \qmlattachedproperty bool QtQuick2::Drag::active - - This property holds whether a drag event sequence is currently active. - - Setting this property to true will send a QDragEnter event to the scene - with the item's current position. Setting it to false will send a - QDragLeave event. - - While a drag is active any change in an item's position will send a QDragMove - event with item's new position to the scene. -*/ - -bool QSGDragAttached::isActive() const -{ - Q_D(const QSGDragAttached); - return d->active; -} - -void QSGDragAttached::setActive(bool active) -{ - Q_D(QSGDragAttached); - if (d->active != active) { - if (active) - d->start(d->supportedActions); - else - cancel(); - } -} - -/*! - \qmlattachedproperty Object QtQuick2::Drag::source - - This property holds an object that is identified to recipients of drag events as - the source of the events. By default this is the item Drag property is attached to. - - Changes to source while a Drag is active don't take effect until a new drag is started. -*/ - -QObject *QSGDragAttached::source() const -{ - Q_D(const QSGDragAttached); - return d->source; -} - -void QSGDragAttached::setSource(QObject *item) -{ - Q_D(QSGDragAttached); - if (d->source != item) { - d->source = item; - emit sourceChanged(); - } -} - -void QSGDragAttached::resetSource() -{ - Q_D(QSGDragAttached); - if (d->source != d->attachedItem) { - d->source = d->attachedItem; - emit sourceChanged(); - } -} - -/*! - \qmlattachedproperty Object QtQuick2::Drag::target - - While a drag is active this property holds the last object to accept an - enter event from the dragged item, if the current drag position doesn't - intersect any accepting targets it is null. - - When a drag is not active this property holds the object that accepted - the drop event that ended the drag, if no object accepted the drop or - the drag was cancelled the target will then be null. -*/ - -QObject *QSGDragAttached::target() const -{ - Q_D(const QSGDragAttached); - return d->target; -} - -/*! - \qmlattachedproperty QPointF QtQuick2::Drag::hotSpot - - This property holds the drag position relative to the top left of the item. - - By default this is (0, 0). - - Changes to hotSpot will take effect when the next event is sent. -*/ - -QPointF QSGDragAttached::hotSpot() const -{ - Q_D(const QSGDragAttached); - return d->hotSpot; -} - -void QSGDragAttached::setHotSpot(const QPointF &hotSpot) -{ - Q_D(QSGDragAttached); - if (d->hotSpot != hotSpot) { - d->hotSpot = hotSpot; - emit hotSpotChanged(); - // Send a move event if active? - } -} - -/*! - \qmlattachedproperty stringlist QtQuick2::Drag::keys - - This property holds a list of keys that can be used by a DropArea to filter drag events. - - Changes to keys while a Drag is active don't take effect until a new drag is started. -*/ - -QStringList QSGDragAttached::keys() const -{ - Q_D(const QSGDragAttached); - return d->keys; -} - -void QSGDragAttached::setKeys(const QStringList &keys) -{ - Q_D(QSGDragAttached); - if (d->keys != keys) { - d->keys = keys; - emit keysChanged(); - } -} - -/*! - \qmlattachedproperty flags QtQuick2::Drag::supportedActions - - This property holds return values of Drag.drop() supported by the drag source. - - Changes to supportedActions while a Drag is active don't take effect - until a new drag is started. -*/ - -Qt::DropActions QSGDragAttached::supportedActions() const -{ - Q_D(const QSGDragAttached); - return d->supportedActions; -} - -void QSGDragAttached::setSupportedActions(Qt::DropActions actions) -{ - Q_D(QSGDragAttached); - if (d->supportedActions != actions) { - d->supportedActions = actions; - emit supportedActionsChanged(); - } -} - -/*! - \qmlattachedproperty enumeration QtQuick2::Drag::proposedAction - - This property holds an action that is recommended by the drag source as a - return value from Drag.drop(). - - Changes to proposedAction will take effect when the next event is sent. -*/ - -Qt::DropAction QSGDragAttached::proposedAction() const -{ - Q_D(const QSGDragAttached); - return d->proposedAction; -} - -void QSGDragAttached::setProposedAction(Qt::DropAction action) -{ - Q_D(QSGDragAttached); - if (d->proposedAction != action) { - d->proposedAction = action; - emit proposedActionChanged(); - // send a move event with the new default action if active? - } -} - -void QSGDragAttachedPrivate::start(Qt::DropActions supportedActions) -{ - Q_Q(QSGDragAttached); - Q_ASSERT(!active); - - if (QSGCanvas *canvas = attachedItem ? attachedItem->canvas() : 0) { - if (!mimeData) - mimeData = new QSGDragMimeData; - if (!listening) { - QSGItemPrivate::get(attachedItem)->addItemChangeListener(this, QSGItemPrivate::Geometry); - listening = true; - } - - mimeData->m_source = source; - mimeData->m_supportedActions = supportedActions; - mimeData->m_keys = keys; - active = true; - - QPoint scenePos = attachedItem->mapToScene(hotSpot).toPoint(); - QDragEnterEvent event(scenePos, supportedActions, mimeData, Qt::NoButton, Qt::NoModifier); - QSGDropEventEx::setProposedAction(&event, proposedAction); - QSGCanvasPrivate::get(canvas)->deliverDragEvent(&dragGrabber, &event); - - emit q->activeChanged(); - if (target != dragGrabber.target()) { - target = dragGrabber.target(); - emit q->targetChanged(); - } - } -} - -/*! - \qmlattachedmethod void QtQuick2::Drag::start(flags supportedActions) - - Starts sending drag events. - - The optional \a supportedActions argument can be used to override the \l supportedActions - property for the started sequence. -*/ - -void QSGDragAttached::start(QDeclarativeV8Function *args) -{ - Q_D(QSGDragAttached); - if (d->active) - cancel(); - - Qt::DropActions supportedActions = d->supportedActions; - // check arguments for supportedActions, maybe data? - if (args->Length() >= 1) { - v8::Local v = (*args)[0]; - if (v->IsInt32()) - supportedActions = Qt::DropActions(v->Int32Value()); - } - - d->start(supportedActions); -} - -/*! - \qmlattachedmethod enum QtQuick2::Drag::drop() - - Ends a drag sequence by sending a drop event to the target item. - - Returns the action accepted by the target item. If the target item or a parent doesn't accept - the drop event then Qt.IgnoreAction will be returned. - - The returned drop action may be one of: - - \list - \o Qt.CopyAction Copy the data to the target - \o Qt.MoveAction Move the data from the source to the target - \o Qt.LinkAction Create a link from the source to the target. - \o Qt.IgnoreAction Ignore the action (do nothing with the data). - \endlist - -*/ - -int QSGDragAttached::drop() -{ - Q_D(QSGDragAttached); - Qt::DropAction acceptedAction = Qt::IgnoreAction; - - if (!d->active) - return acceptedAction; - - QObject *target = 0; - - if (QSGCanvas *canvas = d->attachedItem->canvas()) { - QPoint scenePos = d->attachedItem->mapToScene(d->hotSpot).toPoint(); - - QDropEvent event( - scenePos, d->mimeData->m_supportedActions, d->mimeData, Qt::NoButton, Qt::NoModifier); - QSGDropEventEx::setProposedAction(&event, d->proposedAction); - QSGCanvasPrivate::get(canvas)->deliverDragEvent(&d->dragGrabber, &event); - - if (event.isAccepted()) { - acceptedAction = event.dropAction(); - target = d->dragGrabber.target(); - } - } - - d->active = false; - if (d->target != target) { - d->target = target; - emit targetChanged(); - } - - emit activeChanged(); - return acceptedAction; -} - -/*! - \qmlattachedmethod void QtQuick2::Drag::cancel() - - Ends a drag sequence. -*/ - -void QSGDragAttached::cancel() -{ - Q_D(QSGDragAttached); - if (!d->active) - return; - - if (QSGCanvas *canvas = d->attachedItem->canvas()) { - QDragLeaveEvent event; - QSGCanvasPrivate::get(canvas)->deliverDragEvent(&d->dragGrabber, &event); - } - - d->active = false; - if (d->target) { - d->target = 0; - emit targetChanged(); - } - emit activeChanged(); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgdrag_p.h b/src/declarative/items/qsgdrag_p.h deleted file mode 100644 index 246c6e6beb..0000000000 --- a/src/declarative/items/qsgdrag_p.h +++ /dev/null @@ -1,208 +0,0 @@ -// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGDRAG_P_H -#define QSGDRAG_P_H - -#include - -#include - -#include -#include - - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGItem; -class QSGDrag; -class QSGDragPrivate; - -class QSGDragGrabber -{ - class Item : public QDeclarativeGuard - { - public: - Item(QSGItem *item) : QDeclarativeGuard(item) {} - - QIntrusiveListNode node; - protected: - void objectDestroyed(QSGItem *) { delete this; } - }; - - typedef QIntrusiveList ItemList; - -public: - QSGDragGrabber() : m_target(0) {} - ~QSGDragGrabber() { while (!m_items.isEmpty()) delete m_items.first(); } - - - QObject *target() const - { - if (m_target) - return m_target; - else if (!m_items.isEmpty()) - return *m_items.first(); - else - return 0; - } - void setTarget(QObject *target) { m_target = target; } - void resetTarget() { m_target = 0; } - - typedef ItemList::iterator iterator; - iterator begin() { return m_items.begin(); } - iterator end() { return m_items.end(); } - - void grab(QSGItem *item) { m_items.insert(new Item(item)); } - iterator release(iterator at) { Item *item = *at; at = at.erase(); delete item; return at; } - -private: - - ItemList m_items; - QObject *m_target; -}; - -class QSGDropEventEx : public QDropEvent -{ -public: - void setProposedAction(Qt::DropAction action) { default_action = action; drop_action = action; } - - static void setProposedAction(QDropEvent *event, Qt::DropAction action) { - static_cast(event)->setProposedAction(action); - } - - void copyActions(const QDropEvent &from) { - default_action = from.proposedAction(); drop_action = from.dropAction(); } - - static void copyActions(QDropEvent *to, const QDropEvent &from) { - static_cast(to)->copyActions(from); - } -}; - -class QSGDragMimeData : public QMimeData -{ - Q_OBJECT -public: - QSGDragMimeData() - : m_source(0) - { - } - - QStringList keys() const { return m_keys; } - QObject *source() const { return m_source; } - -private: - QObject *m_source; - Qt::DropActions m_supportedActions; - QStringList m_keys; - - friend class QSGDragAttached; - friend class QSGDragAttachedPrivate; -}; - -class QDeclarativeV8Function; - -class QSGDragAttachedPrivate; -class QSGDragAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) - Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource) - Q_PROPERTY(QObject *target READ target NOTIFY targetChanged) - Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged) - Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged) - Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged) - Q_PROPERTY(Qt::DropAction proposedAction READ proposedAction WRITE setProposedAction NOTIFY proposedActionChanged) -public: - QSGDragAttached(QObject *parent); - ~QSGDragAttached(); - - bool isActive() const; - void setActive(bool active); - - QObject *source() const; - void setSource(QObject *item); - void resetSource(); - - QObject *target() const; - - QPointF hotSpot() const; - void setHotSpot(const QPointF &hotSpot); - - QStringList keys() const; - void setKeys(const QStringList &keys); - - Qt::DropActions supportedActions() const; - void setSupportedActions(Qt::DropActions actions); - - Qt::DropAction proposedAction() const; - void setProposedAction(Qt::DropAction action); - - Q_INVOKABLE int drop(); - -public Q_SLOTS: - void start(QDeclarativeV8Function *); - void cancel(); - -Q_SIGNALS: - void activeChanged(); - void sourceChanged(); - void targetChanged(); - void hotSpotChanged(); - void keysChanged(); - void supportedActionsChanged(); - void proposedActionChanged(); - -private: - Q_DECLARE_PRIVATE(QSGDragAttached) -}; - - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif diff --git a/src/declarative/items/qsgdroparea.cpp b/src/declarative/items/qsgdroparea.cpp deleted file mode 100644 index a1b81be1ae..0000000000 --- a/src/declarative/items/qsgdroparea.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgdroparea_p.h" -#include "qsgdrag_p.h" -#include "qsgitem_p.h" -#include "qsgcanvas.h" - -#include - -QSGDropAreaDrag::QSGDropAreaDrag(QSGDropAreaPrivate *d, QObject *parent) - : QObject(parent) - , d(d) -{ -} - -QSGDropAreaDrag::~QSGDropAreaDrag() -{ -} - -class QSGDropAreaPrivate : public QSGItemPrivate -{ - Q_DECLARE_PUBLIC(QSGDropArea) - -public: - QSGDropAreaPrivate(); - ~QSGDropAreaPrivate(); - - bool hasMatchingKey(const QStringList &keys) const; - - QStringList getKeys(const QMimeData *mimeData) const; - - QStringList keys; - QRegExp keyRegExp; - QPointF dragPosition; - QSGDropAreaDrag *drag; - QDeclarativeGuard source; - QDeclarativeGuard mimeData; -}; - -QSGDropAreaPrivate::QSGDropAreaPrivate() - : drag(0) -{ -} - -QSGDropAreaPrivate::~QSGDropAreaPrivate() -{ - delete drag; -} - -/*! - \qmlclass DropArea QSGDropArea - \inqmlmodule QtQuick 2 - \brief The DropArea item provides drag and drop handling. - - A DropArea is an invisible item which receives events when other items are - dragged over it. - - The Drag attached property can be used to notify the DropArea when an Item is - dragged over it. - - The \l keys property can be used to filter drag events which don't include - a matching key. - - The \l dropItem property is communicated to the source of a drag event as - the recipient of a drop on the drag target. - - The \l delegate property provides a means to specify a component to be - instantiated for each active drag over a drag target. -*/ - -QSGDropArea::QSGDropArea(QSGItem *parent) - : QSGItem(*new QSGDropAreaPrivate, parent) -{ - setFlags(ItemAcceptsDrops); -} - -QSGDropArea::~QSGDropArea() -{ -} - -/*! - \qmlproperty bool QtQuick2::DropArea::containsDrag - - This property identifies whether the DropArea currently contains any - dragged items. -*/ - -bool QSGDropArea::containsDrag() const -{ - Q_D(const QSGDropArea); - return d->mimeData; -} - -/*! - \qmlproperty stringlist QtQuick2::DropArea::keys - - This property holds a list of drag keys a DropArea will accept. - - If no keys are listed the DropArea will accept events from any drag source, - otherwise the drag source must have at least one compatible key. - - \sa QtQuick2::Drag::keys -*/ - -QStringList QSGDropArea::keys() const -{ - Q_D(const QSGDropArea); - return d->keys; -} - -void QSGDropArea::setKeys(const QStringList &keys) -{ - Q_D(QSGDropArea); - if (d->keys != keys) { - d->keys = keys; - - if (keys.isEmpty()) { - d->keyRegExp = QRegExp(); - } else { - QString pattern = QLatin1Char('(') + QRegExp::escape(keys.first()); - for (int i = 1; i < keys.count(); ++i) - pattern += QLatin1Char('|') + QRegExp::escape(keys.at(i)); - pattern += QLatin1Char(')'); - d->keyRegExp = QRegExp(pattern.replace(QLatin1String("\\*"), QLatin1String(".+"))); - } - emit keysChanged(); - } -} - -QSGDropAreaDrag *QSGDropArea::drag() -{ - Q_D(QSGDropArea); - if (!d->drag) - d->drag = new QSGDropAreaDrag(d); - return d->drag; -} - -/*! - \qmlproperty Object QtQuick2::DropArea::drag.source - - This property holds the source of a drag. -*/ - -QObject *QSGDropAreaDrag::source() const -{ - return d->source; -} - -/*! - \qmlproperty qreal QtQuick2::DropArea::drag.x - \qmlproperty qreal QtQuick2::DropArea::drag.y - - These properties hold the coordinates of the last drag event. -*/ - -qreal QSGDropAreaDrag::x() const -{ - return d->dragPosition.x(); -} - -qreal QSGDropAreaDrag::y() const -{ - return d->dragPosition.y(); -} - -/*! - \qmlsignal QtQuick2::DropArea::onPositionChanged(DragEvent drag) - - This handler is called when the position of a drag has changed. -*/ - -void QSGDropArea::dragMoveEvent(QDragMoveEvent *event) -{ - Q_D(QSGDropArea); - if (!d->mimeData) - return; - - d->dragPosition = event->pos(); - if (d->drag) - emit d->drag->positionChanged(); - - event->accept(); - QSGDropEvent dragTargetEvent(d, event); - emit positionChanged(&dragTargetEvent); -} - -bool QSGDropAreaPrivate::hasMatchingKey(const QStringList &keys) const -{ - if (keyRegExp.isEmpty()) - return true; - - foreach (const QString &key, keys) { - if (keyRegExp.exactMatch(key)) - return true; - } - return false; -} - -QStringList QSGDropAreaPrivate::getKeys(const QMimeData *mimeData) const -{ - if (const QSGDragMimeData *dragMime = qobject_cast(mimeData)) - return dragMime->keys(); - return mimeData->formats(); -} - -/*! - \qmlsignal QtQuick2::DropArea::onEntered(DragEvent drag) - - This handler is called when a \a drag enters the bounds of a DropArea. -*/ - -void QSGDropArea::dragEnterEvent(QDragEnterEvent *event) -{ - Q_D(QSGDropArea); - const QMimeData *mimeData = event->mimeData(); - if (!d->effectiveEnable || d->mimeData || !mimeData || !d->hasMatchingKey(d->getKeys(mimeData))) - return; - - d->dragPosition = event->pos(); - - event->accept(); - QSGDropEvent dragTargetEvent(d, event); - emit entered(&dragTargetEvent); - - if (event->isAccepted()) { - d->mimeData = const_cast(mimeData); - if (QSGDragMimeData *dragMime = qobject_cast(d->mimeData)) - d->source = dragMime->source(); - else - d->source = event->source(); - d->dragPosition = event->pos(); - if (d->drag) { - emit d->drag->positionChanged(); - emit d->drag->sourceChanged(); - } - emit containsDragChanged(); - } -} - -/*! - \qmlsignal QtQuick2::DropArea::onExited() - - This handler is called when a drag exits the bounds of a DropArea. -*/ - -void QSGDropArea::dragLeaveEvent(QDragLeaveEvent *) -{ - Q_D(QSGDropArea); - if (!d->mimeData) - return; - - emit exited(); - - d->mimeData = 0; - d->source = 0; - emit containsDragChanged(); - if (d->drag) - emit d->drag->sourceChanged(); -} - -/*! - \qmlsignal QtQuick2::DropArea::onDropped(DragEvent drop) - - This handler is called when a drop event occurs within the bounds of a - a DropArea. -*/ - -void QSGDropArea::dropEvent(QDropEvent *event) -{ - Q_D(QSGDropArea); - if (!d->mimeData) - return; - - QSGDropEvent dragTargetEvent(d, event); - emit dropped(&dragTargetEvent); - - d->mimeData = 0; - d->source = 0; - emit containsDragChanged(); - if (d->drag) - emit d->drag->sourceChanged(); -} - -/*! - \qmlclass DragEvent QSGDragEvent - \inqmlmodule QtQuick 2 - \brief The DragEvent object provides information about a drag event. - - The position of the drag event can be obtained from the \l x and \l y - properties, and the \l keys property identifies the drag keys of the event - \l source. -*/ - -/*! - \qmlproperty real QtQuick2::DragEvent::x - - This property holds the x coordinate of a drag event. -*/ - -/*! - \qmlproperty real QtQuick2::DragEvent::y - - This property holds the y coordinate of a drag event. -*/ - -/*! - \qmlproperty Object QtQuick2::DragEvent::drag.source - - This property holds the source of a drag event. -*/ - -QObject *QSGDropEvent::source() -{ - if (const QSGDragMimeData *dragMime = qobject_cast(event->mimeData())) - return dragMime->source(); - else - return event->source(); -} - -/*! - \qmlproperty stringlist QtQuick2::DragEvent::keys - - This property holds a list of keys identifying the data type or source of a - drag event. -*/ - -QStringList QSGDropEvent::keys() const -{ - return d->getKeys(event->mimeData()); -} - -/*! - \qmlproperty enum QtQuick2::DragEvent::action - - This property holds the action that the \l source is to perform on an accepted drop. - - The drop action may be one of: - - \list - \o Qt.CopyAction Copy the data to the target - \o Qt.MoveAction Move the data from the source to the target - \o Qt.LinkAction Create a link from the source to the target. - \o Qt.IgnoreAction Ignore the action (do nothing with the data). - \endlist -*/ - -/*! - \qmlproperty flags QtQuick2::DragEvent::supportedActions - - This property holds the set of \l {action}{actions} supported by the - drag source. -*/ - -/*! - \qmlproperty real QtQuick2::DragEvent::accepted - - This property holds whether the drag event was accepted by a handler. - - The default value is true. -*/ - -/*! - \qmlmethod void QtQuick2::DragEvent::accept() - \qmlmethod void QtQuick2::DragEvent::accept(enum action) - - Accepts the drag event. - - If an \a action is specified it will overwrite the value of the \l action property. -*/ - -void QSGDropEvent::accept(QDeclarativeV8Function *args) -{ - Qt::DropAction action = event->dropAction(); - - if (args->Length() >= 1) { - v8::Local v = (*args)[0]; - if (v->IsInt32()) - action = Qt::DropAction(v->Int32Value()); - } - // get action from arguments. - event->setDropAction(action); - event->accept(); -} - - -QT_END_NAMESPACE - diff --git a/src/declarative/items/qsgdroparea_p.h b/src/declarative/items/qsgdroparea_p.h deleted file mode 100644 index cd51f57e0b..0000000000 --- a/src/declarative/items/qsgdroparea_p.h +++ /dev/null @@ -1,167 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGDROPAREA_P_H -#define QSGDROPAREA_P_H - -#include "qsgitem.h" - -#include -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGDropAreaPrivate; -class QSGDropEvent : public QObject -{ - Q_OBJECT - Q_PROPERTY(qreal x READ x) - Q_PROPERTY(qreal y READ y) - Q_PROPERTY(QObject *source READ source) - Q_PROPERTY(QStringList keys READ keys) - Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions) - Q_PROPERTY(Qt::DropAction action READ action WRITE setAction RESET resetAction) - Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) -public: - QSGDropEvent(QSGDropAreaPrivate *d, QDropEvent *event) : d(d), event(event) {} - - qreal x() const { return event->pos().x(); } - qreal y() const { return event->pos().y(); } - - QObject *source(); - - Qt::DropActions supportedActions() const { return event->possibleActions(); } - Qt::DropAction action() const { return event->dropAction(); } - void setAction(Qt::DropAction action) { event->setDropAction(action); } - void resetAction() { event->setDropAction(event->proposedAction()); } - - QStringList keys() const; - - bool accepted() const { return event->isAccepted(); } - void setAccepted(bool accepted) { event->setAccepted(accepted); } - - Q_INVOKABLE void accept(QDeclarativeV8Function *); - -private: - QSGDropAreaPrivate *d; - QDropEvent *event; -}; - -class QSGDropAreaDrag : public QObject -{ - Q_OBJECT - Q_PROPERTY(qreal x READ x NOTIFY positionChanged) - Q_PROPERTY(qreal y READ y NOTIFY positionChanged) - Q_PROPERTY(QObject *source READ source NOTIFY sourceChanged) -public: - QSGDropAreaDrag(QSGDropAreaPrivate *d, QObject *parent = 0); - ~QSGDropAreaDrag(); - - qreal x() const; - qreal y() const; - QObject *source() const; - -Q_SIGNALS: - void positionChanged(); - void sourceChanged(); - -private: - QSGDropAreaPrivate *d; - - friend class QSGDropArea; - friend class QSGDropAreaPrivate; -}; - -class QSGDropAreaPrivate; -class Q_AUTOTEST_EXPORT QSGDropArea : public QSGItem -{ - Q_OBJECT - Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged) - Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged) - Q_PROPERTY(QSGDropAreaDrag *drag READ drag CONSTANT) - -public: - QSGDropArea(QSGItem *parent=0); - ~QSGDropArea(); - - bool containsDrag() const; - void setContainsDrag(bool drag); - - QStringList keys() const; - void setKeys(const QStringList &keys); - - QSGDropAreaDrag *drag(); - -Q_SIGNALS: - void containsDragChanged(); - void keysChanged(); - void sourceChanged(); - - void entered(QSGDropEvent *drag); - void exited(); - void positionChanged(QSGDropEvent *drag); - void dropped(QSGDropEvent *drop); - -protected: - void dragMoveEvent(QDragMoveEvent *event); - void dragEnterEvent(QDragEnterEvent *event); - void dragLeaveEvent(QDragLeaveEvent *event); - void dropEvent(QDropEvent *event); - -private: - Q_DISABLE_COPY(QSGDropArea) - Q_DECLARE_PRIVATE(QSGDropArea) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGDropEvent) -QML_DECLARE_TYPE(QSGDropArea) - -QT_END_HEADER - -#endif // QSGDRAGTARGET_P_H diff --git a/src/declarative/items/qsgevents.cpp b/src/declarative/items/qsgevents.cpp deleted file mode 100644 index 2173b4a21f..0000000000 --- a/src/declarative/items/qsgevents.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgevents_p_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \qmlclass KeyEvent QSGKeyEvent - \inqmlmodule QtQuick 2 - \ingroup qml-event-elements - - \brief The KeyEvent object provides information about a key event. - - For example, the following changes the Item's state property when the Enter - key is pressed: - \qml -Item { - focus: true - Keys.onPressed: { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; } -} - \endqml -*/ - -/*! - \qmlproperty int QtQuick2::KeyEvent::key - - This property holds the code of the key that was pressed or released. - - See \l {Qt::Key}{Qt.Key} for the list of keyboard codes. These codes are - independent of the underlying window system. Note that this - function does not distinguish between capital and non-capital - letters, use the text() function (returning the Unicode text the - key generated) for this purpose. - - A value of either 0 or \l {Qt::Key_unknown}{Qt.Key_Unknown} means that the event is not - the result of a known key; for example, it may be the result of - a compose sequence, a keyboard macro, or due to key event - compression. -*/ - -/*! - \qmlproperty string QtQuick2::KeyEvent::text - - This property holds the Unicode text that the key generated. - The text returned can be an empty string in cases where modifier keys, - such as Shift, Control, Alt, and Meta, are being pressed or released. - In such cases \c key will contain a valid value -*/ - -/*! - \qmlproperty bool QtQuick2::KeyEvent::isAutoRepeat - - This property holds whether this event comes from an auto-repeating key. -*/ - -/*! - \qmlproperty int QtQuick2::KeyEvent::count - - This property holds the number of keys involved in this event. If \l KeyEvent::text - is not empty, this is simply the length of the string. -*/ - -/*! - \qmlproperty bool QtQuick2::KeyEvent::accepted - - Setting \a accepted to true prevents the key event from being - propagated to the item's parent. - - Generally, if the item acts on the key event then it should be accepted - so that ancestor items do not also respond to the same event. -*/ - -/*! - \qmlproperty int QtQuick2::KeyEvent::modifiers - - This property holds the keyboard modifier flags that existed immediately - before the event occurred. - - It contains a bitwise combination of: - \list - \o Qt.NoModifier - No modifier key is pressed. - \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. - \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. - \o Qt.AltModifier - An Alt key on the keyboard is pressed. - \o Qt.MetaModifier - A Meta key on the keyboard is pressed. - \o Qt.KeypadModifier - A keypad button is pressed. - \endlist - - For example, to react to a Shift key + Enter key combination: - \qml - Item { - focus: true - Keys.onPressed: { - if ((event.key == Qt.Key_Enter) && (event.modifiers & Qt.ShiftModifier)) - doSomething(); - } - } - \endqml -*/ - - -/*! - \qmlclass MouseEvent QSGMouseEvent - \inqmlmodule QtQuick 2 - \ingroup qml-event-elements - - \brief The MouseEvent object provides information about a mouse event. - - The position of the mouse can be found via the \l x and \l y properties. - The button that caused the event is available via the \l button property. - - \sa MouseArea -*/ - -/*! - \internal - \class QSGMouseEvent -*/ - -/*! - \qmlproperty int QtQuick2::MouseEvent::x - \qmlproperty int QtQuick2::MouseEvent::y - - These properties hold the coordinates of the position supplied by the mouse event. -*/ - - -/*! - \qmlproperty bool QtQuick2::MouseEvent::accepted - - Setting \a accepted to true prevents the mouse event from being - propagated to items below this item. - - Generally, if the item acts on the mouse event then it should be accepted - so that items lower in the stacking order do not also respond to the same event. -*/ - -/*! - \qmlproperty enumeration QtQuick2::MouseEvent::button - - This property holds the button that caused the event. It can be one of: - \list - \o Qt.LeftButton - \o Qt.RightButton - \o Qt.MiddleButton - \endlist -*/ - -/*! - \qmlproperty bool QtQuick2::MouseEvent::wasHeld - - This property is true if the mouse button has been held pressed longer the - threshold (800ms). -*/ - -/*! - \qmlproperty int QtQuick2::MouseEvent::buttons - - This property holds the mouse buttons pressed when the event was generated. - For mouse move events, this is all buttons that are pressed down. For mouse - press and double click events this includes the button that caused the event. - For mouse release events this excludes the button that caused the event. - - It contains a bitwise combination of: - \list - \o Qt.LeftButton - \o Qt.RightButton - \o Qt.MiddleButton - \endlist -*/ - -/*! - \qmlproperty int QtQuick2::MouseEvent::modifiers - - This property holds the keyboard modifier flags that existed immediately - before the event occurred. - - It contains a bitwise combination of: - \list - \o Qt.NoModifier - No modifier key is pressed. - \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. - \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. - \o Qt.AltModifier - An Alt key on the keyboard is pressed. - \o Qt.MetaModifier - A Meta key on the keyboard is pressed. - \o Qt.KeypadModifier - A keypad button is pressed. - \endlist - - For example, to react to a Shift key + Left mouse button click: - \qml - MouseArea { - onClicked: { - if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ShiftModifier)) - doSomething(); - } - } - \endqml -*/ - - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgevents_p_p.h b/src/declarative/items/qsgevents_p_p.h deleted file mode 100644 index 1d2b6a9dec..0000000000 --- a/src/declarative/items/qsgevents_p_p.h +++ /dev/null @@ -1,144 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGEVENTS_P_P_H -#define QSGEVENTS_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGKeyEvent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int key READ key) - Q_PROPERTY(QString text READ text) - Q_PROPERTY(int modifiers READ modifiers) - Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat) - Q_PROPERTY(int count READ count) - Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - -public: - QSGKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text=QString(), bool autorep=false, ushort count=1) - : event(type, key, modifiers, text, autorep, count) { event.setAccepted(false); } - QSGKeyEvent(const QKeyEvent &ke) - : event(ke) { event.setAccepted(false); } - - int key() const { return event.key(); } - QString text() const { return event.text(); } - int modifiers() const { return event.modifiers(); } - bool isAutoRepeat() const { return event.isAutoRepeat(); } - int count() const { return event.count(); } - - bool isAccepted() { return event.isAccepted(); } - void setAccepted(bool accepted) { event.setAccepted(accepted); } - -private: - QKeyEvent event; -}; - -// used in QtLocation -class Q_DECLARATIVE_EXPORT QSGMouseEvent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int x READ x) - Q_PROPERTY(int y READ y) - Q_PROPERTY(int button READ button) - Q_PROPERTY(int buttons READ buttons) - Q_PROPERTY(int modifiers READ modifiers) - Q_PROPERTY(bool wasHeld READ wasHeld) - Q_PROPERTY(bool isClick READ isClick) - Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - -public: - QSGMouseEvent(int x, int y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers - , bool isClick=false, bool wasHeld=false) - : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers) - , _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {} - - int x() const { return _x; } - int y() const { return _y; } - int button() const { return _button; } - int buttons() const { return _buttons; } - int modifiers() const { return _modifiers; } - bool wasHeld() const { return _wasHeld; } - bool isClick() const { return _isClick; } - - // only for internal usage - void setX(int x) { _x = x; } - void setY(int y) { _y = y; } - void setPosition(const QPointF &point) { _x = point.x(); _y = point.y(); } - - bool isAccepted() { return _accepted; } - void setAccepted(bool accepted) { _accepted = accepted; } - -private: - int _x; - int _y; - Qt::MouseButton _button; - Qt::MouseButtons _buttons; - Qt::KeyboardModifiers _modifiers; - bool _wasHeld; - bool _isClick; - bool _accepted; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGKeyEvent) -QML_DECLARE_TYPE(QSGMouseEvent) - -#endif // QSGEVENTS_P_P_H diff --git a/src/declarative/items/qsgflickable.cpp b/src/declarative/items/qsgflickable.cpp deleted file mode 100644 index 4871a43e8d..0000000000 --- a/src/declarative/items/qsgflickable.cpp +++ /dev/null @@ -1,1997 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgflickable_p.h" -#include "qsgflickable_p_p.h" -#include "qsgcanvas.h" -#include "qsgcanvas_p.h" - -#include -#include -#include -#include -#include "qplatformdefs.h" - -QT_BEGIN_NAMESPACE - -// The maximum number of pixels a flick can overshoot -#ifndef QML_FLICK_OVERSHOOT -#define QML_FLICK_OVERSHOOT 200 -#endif - -// The number of samples to use in calculating the velocity of a flick -#ifndef QML_FLICK_SAMPLEBUFFER -#define QML_FLICK_SAMPLEBUFFER 3 -#endif - -// The number of samples to discard when calculating the flick velocity. -// Touch panels often produce inaccurate results as the finger is lifted. -#ifndef QML_FLICK_DISCARDSAMPLES -#define QML_FLICK_DISCARDSAMPLES 1 -#endif - -// The default maximum velocity of a flick. -#ifndef QML_FLICK_DEFAULTMAXVELOCITY -#define QML_FLICK_DEFAULTMAXVELOCITY 2500 -#endif - -// The default deceleration of a flick. -#ifndef QML_FLICK_DEFAULTDECELERATION -#define QML_FLICK_DEFAULTDECELERATION 1500 -#endif - -// How much faster to decelerate when overshooting -#ifndef QML_FLICK_OVERSHOOTFRICTION -#define QML_FLICK_OVERSHOOTFRICTION 8 -#endif - -// FlickThreshold determines how far the "mouse" must have moved -// before we perform a flick. -static const int FlickThreshold = 20; - -// RetainGrabVelocity is the maxmimum instantaneous velocity that -// will ensure the Flickable retains the grab on consecutive flicks. -static const int RetainGrabVelocity = 15; - -QSGFlickableVisibleArea::QSGFlickableVisibleArea(QSGFlickable *parent) - : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.) - , m_yPosition(0.), m_heightRatio(0.) -{ -} - -qreal QSGFlickableVisibleArea::widthRatio() const -{ - return m_widthRatio; -} - -qreal QSGFlickableVisibleArea::xPosition() const -{ - return m_xPosition; -} - -qreal QSGFlickableVisibleArea::heightRatio() const -{ - return m_heightRatio; -} - -qreal QSGFlickableVisibleArea::yPosition() const -{ - return m_yPosition; -} - -void QSGFlickableVisibleArea::updateVisible() -{ - QSGFlickablePrivate *p = QSGFlickablePrivate::get(flickable); - - bool changeX = false; - bool changeY = false; - bool changeWidth = false; - bool changeHeight = false; - - // Vertical - const qreal viewheight = flickable->height(); - const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent(); - qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight); - qreal pageSize = viewheight / (maxyextent + viewheight); - - if (pageSize != m_heightRatio) { - m_heightRatio = pageSize; - changeHeight = true; - } - if (pagePos != m_yPosition) { - m_yPosition = pagePos; - changeY = true; - } - - // Horizontal - const qreal viewwidth = flickable->width(); - const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent(); - pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth); - pageSize = viewwidth / (maxxextent + viewwidth); - - if (pageSize != m_widthRatio) { - m_widthRatio = pageSize; - changeWidth = true; - } - if (pagePos != m_xPosition) { - m_xPosition = pagePos; - changeX = true; - } - - if (changeX) - emit xPositionChanged(m_xPosition); - if (changeY) - emit yPositionChanged(m_yPosition); - if (changeWidth) - emit widthRatioChanged(m_widthRatio); - if (changeHeight) - emit heightRatioChanged(m_heightRatio); -} - - -QSGFlickablePrivate::QSGFlickablePrivate() - : contentItem(new QSGItem) - , hData(this, &QSGFlickablePrivate::setViewportX) - , vData(this, &QSGFlickablePrivate::setViewportY) - , hMoved(false), vMoved(false) - , stealMouse(false), pressed(false), interactive(true), calcVelocity(false) - , pixelAligned(false) - , deceleration(QML_FLICK_DEFAULTDECELERATION) - , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100) - , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400) - , fixupMode(Normal), vTime(0), visibleArea(0) - , flickableDirection(QSGFlickable::AutoFlickDirection) - , boundsBehavior(QSGFlickable::DragAndOvershootBounds) -{ -} - -void QSGFlickablePrivate::init() -{ - Q_Q(QSGFlickable); - QDeclarative_setParent_noEvent(contentItem, q); - contentItem->setParentItem(q); - FAST_CONNECT(&timeline, SIGNAL(updated()), q, SLOT(ticked())) - FAST_CONNECT(&timeline, SIGNAL(completed()), q, SLOT(movementEnding())) - q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFiltersChildMouseEvents(true); - QSGItemPrivate *viewportPrivate = QSGItemPrivate::get(contentItem); - viewportPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - lastPosTime.invalidate(); -} - -/* - Returns the amount to overshoot by given a velocity. - Will be roughly in range 0 - size/4 -*/ -qreal QSGFlickablePrivate::overShootDistance(qreal size) -{ - if (maxVelocity <= 0) - return 0.0; - - return qMin(qreal(QML_FLICK_OVERSHOOT), size/3); -} - -void QSGFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity) -{ - if (v > maxVelocity) - v = maxVelocity; - else if (v < -maxVelocity) - v = -maxVelocity; - velocityBuffer.append(v); - if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER) - velocityBuffer.remove(0); -} - -void QSGFlickablePrivate::AxisData::updateVelocity() -{ - velocity = 0; - if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) { - int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES; - for (int i = 0; i < count; ++i) { - qreal v = velocityBuffer.at(i); - velocity += v; - } - velocity /= count; - } -} - -void QSGFlickablePrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeom, const QRectF &oldGeom) -{ - Q_Q(QSGFlickable); - if (item == contentItem) { - if (newGeom.x() != oldGeom.x()) - emit q->contentXChanged(); - if (newGeom.y() != oldGeom.y()) - emit q->contentYChanged(); - } -} - -void QSGFlickablePrivate::flickX(qreal velocity) -{ - Q_Q(QSGFlickable); - flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); -} - -void QSGFlickablePrivate::flickY(qreal velocity) -{ - Q_Q(QSGFlickable); - flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); -} - -void QSGFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) -{ - Q_Q(QSGFlickable); - qreal maxDistance = -1; - data.fixingUp = false; - // -ve velocity means list is moving up - if (velocity > 0) { - maxDistance = qAbs(minExtent - data.move.value()); - data.flickTarget = minExtent; - } else { - maxDistance = qAbs(maxExtent - data.move.value()); - data.flickTarget = maxExtent; - } - if (maxDistance > 0) { - qreal v = velocity; - if (maxVelocity != -1 && maxVelocity < qAbs(v)) { - if (v < 0) - v = -maxVelocity; - else - v = maxVelocity; - } - timeline.reset(data.move); - if (boundsBehavior == QSGFlickable::DragAndOvershootBounds) - timeline.accel(data.move, v, deceleration); - else - timeline.accel(data.move, v, deceleration, maxDistance); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); - if (!hData.flicking && q->xflick()) { - hData.flicking = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - if (!vData.flicking) - emit q->flickStarted(); - } - if (!vData.flicking && q->yflick()) { - vData.flicking = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - if (!hData.flicking) - emit q->flickStarted(); - } - } else { - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); - } -} - -void QSGFlickablePrivate::fixupY_callback(void *data) -{ - ((QSGFlickablePrivate *)data)->fixupY(); -} - -void QSGFlickablePrivate::fixupX_callback(void *data) -{ - ((QSGFlickablePrivate *)data)->fixupX(); -} - -void QSGFlickablePrivate::fixupX() -{ - Q_Q(QSGFlickable); - fixup(hData, q->minXExtent(), q->maxXExtent()); -} - -void QSGFlickablePrivate::fixupY() -{ - Q_Q(QSGFlickable); - fixup(vData, q->minYExtent(), q->maxYExtent()); -} - -void QSGFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) -{ - if (data.move.value() > minExtent || maxExtent > minExtent) { - timeline.reset(data.move); - if (data.move.value() != minExtent) { - switch (fixupMode) { - case Immediate: - timeline.set(data.move, minExtent); - break; - case ExtentChanged: - // The target has changed. Don't start from the beginning; just complete the - // second half of the animation using the new extent. - timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); - data.fixingUp = true; - break; - default: { - qreal dist = minExtent - data.move; - timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); - timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); - data.fixingUp = true; - } - } - } - } else if (data.move.value() < maxExtent) { - timeline.reset(data.move); - switch (fixupMode) { - case Immediate: - timeline.set(data.move, maxExtent); - break; - case ExtentChanged: - // The target has changed. Don't start from the beginning; just complete the - // second half of the animation using the new extent. - timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); - data.fixingUp = true; - break; - default: { - qreal dist = maxExtent - data.move; - timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); - timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); - data.fixingUp = true; - } - } - } - data.inOvershoot = false; - fixupMode = Normal; - vTime = timeline.time(); -} - -void QSGFlickablePrivate::updateBeginningEnd() -{ - Q_Q(QSGFlickable); - bool atBoundaryChange = false; - - // Vertical - const int maxyextent = int(-q->maxYExtent()); - const qreal ypos = -vData.move.value(); - bool atBeginning = (ypos <= -q->minYExtent()); - bool atEnd = (maxyextent <= ypos); - - if (atBeginning != vData.atBeginning) { - vData.atBeginning = atBeginning; - atBoundaryChange = true; - } - if (atEnd != vData.atEnd) { - vData.atEnd = atEnd; - atBoundaryChange = true; - } - - // Horizontal - const int maxxextent = int(-q->maxXExtent()); - const qreal xpos = -hData.move.value(); - atBeginning = (xpos <= -q->minXExtent()); - atEnd = (maxxextent <= xpos); - - if (atBeginning != hData.atBeginning) { - hData.atBeginning = atBeginning; - atBoundaryChange = true; - } - if (atEnd != hData.atEnd) { - hData.atEnd = atEnd; - atBoundaryChange = true; - } - - if (vData.extentsChanged) { - vData.extentsChanged = false; - emit q->yOriginChanged(); - } - - if (hData.extentsChanged) { - hData.extentsChanged = false; - emit q->xOriginChanged(); - } - - if (atBoundaryChange) - emit q->isAtBoundaryChanged(); - - if (visibleArea) - visibleArea->updateVisible(); -} - -/* -XXXTODO add docs describing moving, dragging, flicking properties, e.g. - -When the user starts dragging the Flickable, the dragging and moving properties -will be true. - -If the velocity is sufficient when the drag is ended, flicking may begin. - -The moving properties will remain true until all dragging and flicking -is finished. -*/ - -/*! - \qmlsignal QtQuick2::Flickable::onDragStarted() - - This handler is called when the view starts to be dragged due to user - interaction. -*/ - -/*! - \qmlsignal QtQuick2::Flickable::onDragEnded() - - This handler is called when the user stops dragging the view. - - If the velocity of the drag is suffient at the time the - touch/mouse button is released then a flick will start. -*/ - -/*! - \qmlclass Flickable QSGFlickable - \inqmlmodule QtQuick 2 - \ingroup qml-basic-interaction-elements - - \brief The Flickable item provides a surface that can be "flicked". - \inherits Item - - The Flickable item places its children on a surface that can be dragged - and flicked, causing the view onto the child items to scroll. This - behavior forms the basis of Items that are designed to show large numbers - of child items, such as \l ListView and \l GridView. - - In traditional user interfaces, views can be scrolled using standard - controls, such as scroll bars and arrow buttons. In some situations, it - is also possible to drag the view directly by pressing and holding a - mouse button while moving the cursor. In touch-based user interfaces, - this dragging action is often complemented with a flicking action, where - scrolling continues after the user has stopped touching the view. - - Flickable does not automatically clip its contents. If it is not used as - a full-screen item, you should consider setting the \l{Item::}{clip} property - to true. - - \section1 Example Usage - - \div {class="float-right"} - \inlineimage flickable.gif - \enddiv - - The following example shows a small view onto a large image in which the - user can drag or flick the image in order to view different parts of it. - - \snippet doc/src/snippets/declarative/flickable.qml document - - \clearfloat - - Items declared as children of a Flickable are automatically parented to the - Flickable's \l contentItem. This should be taken into account when - operating on the children of the Flickable; it is usually the children of - \c contentItem that are relevant. For example, the bound of Items added - to the Flickable will be available by \c contentItem.childrenRect - - \section1 Limitations - - \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by - \c id. Use \c parent instead. -*/ - -/*! - \qmlsignal QtQuick2::Flickable::onMovementStarted() - - This handler is called when the view begins moving due to user - interaction. -*/ - -/*! - \qmlsignal QtQuick2::Flickable::onMovementEnded() - - This handler is called when the view stops moving due to user - interaction. If a flick was generated, this handler will - be triggered once the flick stops. If a flick was not - generated, the handler will be triggered when the - user stops dragging - i.e. a mouse or touch release. -*/ - -/*! - \qmlsignal QtQuick2::Flickable::onFlickStarted() - - This handler is called when the view is flicked. A flick - starts from the point that the mouse or touch is released, - while still in motion. -*/ - -/*! - \qmlsignal QtQuick2::Flickable::onFlickEnded() - - This handler is called when the view stops moving due to a flick. -*/ - -/*! - \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition - \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio - \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition - \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio - - These properties describe the position and size of the currently viewed area. - The size is defined as the percentage of the full view currently visible, - scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to - 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio. - However, it is possible for the contents to be dragged outside of the normal - range, resulting in the page positions also being outside the normal range. - - These properties are typically used to draw a scrollbar. For example: - - \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0 - \dots 8 - \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1 - - \sa {declarative/ui-components/scrollbar}{scrollbar example} -*/ -QSGFlickable::QSGFlickable(QSGItem *parent) - : QSGItem(*(new QSGFlickablePrivate), parent) -{ - Q_D(QSGFlickable); - d->init(); -} - -QSGFlickable::QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent) - : QSGItem(dd, parent) -{ - Q_D(QSGFlickable); - d->init(); -} - -QSGFlickable::~QSGFlickable() -{ -} - -/*! - \qmlproperty real QtQuick2::Flickable::contentX - \qmlproperty real QtQuick2::Flickable::contentY - - These properties hold the surface coordinate currently at the top-left - corner of the Flickable. For example, if you flick an image up 100 pixels, - \c contentY will be 100. -*/ -qreal QSGFlickable::contentX() const -{ - Q_D(const QSGFlickable); - return -d->contentItem->x(); -} - -void QSGFlickable::setContentX(qreal pos) -{ - Q_D(QSGFlickable); - d->hData.explicitValue = true; - d->timeline.reset(d->hData.move); - d->vTime = d->timeline.time(); - movementXEnding(); - if (-pos != d->hData.move.value()) { - d->hData.move.setValue(-pos); - viewportMoved(); - } -} - -qreal QSGFlickable::contentY() const -{ - Q_D(const QSGFlickable); - return -d->contentItem->y(); -} - -void QSGFlickable::setContentY(qreal pos) -{ - Q_D(QSGFlickable); - d->vData.explicitValue = true; - d->timeline.reset(d->vData.move); - d->vTime = d->timeline.time(); - movementYEnding(); - if (-pos != d->vData.move.value()) { - d->vData.move.setValue(-pos); - viewportMoved(); - } -} - -/*! - \qmlproperty bool QtQuick2::Flickable::interactive - - This property describes whether the user can interact with the Flickable. - A user cannot drag or flick a Flickable that is not interactive. - - By default, this property is true. - - This property is useful for temporarily disabling flicking. This allows - special interaction with Flickable's children; for example, you might want - to freeze a flickable map while scrolling through a pop-up dialog that - is a child of the Flickable. -*/ -bool QSGFlickable::isInteractive() const -{ - Q_D(const QSGFlickable); - return d->interactive; -} - -void QSGFlickable::setInteractive(bool interactive) -{ - Q_D(QSGFlickable); - if (interactive != d->interactive) { - d->interactive = interactive; - if (!interactive && (d->hData.flicking || d->vData.flicking)) { - d->timeline.clear(); - d->vTime = d->timeline.time(); - d->hData.flicking = false; - d->vData.flicking = false; - emit flickingChanged(); - emit flickingHorizontallyChanged(); - emit flickingVerticallyChanged(); - emit flickEnded(); - } - emit interactiveChanged(); - } -} - -/*! - \qmlproperty real QtQuick2::Flickable::horizontalVelocity - \qmlproperty real QtQuick2::Flickable::verticalVelocity - - The instantaneous velocity of movement along the x and y axes, in pixels/sec. - - The reported velocity is smoothed to avoid erratic output. -*/ -qreal QSGFlickable::horizontalVelocity() const -{ - Q_D(const QSGFlickable); - return d->hData.smoothVelocity.value(); -} - -qreal QSGFlickable::verticalVelocity() const -{ - Q_D(const QSGFlickable); - return d->vData.smoothVelocity.value(); -} - -/*! - \qmlproperty bool QtQuick2::Flickable::atXBeginning - \qmlproperty bool QtQuick2::Flickable::atXEnd - \qmlproperty bool QtQuick2::Flickable::atYBeginning - \qmlproperty bool QtQuick2::Flickable::atYEnd - - These properties are true if the flickable view is positioned at the beginning, - or end respecively. -*/ -bool QSGFlickable::isAtXEnd() const -{ - Q_D(const QSGFlickable); - return d->hData.atEnd; -} - -bool QSGFlickable::isAtXBeginning() const -{ - Q_D(const QSGFlickable); - return d->hData.atBeginning; -} - -bool QSGFlickable::isAtYEnd() const -{ - Q_D(const QSGFlickable); - return d->vData.atEnd; -} - -bool QSGFlickable::isAtYBeginning() const -{ - Q_D(const QSGFlickable); - return d->vData.atBeginning; -} - -void QSGFlickable::ticked() -{ - viewportMoved(); -} - -/*! - \qmlproperty Item QtQuick2::Flickable::contentItem - - The internal item that contains the Items to be moved in the Flickable. - - Items declared as children of a Flickable are automatically parented to the Flickable's contentItem. - - Items created dynamically need to be explicitly parented to the \e contentItem: - \code - Flickable { - id: myFlickable - function addItem(file) { - var component = Qt.createComponent(file) - component.createObject(myFlickable.contentItem); - } - } - \endcode -*/ -QSGItem *QSGFlickable::contentItem() -{ - Q_D(QSGFlickable); - return d->contentItem; -} - -QSGFlickableVisibleArea *QSGFlickable::visibleArea() -{ - Q_D(QSGFlickable); - if (!d->visibleArea) - d->visibleArea = new QSGFlickableVisibleArea(this); - return d->visibleArea; -} - -/*! - \qmlproperty enumeration QtQuick2::Flickable::flickableDirection - - This property determines which directions the view can be flicked. - - \list - \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the - \e contentHeight is not equal to the \e height of the Flickable. - Allows flicking horizontally if the \e contentWidth is not equal - to the \e width of the Flickable. - \o Flickable.HorizontalFlick - allows flicking horizontally. - \o Flickable.VerticalFlick - allows flicking vertically. - \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions. - \endlist -*/ -QSGFlickable::FlickableDirection QSGFlickable::flickableDirection() const -{ - Q_D(const QSGFlickable); - return d->flickableDirection; -} - -void QSGFlickable::setFlickableDirection(FlickableDirection direction) -{ - Q_D(QSGFlickable); - if (direction != d->flickableDirection) { - d->flickableDirection = direction; - emit flickableDirectionChanged(); - } -} - -bool QSGFlickable::pixelAligned() const -{ - Q_D(const QSGFlickable); - return d->pixelAligned; -} - -void QSGFlickable::setPixelAligned(bool align) -{ - Q_D(QSGFlickable); - if (align != d->pixelAligned) { - d->pixelAligned = align; - emit pixelAlignedChanged(); - } -} - -void QSGFlickablePrivate::handleMousePressEvent(QMouseEvent *event) -{ - Q_Q(QSGFlickable); - if (interactive && timeline.isActive() - && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity - || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) { - stealMouse = true; // If we've been flicked then steal the click. - } else { - stealMouse = false; - } - q->setKeepMouseGrab(stealMouse); - pressed = true; - timeline.clear(); - hData.reset(); - vData.reset(); - hData.dragMinBound = q->minXExtent(); - vData.dragMinBound = q->minYExtent(); - hData.dragMaxBound = q->maxXExtent(); - vData.dragMaxBound = q->maxYExtent(); - fixupMode = Normal; - lastPos = QPoint(); - QSGItemPrivate::start(lastPosTime); - pressPos = event->localPos(); - hData.pressPos = hData.move.value(); - vData.pressPos = vData.move.value(); - hData.flicking = false; - vData.flicking = false; - QSGItemPrivate::start(pressTime); - QSGItemPrivate::start(velocityTime); -} - -void QSGFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) -{ - Q_Q(QSGFlickable); - if (!interactive || !lastPosTime.isValid()) - return; - bool rejectY = false; - bool rejectX = false; - - bool stealY = stealMouse; - bool stealX = stealMouse; - - if (q->yflick()) { - int dy = int(event->localPos().y() - pressPos.y()); - if (qAbs(dy) > qApp->styleHints()->startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) { - if (!vMoved) - vData.dragStartOffset = dy; - qreal newY = dy + vData.pressPos - vData.dragStartOffset; - const qreal minY = vData.dragMinBound; - const qreal maxY = vData.dragMaxBound; - if (newY > minY) - newY = minY + (newY - minY) / 2; - if (newY < maxY && maxY - minY <= 0) - newY = maxY + (newY - maxY) / 2; - if (boundsBehavior == QSGFlickable::StopAtBounds && (newY > minY || newY < maxY)) { - rejectY = true; - if (newY < maxY) { - newY = maxY; - rejectY = false; - } - if (newY > minY) { - newY = minY; - rejectY = false; - } - } - if (!rejectY && stealMouse) { - vData.move.setValue(qRound(newY)); - vMoved = true; - } - if (qAbs(dy) > qApp->styleHints()->startDragDistance()) - stealY = true; - } - } - - if (q->xflick()) { - int dx = int(event->localPos().x() - pressPos.x()); - if (qAbs(dx) > qApp->styleHints()->startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) { - if (!hMoved) - hData.dragStartOffset = dx; - qreal newX = dx + hData.pressPos - hData.dragStartOffset; - const qreal minX = hData.dragMinBound; - const qreal maxX = hData.dragMaxBound; - if (newX > minX) - newX = minX + (newX - minX) / 2; - if (newX < maxX && maxX - minX <= 0) - newX = maxX + (newX - maxX) / 2; - if (boundsBehavior == QSGFlickable::StopAtBounds && (newX > minX || newX < maxX)) { - rejectX = true; - if (newX < maxX) { - newX = maxX; - rejectX = false; - } - if (newX > minX) { - newX = minX; - rejectX = false; - } - } - if (!rejectX && stealMouse) { - hData.move.setValue(qRound(newX)); - hMoved = true; - } - - if (qAbs(dx) > qApp->styleHints()->startDragDistance()) - stealX = true; - } - } - - stealMouse = stealX || stealY; - if (stealMouse) - q->setKeepMouseGrab(true); - - if (rejectY) { - vData.velocityBuffer.clear(); - vData.velocity = 0; - } - if (rejectX) { - hData.velocityBuffer.clear(); - hData.velocity = 0; - } - - if (hMoved || vMoved) { - draggingStarting(); - q->movementStarting(); - q->viewportMoved(); - } - - if (!lastPos.isNull()) { - qreal elapsed = qreal(QSGItemPrivate::elapsed(lastPosTime)) / 1000.; - if (elapsed <= 0) - return; - QSGItemPrivate::restart(lastPosTime); - qreal dy = event->localPos().y()-lastPos.y(); - if (q->yflick() && !rejectY) - vData.addVelocitySample(dy/elapsed, maxVelocity); - qreal dx = event->localPos().x()-lastPos.x(); - if (q->xflick() && !rejectX) - hData.addVelocitySample(dx/elapsed, maxVelocity); - } - - lastPos = event->localPos(); -} - -void QSGFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event) -{ - Q_Q(QSGFlickable); - stealMouse = false; - q->setKeepMouseGrab(false); - pressed = false; - - // if we drag then pause before release we should not cause a flick. - qint64 elapsed = QSGItemPrivate::elapsed(lastPosTime); - - vData.updateVelocity(); - hData.updateVelocity(); - - draggingEnding(); - - if (!lastPosTime.isValid()) - return; - - vTime = timeline.time(); - - qreal velocity = elapsed < 100 ? vData.velocity : 0; - if (vData.atBeginning || vData.atEnd) - velocity /= 2; - if (q->yflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold) { - velocityTimeline.reset(vData.smoothVelocity); - vData.smoothVelocity.setValue(-velocity); - flickY(velocity); - } else { - fixupY(); - } - - velocity = elapsed < 100 ? hData.velocity : 0; - if (hData.atBeginning || hData.atEnd) - velocity /= 2; - if (q->xflick() && qAbs(velocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold) { - velocityTimeline.reset(hData.smoothVelocity); - hData.smoothVelocity.setValue(-velocity); - flickX(velocity); - } else { - fixupX(); - } - - if (!timeline.isActive()) - q->movementEnding(); -} - -void QSGFlickable::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGFlickable); - if (d->interactive) { - if (!d->pressed) - d->handleMousePressEvent(event); - event->accept(); - } else { - QSGItem::mousePressEvent(event); - } -} - -void QSGFlickable::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QSGFlickable); - if (d->interactive) { - d->handleMouseMoveEvent(event); - event->accept(); - } else { - QSGItem::mouseMoveEvent(event); - } -} - -void QSGFlickable::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGFlickable); - if (d->interactive) { - d->clearDelayedPress(); - d->handleMouseReleaseEvent(event); - event->accept(); - ungrabMouse(); - } else { - QSGItem::mouseReleaseEvent(event); - } -} - -void QSGFlickable::wheelEvent(QWheelEvent *event) -{ - Q_D(QSGFlickable); - if (!d->interactive) { - QSGItem::wheelEvent(event); - } else if (yflick() && event->orientation() == Qt::Vertical) { - bool valid = false; - if (event->delta() > 0 && contentY() > -minYExtent()) { - d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4)); - valid = true; - } else if (event->delta() < 0 && contentY() < -maxYExtent()) { - d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4)); - valid = true; - } - if (valid) { - d->vData.flicking = false; - d->flickY(d->vData.velocity); - if (d->vData.flicking) { - d->vMoved = true; - movementStarting(); - } - event->accept(); - } - } else if (xflick() && event->orientation() == Qt::Horizontal) { - bool valid = false; - if (event->delta() > 0 && contentX() > -minXExtent()) { - d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4)); - valid = true; - } else if (event->delta() < 0 && contentX() < -maxXExtent()) { - d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4)); - valid = true; - } - if (valid) { - d->hData.flicking = false; - d->flickX(d->hData.velocity); - if (d->hData.flicking) { - d->hMoved = true; - movementStarting(); - } - event->accept(); - } - } else { - QSGItem::wheelEvent(event); - } -} - -bool QSGFlickablePrivate::isOutermostPressDelay() const -{ - Q_Q(const QSGFlickable); - QSGItem *item = q->parentItem(); - while (item) { - QSGFlickable *flick = qobject_cast(item); - if (flick && flick->pressDelay() > 0 && flick->isInteractive()) - return false; - item = item->parentItem(); - } - - return true; -} - -void QSGFlickablePrivate::captureDelayedPress(QMouseEvent *event) -{ - Q_Q(QSGFlickable); - if (!q->canvas() || pressDelay <= 0) - return; - if (!isOutermostPressDelay()) - return; - delayedPressTarget = q->canvas()->mouseGrabberItem(); - delayedPressEvent = new QMouseEvent(*event); - delayedPressEvent->setAccepted(false); - delayedPressTimer.start(pressDelay, q); -} - -void QSGFlickablePrivate::clearDelayedPress() -{ - if (delayedPressEvent) { - delayedPressTimer.stop(); - delete delayedPressEvent; - delayedPressEvent = 0; - } -} - -//XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned. -void QSGFlickablePrivate::setViewportX(qreal x) -{ - contentItem->setX(pixelAligned ? qRound(x) : x); -} - -void QSGFlickablePrivate::setViewportY(qreal y) -{ - contentItem->setY(pixelAligned ? qRound(y) : y); -} - -void QSGFlickable::timerEvent(QTimerEvent *event) -{ - Q_D(QSGFlickable); - if (event->timerId() == d->delayedPressTimer.timerId()) { - d->delayedPressTimer.stop(); - if (d->delayedPressEvent) { - QSGItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0; - if (!grabber || grabber != this) { - // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) - // so we reset the grabber - if (canvas()->mouseGrabberItem() == d->delayedPressTarget) - d->delayedPressTarget->ungrabMouse(); - // Use the event handler that will take care of finding the proper item to propagate the event - QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent); - } - delete d->delayedPressEvent; - d->delayedPressEvent = 0; - } - } -} - -qreal QSGFlickable::minYExtent() const -{ - Q_D(const QSGFlickable); - return d->vData.startMargin; -} - -qreal QSGFlickable::minXExtent() const -{ - Q_D(const QSGFlickable); - return d->hData.startMargin; -} - -/* returns -ve */ -qreal QSGFlickable::maxXExtent() const -{ - Q_D(const QSGFlickable); - return width() - vWidth() - d->hData.endMargin; -} -/* returns -ve */ -qreal QSGFlickable::maxYExtent() const -{ - Q_D(const QSGFlickable); - return height() - vHeight() - d->vData.endMargin; -} - -void QSGFlickable::componentComplete() -{ - Q_D(QSGFlickable); - QSGItem::componentComplete(); - if (!d->hData.explicitValue && d->hData.startMargin != 0.) - setContentX(-minXExtent()); - if (!d->vData.explicitValue && d->vData.startMargin != 0.) - setContentY(-minYExtent()); -} - -void QSGFlickable::viewportMoved() -{ - Q_D(QSGFlickable); - - qreal prevX = d->lastFlickablePosition.x(); - qreal prevY = d->lastFlickablePosition.y(); - if (d->pressed || d->calcVelocity) { - int elapsed = QSGItemPrivate::restart(d->velocityTime); - if (elapsed > 0) { - qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed; - if (qAbs(horizontalVelocity) > 0) { - d->velocityTimeline.reset(d->hData.smoothVelocity); - d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing); - d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing); - } - qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed; - if (qAbs(verticalVelocity) > 0) { - d->velocityTimeline.reset(d->vData.smoothVelocity); - d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing); - d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing); - } - } - } else { - if (d->timeline.time() > d->vTime) { - d->velocityTimeline.clear(); - qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime); - qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime); - d->hData.smoothVelocity.setValue(horizontalVelocity); - d->vData.smoothVelocity.setValue(verticalVelocity); - } - } - - if (!d->vData.inOvershoot && !d->vData.fixingUp && d->vData.flicking - && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent()) - && qAbs(d->vData.smoothVelocity.value()) > 100) { - // Increase deceleration if we've passed a bound - d->vData.inOvershoot = true; - qreal maxDistance = d->overShootDistance(height()); - d->timeline.reset(d->vData.move); - d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance); - d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d)); - } - if (!d->hData.inOvershoot && !d->hData.fixingUp && d->hData.flicking - && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent()) - && qAbs(d->hData.smoothVelocity.value()) > 100) { - // Increase deceleration if we've passed a bound - d->hData.inOvershoot = true; - qreal maxDistance = d->overShootDistance(width()); - d->timeline.reset(d->hData.move); - d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance); - d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d)); - } - - d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value()); - - d->vTime = d->timeline.time(); - d->updateBeginningEnd(); -} - -void QSGFlickable::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) -{ - Q_D(QSGFlickable); - QSGItem::geometryChanged(newGeometry, oldGeometry); - - bool changed = false; - if (newGeometry.width() != oldGeometry.width()) { - if (xflick()) - changed = true; - if (d->hData.viewSize < 0) { - d->contentItem->setWidth(width()); - emit contentWidthChanged(); - } - // Make sure that we're entirely in view. - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupX(); - } - } - if (newGeometry.height() != oldGeometry.height()) { - if (yflick()) - changed = true; - if (d->vData.viewSize < 0) { - d->contentItem->setHeight(height()); - emit contentHeightChanged(); - } - // Make sure that we're entirely in view. - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupY(); - } - } - - if (changed) - d->updateBeginningEnd(); -} - -void QSGFlickable::cancelFlick() -{ - Q_D(QSGFlickable); - d->timeline.reset(d->hData.move); - d->timeline.reset(d->vData.move); - movementEnding(); -} - -void QSGFlickablePrivate::data_append(QDeclarativeListProperty *prop, QObject *o) -{ - QSGItem *i = qobject_cast(o); - if (i) { - i->setParentItem(static_cast(prop->data)->contentItem); - } else { - o->setParent(prop->object); // XXX todo - do we want this? - } -} - -int QSGFlickablePrivate::data_count(QDeclarativeListProperty *) -{ - // XXX todo - return 0; -} - -QObject *QSGFlickablePrivate::data_at(QDeclarativeListProperty *, int) -{ - // XXX todo - return 0; -} - -void QSGFlickablePrivate::data_clear(QDeclarativeListProperty *) -{ - // XXX todo -} - -QDeclarativeListProperty QSGFlickable::flickableData() -{ - Q_D(QSGFlickable); - return QDeclarativeListProperty(this, (void *)d, QSGFlickablePrivate::data_append, - QSGFlickablePrivate::data_count, - QSGFlickablePrivate::data_at, - QSGFlickablePrivate::data_clear); -} - -QDeclarativeListProperty QSGFlickable::flickableChildren() -{ - Q_D(QSGFlickable); - return QSGItemPrivate::get(d->contentItem)->children(); -} - -/*! - \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior - This property holds whether the surface may be dragged - beyond the Fickable's boundaries, or overshoot the - Flickable's boundaries when flicked. - - This enables the feeling that the edges of the view are soft, - rather than a hard physical boundary. - - The \c boundsBehavior can be one of: - - \list - \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary - of the flickable, and flicks will not overshoot. - \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary - of the Flickable, but flicks will not overshoot. - \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged - beyond the boundary of the Flickable, and can overshoot the - boundary when flicked. - \endlist -*/ -QSGFlickable::BoundsBehavior QSGFlickable::boundsBehavior() const -{ - Q_D(const QSGFlickable); - return d->boundsBehavior; -} - -void QSGFlickable::setBoundsBehavior(BoundsBehavior b) -{ - Q_D(QSGFlickable); - if (b == d->boundsBehavior) - return; - d->boundsBehavior = b; - emit boundsBehaviorChanged(); -} - -/*! - \qmlproperty real QtQuick2::Flickable::contentWidth - \qmlproperty real QtQuick2::Flickable::contentHeight - - The dimensions of the content (the surface controlled by Flickable). - This should typically be set to the combined size of the items placed in the - Flickable. - - The following snippet shows how these properties are used to display - an image that is larger than the Flickable item itself: - - \snippet doc/src/snippets/declarative/flickable.qml document - - In some cases, the the content dimensions can be automatically set - using the \l {Item::childrenRect.width}{childrenRect.width} - and \l {Item::childrenRect.height}{childrenRect.height} properties. -*/ -qreal QSGFlickable::contentWidth() const -{ - Q_D(const QSGFlickable); - return d->hData.viewSize; -} - -void QSGFlickable::setContentWidth(qreal w) -{ - Q_D(QSGFlickable); - if (d->hData.viewSize == w) - return; - d->hData.viewSize = w; - if (w < 0) - d->contentItem->setWidth(width()); - else - d->contentItem->setWidth(w); - d->hData.markExtentsDirty(); - // Make sure that we're entirely in view. - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupX(); - } else if (!d->pressed && d->hData.fixingUp) { - d->fixupMode = QSGFlickablePrivate::ExtentChanged; - d->fixupX(); - } - emit contentWidthChanged(); - d->updateBeginningEnd(); -} - -qreal QSGFlickable::contentHeight() const -{ - Q_D(const QSGFlickable); - return d->vData.viewSize; -} - -void QSGFlickable::setContentHeight(qreal h) -{ - Q_D(QSGFlickable); - if (d->vData.viewSize == h) - return; - d->vData.viewSize = h; - if (h < 0) - d->contentItem->setHeight(height()); - else - d->contentItem->setHeight(h); - d->vData.markExtentsDirty(); - // Make sure that we're entirely in view. - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupY(); - } else if (!d->pressed && d->vData.fixingUp) { - d->fixupMode = QSGFlickablePrivate::ExtentChanged; - d->fixupY(); - } - emit contentHeightChanged(); - d->updateBeginningEnd(); -} - -/*! - \qmlproperty real QtQuick2::Flickable::topMargin - \qmlproperty real QtQuick2::Flickable::leftMargin - \qmlproperty real QtQuick2::Flickable::bottomMargin - \qmlproperty real QtQuick2::Flickable::rightMargin - - These properties hold the margins around the content. This space is reserved - in addition to the contentWidth and contentHeight. -*/ - - -qreal QSGFlickable::topMargin() const -{ - Q_D(const QSGFlickable); - return d->vData.startMargin; -} - -void QSGFlickable::setTopMargin(qreal m) -{ - Q_D(QSGFlickable); - if (d->vData.startMargin == m) - return; - d->vData.startMargin = m; - d->vData.markExtentsDirty(); - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupY(); - } - emit topMarginChanged(); - d->updateBeginningEnd(); -} - -qreal QSGFlickable::bottomMargin() const -{ - Q_D(const QSGFlickable); - return d->vData.endMargin; -} - -void QSGFlickable::setBottomMargin(qreal m) -{ - Q_D(QSGFlickable); - if (d->vData.endMargin == m) - return; - d->vData.endMargin = m; - d->vData.markExtentsDirty(); - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupY(); - } - emit bottomMarginChanged(); - d->updateBeginningEnd(); -} - -qreal QSGFlickable::leftMargin() const -{ - Q_D(const QSGFlickable); - return d->hData.startMargin; -} - -void QSGFlickable::setLeftMargin(qreal m) -{ - Q_D(QSGFlickable); - if (d->hData.startMargin == m) - return; - d->hData.startMargin = m; - d->hData.markExtentsDirty(); - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupX(); - } - emit leftMarginChanged(); - d->updateBeginningEnd(); -} - -qreal QSGFlickable::rightMargin() const -{ - Q_D(const QSGFlickable); - return d->hData.endMargin; -} - -void QSGFlickable::setRightMargin(qreal m) -{ - Q_D(QSGFlickable); - if (d->hData.endMargin == m) - return; - d->hData.endMargin = m; - d->hData.markExtentsDirty(); - if (!d->pressed && !d->hData.moving && !d->vData.moving) { - d->fixupMode = QSGFlickablePrivate::Immediate; - d->fixupX(); - } - emit rightMarginChanged(); - d->updateBeginningEnd(); -} - -/*! - \qmlproperty real QtQuick2::Flickable::xOrigin - \qmlproperty real QtQuick2::Flickable::yOrigin - - These properties hold the origin of the content. This is usually (0,0), however - ListView and GridView may have an arbitrary origin due to delegate size variation, - or item insertion/removal outside the visible region. -*/ - -qreal QSGFlickable::yOrigin() const -{ - Q_D(const QSGFlickable); - return -minYExtent() + d->vData.startMargin; -} - -qreal QSGFlickable::xOrigin() const -{ - Q_D(const QSGFlickable); - return -minXExtent() + d->hData.startMargin; -} - - -/*! - \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center) - - Resizes the content to \a width x \a height about \a center. - - This does not scale the contents of the Flickable - it only resizes the \l contentWidth - and \l contentHeight. - - Resizing the content may result in the content being positioned outside - the bounds of the Flickable. Calling \l returnToBounds() will - move the content back within legal bounds. -*/ -void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center) -{ - Q_D(QSGFlickable); - if (w != d->hData.viewSize) { - qreal oldSize = d->hData.viewSize; - d->hData.viewSize = w; - d->contentItem->setWidth(w); - emit contentWidthChanged(); - if (center.x() != 0) { - qreal pos = center.x() * w / oldSize; - setContentX(contentX() + pos - center.x()); - } - } - if (h != d->vData.viewSize) { - qreal oldSize = d->vData.viewSize; - d->vData.viewSize = h; - d->contentItem->setHeight(h); - emit contentHeightChanged(); - if (center.y() != 0) { - qreal pos = center.y() * h / oldSize; - setContentY(contentY() + pos - center.y()); - } - } - d->updateBeginningEnd(); -} - -/*! - \qmlmethod QtQuick2::Flickable::returnToBounds() - - Ensures the content is within legal bounds. - - This may be called to ensure that the content is within legal bounds - after manually positioning the content. -*/ -void QSGFlickable::returnToBounds() -{ - Q_D(QSGFlickable); - d->fixupX(); - d->fixupY(); -} - -qreal QSGFlickable::vWidth() const -{ - Q_D(const QSGFlickable); - if (d->hData.viewSize < 0) - return width(); - else - return d->hData.viewSize; -} - -qreal QSGFlickable::vHeight() const -{ - Q_D(const QSGFlickable); - if (d->vData.viewSize < 0) - return height(); - else - return d->vData.viewSize; -} - -bool QSGFlickable::xflick() const -{ - Q_D(const QSGFlickable); - if (d->flickableDirection == QSGFlickable::AutoFlickDirection) - return vWidth() != width(); - return d->flickableDirection & QSGFlickable::HorizontalFlick; -} - -bool QSGFlickable::yflick() const -{ - Q_D(const QSGFlickable); - if (d->flickableDirection == QSGFlickable::AutoFlickDirection) - return vHeight() != height(); - return d->flickableDirection & QSGFlickable::VerticalFlick; -} - -void QSGFlickable::mouseUngrabEvent() -{ - Q_D(QSGFlickable); - if (d->pressed) { - // if our mouse grab has been removed (probably by another Flickable), - // fix our state - d->pressed = false; - d->draggingEnding(); - d->stealMouse = false; - setKeepMouseGrab(false); - } -} - -bool QSGFlickable::sendMouseEvent(QMouseEvent *event) -{ - Q_D(QSGFlickable); - QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); - - QSGCanvas *c = canvas(); - QSGItem *grabber = c ? c->mouseGrabberItem() : 0; - bool disabledItem = grabber && !grabber->isEnabled(); - bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) { - QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - - mouseEvent.setAccepted(false); - - switch (mouseEvent.type()) { - case QEvent::MouseMove: - d->handleMouseMoveEvent(&mouseEvent); - break; - case QEvent::MouseButtonPress: - if (d->pressed) // we are already pressed - this is a delayed replay - return false; - - d->handleMousePressEvent(&mouseEvent); - d->captureDelayedPress(event); - stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above - break; - case QEvent::MouseButtonRelease: - if (d->delayedPressEvent) { - // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay) - // so we reset the grabber - if (c->mouseGrabberItem() == d->delayedPressTarget) - d->delayedPressTarget->ungrabMouse(); - //Use the event handler that will take care of finding the proper item to propagate the event - QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent); - d->clearDelayedPress(); - // We send the release - canvas()->sendEvent(c->mouseGrabberItem(), event); - // And the event has been consumed - d->stealMouse = false; - d->pressed = false; - return true; - } - d->handleMouseReleaseEvent(&mouseEvent); - break; - default: - break; - } - grabber = qobject_cast(c->mouseGrabberItem()); - if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) { - d->clearDelayedPress(); - grabMouse(); - } - - return stealThisEvent || d->delayedPressEvent || disabledItem; - } else if (d->lastPosTime.isValid()) { - d->lastPosTime.invalidate(); - returnToBounds(); - } - if (event->type() == QEvent::MouseButtonRelease) { - d->lastPosTime.invalidate(); - d->clearDelayedPress(); - d->stealMouse = false; - d->pressed = false; - } - return false; -} - - -bool QSGFlickable::childMouseEventFilter(QSGItem *i, QEvent *e) -{ - Q_D(QSGFlickable); - if (!isVisible() || !d->interactive || !isEnabled()) - return QSGItem::childMouseEventFilter(i, e); - switch (e->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - return sendMouseEvent(static_cast(e)); - default: - break; - } - - return QSGItem::childMouseEventFilter(i, e); -} - -/*! - \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity - This property holds the maximum velocity that the user can flick the view in pixels/second. - - The default value is platform dependent. -*/ -qreal QSGFlickable::maximumFlickVelocity() const -{ - Q_D(const QSGFlickable); - return d->maxVelocity; -} - -void QSGFlickable::setMaximumFlickVelocity(qreal v) -{ - Q_D(QSGFlickable); - if (v == d->maxVelocity) - return; - d->maxVelocity = v; - emit maximumFlickVelocityChanged(); -} - -/*! - \qmlproperty real QtQuick2::Flickable::flickDeceleration - This property holds the rate at which a flick will decelerate. - - The default value is platform dependent. -*/ -qreal QSGFlickable::flickDeceleration() const -{ - Q_D(const QSGFlickable); - return d->deceleration; -} - -void QSGFlickable::setFlickDeceleration(qreal deceleration) -{ - Q_D(QSGFlickable); - if (deceleration == d->deceleration) - return; - d->deceleration = deceleration; - emit flickDecelerationChanged(); -} - -bool QSGFlickable::isFlicking() const -{ - Q_D(const QSGFlickable); - return d->hData.flicking || d->vData.flicking; -} - -/*! - \qmlproperty bool QtQuick2::Flickable::flicking - \qmlproperty bool QtQuick2::Flickable::flickingHorizontally - \qmlproperty bool QtQuick2::Flickable::flickingVertically - - These properties describe whether the view is currently moving horizontally, - vertically or in either direction, due to the user flicking the view. -*/ -bool QSGFlickable::isFlickingHorizontally() const -{ - Q_D(const QSGFlickable); - return d->hData.flicking; -} - -bool QSGFlickable::isFlickingVertically() const -{ - Q_D(const QSGFlickable); - return d->vData.flicking; -} - -/*! - \qmlproperty bool QtQuick2::Flickable::dragging - \qmlproperty bool QtQuick2::Flickable::draggingHorizontally - \qmlproperty bool QtQuick2::Flickable::draggingVertically - - These properties describe whether the view is currently moving horizontally, - vertically or in either direction, due to the user dragging the view. -*/ -bool QSGFlickable::isDragging() const -{ - Q_D(const QSGFlickable); - return d->hData.dragging || d->vData.dragging; -} - -bool QSGFlickable::isDraggingHorizontally() const -{ - Q_D(const QSGFlickable); - return d->hData.dragging; -} - -bool QSGFlickable::isDraggingVertically() const -{ - Q_D(const QSGFlickable); - return d->vData.dragging; -} - -void QSGFlickablePrivate::draggingStarting() -{ - Q_Q(QSGFlickable); - bool wasDragging = hData.dragging || vData.dragging; - if (hMoved && !hData.dragging) { - hData.dragging = true; - emit q->draggingHorizontallyChanged(); - } - if (vMoved && !vData.dragging) { - vData.dragging = true; - emit q->draggingVerticallyChanged(); - } - if (!wasDragging && (hData.dragging || vData.dragging)) { - emit q->draggingChanged(); - emit q->dragStarted(); - } -} - -void QSGFlickablePrivate::draggingEnding() -{ - Q_Q(QSGFlickable); - bool wasDragging = hData.dragging || vData.dragging; - if (hData.dragging) { - hData.dragging = false; - emit q->draggingHorizontallyChanged(); - } - if (vData.dragging) { - vData.dragging = false; - emit q->draggingVerticallyChanged(); - } - if (wasDragging && !hData.dragging && !vData.dragging) { - emit q->draggingChanged(); - emit q->dragEnded(); - } -} - -/*! - \qmlproperty int QtQuick2::Flickable::pressDelay - - This property holds the time to delay (ms) delivering a press to - children of the Flickable. This can be useful where reacting - to a press before a flicking action has undesirable effects. - - If the flickable is dragged/flicked before the delay times out - the press event will not be delivered. If the button is released - within the timeout, both the press and release will be delivered. - - Note that for nested Flickables with pressDelay set, the pressDelay of - inner Flickables is overridden by the outermost Flickable. -*/ -int QSGFlickable::pressDelay() const -{ - Q_D(const QSGFlickable); - return d->pressDelay; -} - -void QSGFlickable::setPressDelay(int delay) -{ - Q_D(QSGFlickable); - if (d->pressDelay == delay) - return; - d->pressDelay = delay; - emit pressDelayChanged(); -} - -/*! - \qmlproperty bool QtQuick2::Flickable::moving - \qmlproperty bool QtQuick2::Flickable::movingHorizontally - \qmlproperty bool QtQuick2::Flickable::movingVertically - - These properties describe whether the view is currently moving horizontally, - vertically or in either direction, due to the user either dragging or - flicking the view. -*/ - -bool QSGFlickable::isMoving() const -{ - Q_D(const QSGFlickable); - return d->hData.moving || d->vData.moving; -} - -bool QSGFlickable::isMovingHorizontally() const -{ - Q_D(const QSGFlickable); - return d->hData.moving; -} - -bool QSGFlickable::isMovingVertically() const -{ - Q_D(const QSGFlickable); - return d->vData.moving; -} - -void QSGFlickable::movementStarting() -{ - Q_D(QSGFlickable); - if (d->hMoved && !d->hData.moving) { - d->hData.moving = true; - emit movingChanged(); - emit movingHorizontallyChanged(); - if (!d->vData.moving) - emit movementStarted(); - } - else if (d->vMoved && !d->vData.moving) { - d->vData.moving = true; - emit movingChanged(); - emit movingVerticallyChanged(); - if (!d->hData.moving) - emit movementStarted(); - } -} - -void QSGFlickable::movementEnding() -{ - Q_D(QSGFlickable); - movementXEnding(); - movementYEnding(); - d->hData.smoothVelocity.setValue(0); - d->vData.smoothVelocity.setValue(0); -} - -void QSGFlickable::movementXEnding() -{ - Q_D(QSGFlickable); - if (d->hData.flicking) { - d->hData.flicking = false; - emit flickingChanged(); - emit flickingHorizontallyChanged(); - if (!d->vData.flicking) - emit flickEnded(); - } - if (!d->pressed && !d->stealMouse) { - if (d->hData.moving) { - d->hData.moving = false; - d->hMoved = false; - emit movingChanged(); - emit movingHorizontallyChanged(); - if (!d->vData.moving) - emit movementEnded(); - } - } - d->hData.fixingUp = false; -} - -void QSGFlickable::movementYEnding() -{ - Q_D(QSGFlickable); - if (d->vData.flicking) { - d->vData.flicking = false; - emit flickingChanged(); - emit flickingVerticallyChanged(); - if (!d->hData.flicking) - emit flickEnded(); - } - if (!d->pressed && !d->stealMouse) { - if (d->vData.moving) { - d->vData.moving = false; - d->vMoved = false; - emit movingChanged(); - emit movingVerticallyChanged(); - if (!d->hData.moving) - emit movementEnded(); - } - } - d->vData.fixingUp = false; -} - -void QSGFlickablePrivate::updateVelocity() -{ - Q_Q(QSGFlickable); - emit q->horizontalVelocityChanged(); - emit q->verticalVelocityChanged(); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgflickable_p.h b/src/declarative/items/qsgflickable_p.h deleted file mode 100644 index 54581a5932..0000000000 --- a/src/declarative/items/qsgflickable_p.h +++ /dev/null @@ -1,277 +0,0 @@ -// Commit: 1bcddaaf318fc37c71c5191913f3487c49444ec6 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGFLICKABLE_P_H -#define QSGFLICKABLE_P_H - -#include "qsgitem.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGFlickablePrivate; -class QSGFlickableVisibleArea; -class Q_AUTOTEST_EXPORT QSGFlickable : public QSGItem -{ - Q_OBJECT - - Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged) - Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged) - Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged) - Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged) - Q_PROPERTY(QSGItem *contentItem READ contentItem CONSTANT) - - Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) - Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) - Q_PROPERTY(qreal yOrigin READ yOrigin NOTIFY yOriginChanged) - - Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) - Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) - Q_PROPERTY(qreal xOrigin READ xOrigin NOTIFY xOriginChanged) - - Q_PROPERTY(qreal horizontalVelocity READ horizontalVelocity NOTIFY horizontalVelocityChanged) - Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged) - - Q_PROPERTY(BoundsBehavior boundsBehavior READ boundsBehavior WRITE setBoundsBehavior NOTIFY boundsBehaviorChanged) - Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged) - Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) - Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) - Q_PROPERTY(bool movingHorizontally READ isMovingHorizontally NOTIFY movingHorizontallyChanged) - Q_PROPERTY(bool movingVertically READ isMovingVertically NOTIFY movingVerticallyChanged) - Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) - Q_PROPERTY(bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged) - Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged) - Q_PROPERTY(bool dragging READ isDragging NOTIFY draggingChanged) - Q_PROPERTY(bool draggingHorizontally READ isDraggingHorizontally NOTIFY draggingHorizontallyChanged) - Q_PROPERTY(bool draggingVertically READ isDraggingVertically NOTIFY draggingVerticallyChanged) - Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE setFlickableDirection NOTIFY flickableDirectionChanged) - - Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) - Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay NOTIFY pressDelayChanged) - - Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY isAtBoundaryChanged) - Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY isAtBoundaryChanged) - Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY isAtBoundaryChanged) - Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY isAtBoundaryChanged) - - Q_PROPERTY(QSGFlickableVisibleArea *visibleArea READ visibleArea CONSTANT) - - Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged) - - Q_PROPERTY(QDeclarativeListProperty flickableData READ flickableData) - Q_PROPERTY(QDeclarativeListProperty flickableChildren READ flickableChildren) - Q_CLASSINFO("DefaultProperty", "flickableData") - - Q_ENUMS(FlickableDirection) - Q_ENUMS(BoundsBehavior) - -public: - QSGFlickable(QSGItem *parent=0); - ~QSGFlickable(); - - QDeclarativeListProperty flickableData(); - QDeclarativeListProperty flickableChildren(); - - enum BoundsBehavior { StopAtBounds, DragOverBounds, DragAndOvershootBounds }; - BoundsBehavior boundsBehavior() const; - void setBoundsBehavior(BoundsBehavior); - - qreal contentWidth() const; - void setContentWidth(qreal); - - qreal contentHeight() const; - void setContentHeight(qreal); - - qreal contentX() const; - virtual void setContentX(qreal pos); - - qreal contentY() const; - virtual void setContentY(qreal pos); - - qreal topMargin() const; - void setTopMargin(qreal m); - - qreal bottomMargin() const; - void setBottomMargin(qreal m); - - qreal leftMargin() const; - void setLeftMargin(qreal m); - - qreal rightMargin() const; - void setRightMargin(qreal m); - - virtual qreal yOrigin() const; - virtual qreal xOrigin() const; - - bool isMoving() const; - bool isMovingHorizontally() const; - bool isMovingVertically() const; - bool isFlicking() const; - bool isFlickingHorizontally() const; - bool isFlickingVertically() const; - bool isDragging() const; - bool isDraggingHorizontally() const; - bool isDraggingVertically() const; - - int pressDelay() const; - void setPressDelay(int delay); - - qreal maximumFlickVelocity() const; - void setMaximumFlickVelocity(qreal); - - qreal flickDeceleration() const; - void setFlickDeceleration(qreal); - - bool isInteractive() const; - void setInteractive(bool); - - qreal horizontalVelocity() const; - qreal verticalVelocity() const; - - bool isAtXEnd() const; - bool isAtXBeginning() const; - bool isAtYEnd() const; - bool isAtYBeginning() const; - - QSGItem *contentItem(); - - enum FlickableDirection { AutoFlickDirection=0x00, HorizontalFlick=0x01, VerticalFlick=0x02, HorizontalAndVerticalFlick=0x03 }; - FlickableDirection flickableDirection() const; - void setFlickableDirection(FlickableDirection); - - bool pixelAligned() const; - void setPixelAligned(bool align); - - Q_INVOKABLE void resizeContent(qreal w, qreal h, QPointF center); - Q_INVOKABLE void returnToBounds(); - -Q_SIGNALS: - void contentWidthChanged(); - void contentHeightChanged(); - void contentXChanged(); - void contentYChanged(); - void topMarginChanged(); - void bottomMarginChanged(); - void leftMarginChanged(); - void rightMarginChanged(); - void yOriginChanged(); - void xOriginChanged(); - void movingChanged(); - void movingHorizontallyChanged(); - void movingVerticallyChanged(); - void flickingChanged(); - void flickingHorizontallyChanged(); - void flickingVerticallyChanged(); - void draggingChanged(); - void draggingHorizontallyChanged(); - void draggingVerticallyChanged(); - void horizontalVelocityChanged(); - void verticalVelocityChanged(); - void isAtBoundaryChanged(); - void flickableDirectionChanged(); - void interactiveChanged(); - void boundsBehaviorChanged(); - void maximumFlickVelocityChanged(); - void flickDecelerationChanged(); - void pressDelayChanged(); - void movementStarted(); - void movementEnded(); - void flickStarted(); - void flickEnded(); - void dragStarted(); - void dragEnded(); - void pixelAlignedChanged(); - -protected: - virtual bool childMouseEventFilter(QSGItem *, QEvent *); - virtual void mousePressEvent(QMouseEvent *event); - virtual void mouseMoveEvent(QMouseEvent *event); - virtual void mouseReleaseEvent(QMouseEvent *event); - virtual void wheelEvent(QWheelEvent *event); - virtual void timerEvent(QTimerEvent *event); - - QSGFlickableVisibleArea *visibleArea(); - -protected Q_SLOTS: - virtual void ticked(); - void movementStarting(); - void movementEnding(); - -protected: - void movementXEnding(); - void movementYEnding(); - virtual qreal minXExtent() const; - virtual qreal minYExtent() const; - virtual qreal maxXExtent() const; - virtual qreal maxYExtent() const; - qreal vWidth() const; - qreal vHeight() const; - virtual void componentComplete(); - virtual void viewportMoved(); - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - void mouseUngrabEvent(); - bool sendMouseEvent(QMouseEvent *event); - - bool xflick() const; - bool yflick() const; - void cancelFlick(); - -protected: - QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent); - -private: - Q_DISABLE_COPY(QSGFlickable) - Q_DECLARE_PRIVATE(QSGFlickable) - friend class QSGFlickableVisibleArea; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGFlickable) - -QT_END_HEADER - -#endif // QSGFLICKABLE_P_H diff --git a/src/declarative/items/qsgflickable_p_p.h b/src/declarative/items/qsgflickable_p_p.h deleted file mode 100644 index b7a91a072b..0000000000 --- a/src/declarative/items/qsgflickable_p_p.h +++ /dev/null @@ -1,262 +0,0 @@ -// Commit: 160f1867868cdea916923652b00484ed11f90aaa -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGFLICKABLE_P_P_H -#define QSGFLICKABLE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgflickable_p.h" -#include "qsgitem_p.h" -#include "qsgitemchangelistener_p.h" - -#include -#include -#include "qplatformdefs.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -// Really slow flicks can be annoying. -const qreal MinimumFlickVelocity = 75.0; - -class QSGFlickableVisibleArea; -class QSGFlickablePrivate : public QSGItemPrivate, public QSGItemChangeListener -{ - Q_DECLARE_PUBLIC(QSGFlickable) - -public: - static inline QSGFlickablePrivate *get(QSGFlickable *o) { return o->d_func(); } - - QSGFlickablePrivate(); - void init(); - - struct Velocity : public QDeclarativeTimeLineValue - { - Velocity(QSGFlickablePrivate *p) - : parent(p) {} - virtual void setValue(qreal v) { - if (v != value()) { - QDeclarativeTimeLineValue::setValue(v); - parent->updateVelocity(); - } - } - QSGFlickablePrivate *parent; - }; - - struct AxisData { - AxisData(QSGFlickablePrivate *fp, void (QSGFlickablePrivate::*func)(qreal)) - : move(fp, func), viewSize(-1), startMargin(0), endMargin(0) - , smoothVelocity(fp), atEnd(false), atBeginning(true) - , fixingUp(false), inOvershoot(false), moving(false), flicking(false) - , dragging(false), extentsChanged(false) - , explicitValue(false), minExtentDirty(true), maxExtentDirty(true) - {} - - void reset() { - velocityBuffer.clear(); - dragStartOffset = 0; - fixingUp = false; - inOvershoot = false; - } - - void markExtentsDirty() { - minExtentDirty = true; - maxExtentDirty = true; - extentsChanged = true; - } - - void addVelocitySample(qreal v, qreal maxVelocity); - void updateVelocity(); - - QDeclarativeTimeLineValueProxy move; - qreal viewSize; - qreal pressPos; - qreal dragStartOffset; - qreal dragMinBound; - qreal dragMaxBound; - qreal velocity; - qreal flickTarget; - qreal startMargin; - qreal endMargin; - QSGFlickablePrivate::Velocity smoothVelocity; - QPODVector velocityBuffer; - bool atEnd : 1; - bool atBeginning : 1; - bool fixingUp : 1; - bool inOvershoot : 1; - bool moving : 1; - bool flicking : 1; - bool dragging : 1; - bool extentsChanged : 1; - bool explicitValue : 1; - mutable bool minExtentDirty : 1; - mutable bool maxExtentDirty : 1; - }; - - void flickX(qreal velocity); - void flickY(qreal velocity); - virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); - - void fixupX(); - void fixupY(); - virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - - void updateBeginningEnd(); - - bool isOutermostPressDelay() const; - void captureDelayedPress(QMouseEvent *event); - void clearDelayedPress(); - - void setViewportX(qreal x); - void setViewportY(qreal y); - - qreal overShootDistance(qreal size); - - void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &); - - void draggingStarting(); - void draggingEnding(); - -public: - QSGItem *contentItem; - - AxisData hData; - AxisData vData; - - QDeclarativeTimeLine timeline; - bool hMoved : 1; - bool vMoved : 1; - bool stealMouse : 1; - bool pressed : 1; - bool interactive : 1; - bool calcVelocity : 1; - bool pixelAligned : 1; - QElapsedTimer lastPosTime; - QPointF lastPos; - QPointF pressPos; - QElapsedTimer pressTime; - qreal deceleration; - qreal maxVelocity; - QElapsedTimer velocityTime; - QPointF lastFlickablePosition; - qreal reportedVelocitySmoothing; - QMouseEvent *delayedPressEvent; - QSGItem *delayedPressTarget; - QBasicTimer delayedPressTimer; - int pressDelay; - int fixupDuration; - - enum FixupMode { Normal, Immediate, ExtentChanged }; - FixupMode fixupMode; - - static void fixupY_callback(void *); - static void fixupX_callback(void *); - - void updateVelocity(); - int vTime; - QDeclarativeTimeLine velocityTimeline; - QSGFlickableVisibleArea *visibleArea; - QSGFlickable::FlickableDirection flickableDirection; - QSGFlickable::BoundsBehavior boundsBehavior; - - void handleMousePressEvent(QMouseEvent *); - void handleMouseMoveEvent(QMouseEvent *); - void handleMouseReleaseEvent(QMouseEvent *); - - // flickableData property - static void data_append(QDeclarativeListProperty *, QObject *); - static int data_count(QDeclarativeListProperty *); - static QObject *data_at(QDeclarativeListProperty *, int); - static void data_clear(QDeclarativeListProperty *); -}; - -class QSGFlickableVisibleArea : public QObject -{ - Q_OBJECT - - Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged) - Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged) - Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged) - Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged) - -public: - QSGFlickableVisibleArea(QSGFlickable *parent=0); - - qreal xPosition() const; - qreal widthRatio() const; - qreal yPosition() const; - qreal heightRatio() const; - - void updateVisible(); - -signals: - void xPositionChanged(qreal xPosition); - void yPositionChanged(qreal yPosition); - void widthRatioChanged(qreal widthRatio); - void heightRatioChanged(qreal heightRatio); - -private: - QSGFlickable *flickable; - qreal m_xPosition; - qreal m_widthRatio; - qreal m_yPosition; - qreal m_heightRatio; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGFlickableVisibleArea) - -#endif // QSGFLICKABLE_P_P_H diff --git a/src/declarative/items/qsgflipable.cpp b/src/declarative/items/qsgflipable.cpp deleted file mode 100644 index ac68b3171c..0000000000 --- a/src/declarative/items/qsgflipable.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgflipable_p.h" -#include "qsgitem_p.h" - -#include - -#include - -QT_BEGIN_NAMESPACE - -// XXX todo - i think this needs work and a bit of a re-think - -class QSGLocalTransform : public QSGTransform -{ - Q_OBJECT -public: - QSGLocalTransform(QObject *parent) : QSGTransform(parent) {} - - void setTransform(const QTransform &t) { - transform = t; - update(); - } - virtual void applyTo(QMatrix4x4 *matrix) const { - *matrix *= transform; - } -private: - QTransform transform; -}; - -class QSGFlipablePrivate : public QSGItemPrivate -{ - Q_DECLARE_PUBLIC(QSGFlipable) -public: - QSGFlipablePrivate() : current(QSGFlipable::Front), front(0), back(0), sideDirty(false) {} - - virtual void transformChanged(); - void updateSide(); - void setBackTransform(); - - QSGFlipable::Side current; - QDeclarativeGuard backTransform; - QDeclarativeGuard front; - QDeclarativeGuard back; - - bool sideDirty; - bool wantBackXFlipped; - bool wantBackYFlipped; -}; - -/*! - \qmlclass Flipable QSGFlipable - \inqmlmodule QtQuick 2 - \ingroup qml-basic-interaction-elements - \brief The Flipable item provides a surface that can be flipped. - \inherits Item - - Flipable is an item that can be visibly "flipped" between its front and - back sides, like a card. It is used together with \l Rotation, \l State - and \l Transition elements to produce a flipping effect. - - The \l front and \l back properties are used to hold the items that are - shown respectively on the front and back sides of the flipable item. - - \section1 Example Usage - - The following example shows a Flipable item that flips whenever it is - clicked, rotating about the y-axis. - - This flipable item has a \c flipped boolean property that is toggled - whenever the MouseArea within the flipable is clicked. When - \c flipped is true, the item changes to the "back" state; in this - state, the \c angle of the \l Rotation item is changed to 180 - degrees to produce the flipping effect. When \c flipped is false, the - item reverts to the default state, in which the \c angle value is 0. - - \snippet doc/src/snippets/declarative/flipable/flipable.qml 0 - - \image flipable.gif - - The \l Transition creates the animation that changes the angle over - four seconds. When the item changes between its "back" and - default states, the NumberAnimation animates the angle between - its old and new values. - - See \l {QML States} for details on state changes and the default - state, and \l {QML Animation and Transitions} for more information on how - animations work within transitions. - - \sa {declarative/ui-components/flipable}{Flipable example} -*/ -QSGFlipable::QSGFlipable(QSGItem *parent) -: QSGItem(*(new QSGFlipablePrivate), parent) -{ -} - -QSGFlipable::~QSGFlipable() -{ -} - -/*! - \qmlproperty Item QtQuick2::Flipable::front - \qmlproperty Item QtQuick2::Flipable::back - - The front and back sides of the flipable. -*/ - -QSGItem *QSGFlipable::front() -{ - Q_D(const QSGFlipable); - return d->front; -} - -void QSGFlipable::setFront(QSGItem *front) -{ - Q_D(QSGFlipable); - if (d->front) { - qmlInfo(this) << tr("front is a write-once property"); - return; - } - d->front = front; - d->front->setParentItem(this); - if (Back == d->current) - d->front->setOpacity(0.); - emit frontChanged(); -} - -QSGItem *QSGFlipable::back() -{ - Q_D(const QSGFlipable); - return d->back; -} - -void QSGFlipable::setBack(QSGItem *back) -{ - Q_D(QSGFlipable); - if (d->back) { - qmlInfo(this) << tr("back is a write-once property"); - return; - } - if (back == 0) - return; - d->back = back; - d->back->setParentItem(this); - - d->backTransform = new QSGLocalTransform(d->back); - d->backTransform->prependToItem(d->back); - - if (Front == d->current) - d->back->setOpacity(0.); - connect(back, SIGNAL(widthChanged()), - this, SLOT(retransformBack())); - connect(back, SIGNAL(heightChanged()), - this, SLOT(retransformBack())); - emit backChanged(); -} - -void QSGFlipable::retransformBack() -{ - Q_D(QSGFlipable); - if (d->current == QSGFlipable::Back && d->back) - d->setBackTransform(); -} - -/*! - \qmlproperty enumeration QtQuick2::Flipable::side - - The side of the Flipable currently visible. Possible values are \c - Flipable.Front and \c Flipable.Back. -*/ -QSGFlipable::Side QSGFlipable::side() const -{ - Q_D(const QSGFlipable); - - const_cast(d)->updateSide(); - return d->current; -} - -void QSGFlipablePrivate::transformChanged() -{ - Q_Q(QSGFlipable); - - if (!sideDirty) { - sideDirty = true; - q->polish(); - } - - QSGItemPrivate::transformChanged(); -} - -void QSGFlipable::updatePolish() -{ - Q_D(QSGFlipable); - d->updateSide(); -} - -// determination on the currently visible side of the flipable -// has to be done on the complete scene transform to give -// correct results. -void QSGFlipablePrivate::updateSide() -{ - Q_Q(QSGFlipable); - - if (!sideDirty) - return; - - sideDirty = false; - - QTransform sceneTransform; - itemToParentTransform(sceneTransform); - - QPointF p1(0, 0); - QPointF p2(1, 0); - QPointF p3(1, 1); - - QPointF scenep1 = sceneTransform.map(p1); - QPointF scenep2 = sceneTransform.map(p2); - QPointF scenep3 = sceneTransform.map(p3); -#if 0 - p1 = q->mapToParent(p1); - p2 = q->mapToParent(p2); - p3 = q->mapToParent(p3); -#endif - - qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) - - (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x()); - - wantBackYFlipped = scenep1.x() >= scenep2.x(); - wantBackXFlipped = scenep2.y() >= scenep3.y(); - - QSGFlipable::Side newSide; - if (cross > 0) { - newSide = QSGFlipable::Back; - } else { - newSide = QSGFlipable::Front; - } - - if (newSide != current) { - current = newSide; - if (current == QSGFlipable::Back && back) - setBackTransform(); - if (front) - front->setOpacity((current==QSGFlipable::Front)?1.:0.); - if (back) - back->setOpacity((current==QSGFlipable::Back)?1.:0.); - emit q->sideChanged(); - } -} - -/* Depends on the width/height of the back item, and so needs reevaulating - if those change. -*/ -void QSGFlipablePrivate::setBackTransform() -{ - QTransform mat; - mat.translate(back->width()/2,back->height()/2); - if (back->width() && wantBackYFlipped) - mat.rotate(180, Qt::YAxis); - if (back->height() && wantBackXFlipped) - mat.rotate(180, Qt::XAxis); - mat.translate(-back->width()/2,-back->height()/2); - - if (backTransform) - backTransform->setTransform(mat); -} - -QT_END_NAMESPACE - -#include "qsgflipable.moc" diff --git a/src/declarative/items/qsgflipable_p.h b/src/declarative/items/qsgflipable_p.h deleted file mode 100644 index 4c83932152..0000000000 --- a/src/declarative/items/qsgflipable_p.h +++ /dev/null @@ -1,104 +0,0 @@ -// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGFLIPABLE_P_H -#define QSGFLIPABLE_P_H - -#include "qsgitem.h" - -#include -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGFlipablePrivate; -class Q_AUTOTEST_EXPORT QSGFlipable : public QSGItem -{ - Q_OBJECT - - Q_ENUMS(Side) - Q_PROPERTY(QSGItem *front READ front WRITE setFront NOTIFY frontChanged) - Q_PROPERTY(QSGItem *back READ back WRITE setBack NOTIFY backChanged) - Q_PROPERTY(Side side READ side NOTIFY sideChanged) - //### flipAxis - //### flipRotation -public: - QSGFlipable(QSGItem *parent=0); - ~QSGFlipable(); - - QSGItem *front(); - void setFront(QSGItem *); - - QSGItem *back(); - void setBack(QSGItem *); - - enum Side { Front, Back }; - Side side() const; - -Q_SIGNALS: - void frontChanged(); - void backChanged(); - void sideChanged(); - -protected: - virtual void updatePolish(); - -private Q_SLOTS: - void retransformBack(); - -private: - Q_DISABLE_COPY(QSGFlipable) - Q_DECLARE_PRIVATE(QSGFlipable) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGFlipable) - -QT_END_HEADER - -#endif // QSGFLIPABLE_P_H diff --git a/src/declarative/items/qsgfocusscope.cpp b/src/declarative/items/qsgfocusscope.cpp deleted file mode 100644 index 2018d5ce7d..0000000000 --- a/src/declarative/items/qsgfocusscope.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgfocusscope_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \qmlclass FocusScope QSGFocusScope - \inqmlmodule QtQuick 2 - \ingroup qml-basic-interaction-elements - - \brief The FocusScope object explicitly creates a focus scope. - \inherits Item - - Focus scopes assist in keyboard focus handling when building reusable QML - components. All the details are covered in the - \l {qmlfocus}{keyboard focus documentation}. - - \sa {declarative/keyinteraction/focus}{Keyboard focus example} -*/ -QSGFocusScope::QSGFocusScope(QSGItem *parent) -: QSGItem(parent) -{ - setFlag(ItemIsFocusScope); -} - -QSGFocusScope::~QSGFocusScope() -{ -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgfocusscope_p.h b/src/declarative/items/qsgfocusscope_p.h deleted file mode 100644 index 222269bec9..0000000000 --- a/src/declarative/items/qsgfocusscope_p.h +++ /dev/null @@ -1,68 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGFOCUSSCOPE_P_H -#define QSGFOCUSSCOPE_P_H - -#include "qsgitem.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class Q_AUTOTEST_EXPORT QSGFocusScope : public QSGItem -{ - Q_OBJECT -public: - QSGFocusScope(QSGItem *parent=0); - virtual ~QSGFocusScope(); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGFocusScope) - -QT_END_HEADER - -#endif // QSGFOCUSSCOPE_P_H diff --git a/src/declarative/items/qsggridview.cpp b/src/declarative/items/qsggridview.cpp deleted file mode 100644 index 2507a293de..0000000000 --- a/src/declarative/items/qsggridview.cpp +++ /dev/null @@ -1,1930 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsggridview_p.h" -#include "qsgvisualitemmodel_p.h" -#include "qsgflickable_p_p.h" -#include "qsgitemview_p_p.h" - -#include -#include - -#include -#include -#include -#include -#include "qplatformdefs.h" - -QT_BEGIN_NAMESPACE - -#ifndef QML_FLICK_SNAPONETHRESHOLD -#define QML_FLICK_SNAPONETHRESHOLD 30 -#endif - -//---------------------------------------------------------------------------- - -class FxGridItemSG : public FxViewItem -{ -public: - FxGridItemSG(QSGItem *i, QSGGridView *v, bool own) : FxViewItem(i, own), view(v) { - attached = static_cast(qmlAttachedPropertiesObject(item)); - if (attached) - static_cast(attached)->setView(view); - } - - ~FxGridItemSG() {} - - qreal position() const { - return rowPos(); - } - - qreal endPosition() const { - return endRowPos(); - } - - qreal size() const { - return view->flow() == QSGGridView::LeftToRight ? view->cellHeight() : view->cellWidth(); - } - - qreal sectionSize() const { - return 0.0; - } - - qreal rowPos() const { - if (view->flow() == QSGGridView::LeftToRight) - return item->y(); - else - return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-item->x() : item->x()); - } - - qreal colPos() const { - if (view->flow() == QSGGridView::LeftToRight) { - if (view->effectiveLayoutDirection() == Qt::RightToLeft) { - qreal colSize = view->cellWidth(); - int columns = view->width()/colSize; - return colSize * (columns-1) - item->x(); - } else { - return item->x(); - } - } else { - return item->y(); - } - } - qreal endRowPos() const { - if (view->flow() == QSGGridView::LeftToRight) { - return item->y() + view->cellHeight(); - } else { - if (view->effectiveLayoutDirection() == Qt::RightToLeft) - return -item->x(); - else - return item->x() + view->cellWidth(); - } - } - void setPosition(qreal col, qreal row) { - if (view->effectiveLayoutDirection() == Qt::RightToLeft) { - if (view->flow() == QSGGridView::LeftToRight) { - int columns = view->width()/view->cellWidth(); - item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row)); - } else { - item->setPos(QPointF(-view->cellWidth()-row, col)); - } - } else { - if (view->flow() == QSGGridView::LeftToRight) - item->setPos(QPointF(col, row)); - else - item->setPos(QPointF(row, col)); - } - } - bool contains(qreal x, qreal y) const { - return (x >= item->x() && x < item->x() + view->cellWidth() && - y >= item->y() && y < item->y() + view->cellHeight()); - } - - QSGGridView *view; -}; - -//---------------------------------------------------------------------------- - -class QSGGridViewPrivate : public QSGItemViewPrivate -{ - Q_DECLARE_PUBLIC(QSGGridView) - -public: - virtual Qt::Orientation layoutOrientation() const; - virtual bool isContentFlowReversed() const; - bool isRightToLeftTopToBottom() const; - - virtual qreal positionAt(int index) const; - virtual qreal endPositionAt(int index) const; - virtual qreal originPosition() const; - virtual qreal lastPosition() const; - - qreal rowSize() const; - qreal colSize() const; - qreal colPosAt(int modelIndex) const; - qreal rowPosAt(int modelIndex) const; - qreal snapPosAt(qreal pos) const; - FxViewItem *snapItemAt(qreal pos) const; - int snapIndex() const; - - virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer); - virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo); - virtual void visibleItemsChanged(); - - virtual FxViewItem *newViewItem(int index, QSGItem *item); - virtual void repositionPackageItemAt(QSGItem *item, int index); - virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem); - virtual void resetFirstItemPosition(); - virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards); - - virtual void createHighlight(); - virtual void updateHighlight(); - virtual void resetHighlightPosition(); - - virtual void setPosition(qreal pos); - virtual void layoutVisibleItems(); - bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *, InsertionsResult *); - - virtual qreal headerSize() const; - virtual qreal footerSize() const; - virtual bool showHeaderForIndex(int index) const; - virtual bool showFooterForIndex(int index) const; - virtual void updateHeader(); - virtual void updateFooter(); - - virtual void changedVisibleIndex(int newIndex); - virtual void initializeCurrentItem(); - - virtual void updateViewport(); - virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); - virtual void fixupPosition(); - virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); - - QSGGridView::Flow flow; - qreal cellWidth; - qreal cellHeight; - int columns; - QSGGridView::SnapMode snapMode; - - QSmoothedAnimation *highlightXAnimator; - QSmoothedAnimation *highlightYAnimator; - - QSGGridViewPrivate() - : flow(QSGGridView::LeftToRight) - , cellWidth(100), cellHeight(100), columns(1) - , snapMode(QSGGridView::NoSnap) - , highlightXAnimator(0), highlightYAnimator(0) - {} -}; - -Qt::Orientation QSGGridViewPrivate::layoutOrientation() const -{ - return flow == QSGGridView::LeftToRight ? Qt::Vertical : Qt::Horizontal; -} - -bool QSGGridViewPrivate::isContentFlowReversed() const -{ - return isRightToLeftTopToBottom(); -} - -bool QSGGridViewPrivate::isRightToLeftTopToBottom() const -{ - Q_Q(const QSGGridView); - return flow == QSGGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft; -} - -void QSGGridViewPrivate::changedVisibleIndex(int newIndex) -{ - visibleIndex = newIndex / columns * columns; -} - -void QSGGridViewPrivate::setPosition(qreal pos) -{ - Q_Q(QSGGridView); - if (flow == QSGGridView::LeftToRight) { - q->QSGFlickable::setContentY(pos); - q->QSGFlickable::setContentX(0); - } else { - if (q->effectiveLayoutDirection() == Qt::LeftToRight) - q->QSGFlickable::setContentX(pos); - else - q->QSGFlickable::setContentX(-pos-size()); - q->QSGFlickable::setContentY(0); - } -} - -qreal QSGGridViewPrivate::originPosition() const -{ - qreal pos = 0; - if (!visibleItems.isEmpty()) - pos = static_cast(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize(); - return pos; -} - -qreal QSGGridViewPrivate::lastPosition() const -{ - qreal pos = 0; - if (model && model->count()) { - // get end position of last item - pos = (rowPosAt(model->count() - 1) + rowSize()); - } - return pos; -} - -qreal QSGGridViewPrivate::positionAt(int index) const -{ - return rowPosAt(index); -} - -qreal QSGGridViewPrivate::endPositionAt(int index) const -{ - return rowPosAt(index) + rowSize(); -} - -qreal QSGGridViewPrivate::rowSize() const { - return flow == QSGGridView::LeftToRight ? cellHeight : cellWidth; -} -qreal QSGGridViewPrivate::colSize() const { - return flow == QSGGridView::LeftToRight ? cellWidth : cellHeight; -} - -qreal QSGGridViewPrivate::colPosAt(int modelIndex) const -{ - if (FxViewItem *item = visibleItem(modelIndex)) - return static_cast(item)->colPos(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - int count = (visibleIndex - modelIndex) % columns; - int col = static_cast(visibleItems.first())->colPos() / colSize(); - col = (columns - count + col) % columns; - return col * colSize(); - } else { - int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns; - return static_cast(visibleItems.last())->colPos() - count * colSize(); - } - } - return (modelIndex % columns) * colSize(); -} - -qreal QSGGridViewPrivate::rowPosAt(int modelIndex) const -{ - if (FxViewItem *item = visibleItem(modelIndex)) - return static_cast(item)->rowPos(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - FxGridItemSG *firstItem = static_cast(visibleItems.first()); - int firstCol = firstItem->colPos() / colSize(); - int col = visibleIndex - modelIndex + (columns - firstCol - 1); - int rows = col / columns; - return firstItem->rowPos() - rows * rowSize(); - } else { - FxGridItemSG *lastItem = static_cast(visibleItems.last()); - int count = modelIndex - lastItem->index; - int col = lastItem->colPos() + count * colSize(); - int rows = col / (columns * colSize()); - return lastItem->rowPos() + rows * rowSize(); - } - } - return (modelIndex / columns) * rowSize(); -} - - -qreal QSGGridViewPrivate::snapPosAt(qreal pos) const -{ - Q_Q(const QSGGridView); - qreal snapPos = 0; - if (!visibleItems.isEmpty()) { - qreal highlightStart = highlightRangeStart; - if (isRightToLeftTopToBottom()) - highlightStart = highlightRangeEndValid ? -size() + highlightRangeEnd : -size(); - - pos += highlightStart; - pos += rowSize()/2; - snapPos = static_cast(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize(); - snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); - snapPos -= highlightStart; - qreal maxExtent; - qreal minExtent; - if (isRightToLeftTopToBottom()) { - maxExtent = q->minXExtent(); - minExtent = q->maxXExtent(); - } else { - maxExtent = flow == QSGGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); - minExtent = flow == QSGGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); - } - if (snapPos > maxExtent) - snapPos = maxExtent; - if (snapPos < minExtent) - snapPos = minExtent; - } - return snapPos; -} - -FxViewItem *QSGGridViewPrivate::snapItemAt(qreal pos) const -{ - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index == -1) - continue; - qreal itemTop = item->position(); - if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos) - return item; - } - return 0; -} - -int QSGGridViewPrivate::snapIndex() const -{ - int index = currentIndex; - for (int i = 0; i < visibleItems.count(); ++i) { - FxGridItemSG *item = static_cast(visibleItems.at(i)); - if (item->index == -1) - continue; - qreal itemTop = item->position(); - FxGridItemSG *hItem = static_cast(highlight); - if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) { - index = item->index; - if (item->colPos() >= hItem->colPos()-colSize()/2 && item->colPos() < hItem->colPos()+colSize()/2) - return item->index; - } - } - return index; -} - -FxViewItem *QSGGridViewPrivate::newViewItem(int modelIndex, QSGItem *item) -{ - Q_Q(QSGGridView); - Q_UNUSED(modelIndex); - return new FxGridItemSG(item, q, false); -} - -bool QSGGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer) -{ - int colPos = colPosAt(visibleIndex); - int rowPos = rowPosAt(visibleIndex); - if (visibleItems.count()) { - FxGridItemSG *lastItem = static_cast(visibleItems.last()); - rowPos = lastItem->rowPos(); - colPos = lastItem->colPos() + colSize(); - if (colPos > colSize() * (columns-1)) { - colPos = 0; - rowPos += rowSize(); - } - } - - int modelIndex = findLastVisibleIndex(); - modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1; - - if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2 - || fillTo < rowPosAt(visibleIndex) - rowSize())) { - // We've jumped more than a page. Estimate which items are now - // visible and fill from there. - int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns; - for (int i = 0; i < visibleItems.count(); ++i) - releaseItem(visibleItems.at(i)); - visibleItems.clear(); - modelIndex += count; - if (modelIndex >= model->count()) - modelIndex = model->count() - 1; - else if (modelIndex < 0) - modelIndex = 0; - modelIndex = modelIndex / columns * columns; - visibleIndex = modelIndex; - colPos = colPosAt(visibleIndex); - rowPos = rowPosAt(visibleIndex); - } - - int colNum = colPos / colSize(); - FxGridItemSG *item = 0; - bool changed = false; - - while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) { -// qDebug() << "refill: append item" << modelIndex; - if (!(item = static_cast(createItem(modelIndex)))) - break; - item->setPosition(colPos, rowPos); - visibleItems.append(item); - colPos += colSize(); - colNum++; - if (colPos > colSize() * (columns-1)) { - colPos = 0; - colNum = 0; - rowPos += rowSize(); - } - ++modelIndex; - changed = true; - if (doBuffer) // never buffer more than one item per frame - break; - } - - if (visibleItems.count()) { - FxGridItemSG *firstItem = static_cast(visibleItems.first()); - rowPos = firstItem->rowPos(); - colPos = firstItem->colPos() - colSize(); - if (colPos < 0) { - colPos = colSize() * (columns - 1); - rowPos -= rowSize(); - } - } - - colNum = colPos / colSize(); - while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){ -// qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos; - if (!(item = static_cast(createItem(visibleIndex-1)))) - break; - --visibleIndex; - item->setPosition(colPos, rowPos); - visibleItems.prepend(item); - colPos -= colSize(); - colNum--; - if (colPos < 0) { - colPos = colSize() * (columns - 1); - colNum = columns-1; - rowPos -= rowSize(); - } - changed = true; - if (doBuffer) // never buffer more than one item per frame - break; - } - - return changed; -} - -bool QSGGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) -{ - FxGridItemSG *item = 0; - bool changed = false; - - while (visibleItems.count() > 1 - && (item = static_cast(visibleItems.first())) - && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { - if (item->attached->delayRemove()) - break; -// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos(); - if (item->index != -1) - visibleIndex++; - visibleItems.removeFirst(); - releaseItem(item); - changed = true; - } - while (visibleItems.count() > 1 - && (item = static_cast(visibleItems.last())) - && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) { - if (item->attached->delayRemove()) - break; -// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1; - visibleItems.removeLast(); - releaseItem(item); - changed = true; - } - - return changed; -} - -void QSGGridViewPrivate::visibleItemsChanged() -{ - updateHeader(); - updateFooter(); - updateViewport(); -} - -void QSGGridViewPrivate::updateViewport() -{ - Q_Q(QSGGridView); - columns = (int)qMax((flow == QSGGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); - QSGItemViewPrivate::updateViewport(); -} - -void QSGGridViewPrivate::layoutVisibleItems() -{ - if (visibleItems.count()) { - FxGridItemSG *firstItem = static_cast(visibleItems.first()); - qreal rowPos = firstItem->rowPos(); - qreal colPos = firstItem->colPos(); - int col = visibleIndex % columns; - if (colPos != col * colSize()) { - colPos = col * colSize(); - firstItem->setPosition(colPos, rowPos); - } - for (int i = 1; i < visibleItems.count(); ++i) { - FxGridItemSG *item = static_cast(visibleItems.at(i)); - colPos += colSize(); - if (colPos > colSize() * (columns-1)) { - colPos = 0; - rowPos += rowSize(); - } - item->setPosition(colPos, rowPos); - } - } -} - -void QSGGridViewPrivate::repositionPackageItemAt(QSGItem *item, int index) -{ - Q_Q(QSGGridView); - qreal pos = position(); - if (flow == QSGGridView::LeftToRight) { - if (item->y() + item->height() > pos && item->y() < pos + q->height()) - item->setPos(QPointF(colPosAt(index), rowPosAt(index))); - } else { - if (item->x() + item->width() > pos && item->x() < pos + q->width()) { - if (isRightToLeftTopToBottom()) - item->setPos(QPointF(-rowPosAt(index)-item->width(), colPosAt(index))); - else - item->setPos(QPointF(rowPosAt(index), colPosAt(index))); - } - } -} - -void QSGGridViewPrivate::resetItemPosition(FxViewItem *item, FxViewItem *toItem) -{ - if (item == toItem) - return; - FxGridItemSG *toGridItem = static_cast(toItem); - static_cast(item)->setPosition(toGridItem->colPos(), toGridItem->rowPos()); -} - -void QSGGridViewPrivate::resetFirstItemPosition() -{ - FxGridItemSG *item = static_cast(visibleItems.first()); - item->setPosition(0, 0); -} - -void QSGGridViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards) -{ - int moveCount = (forwards / rowSize()) - (backwards / rowSize()); - - FxGridItemSG *gridItem = static_cast(item); - gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize())); -} - -void QSGGridViewPrivate::createHighlight() -{ - Q_Q(QSGGridView); - bool changed = false; - if (highlight) { - if (trackedItem == highlight) - trackedItem = 0; - delete highlight; - highlight = 0; - - delete highlightXAnimator; - delete highlightYAnimator; - highlightXAnimator = 0; - highlightYAnimator = 0; - - changed = true; - } - - if (currentItem) { - QSGItem *item = createHighlightItem(); - if (item) { - FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true); - if (autoHighlight) - resetHighlightPosition(); - highlightXAnimator = new QSmoothedAnimation(q); - highlightXAnimator->target = QDeclarativeProperty(item, QLatin1String("x")); - highlightXAnimator->userDuration = highlightMoveDuration; - highlightYAnimator = new QSmoothedAnimation(q); - highlightYAnimator->target = QDeclarativeProperty(item, QLatin1String("y")); - highlightYAnimator->userDuration = highlightMoveDuration; - - highlight = newHighlight; - changed = true; - } - } - if (changed) - emit q->highlightItemChanged(); -} - -void QSGGridViewPrivate::updateHighlight() -{ - applyPendingChanges(); - - if ((!currentItem && highlight) || (currentItem && !highlight)) - createHighlight(); - bool strictHighlight = haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange; - if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) { - // auto-update highlight - highlightXAnimator->to = currentItem->item->x(); - highlightYAnimator->to = currentItem->item->y(); - highlight->item->setWidth(currentItem->item->width()); - highlight->item->setHeight(currentItem->item->height()); - - highlightXAnimator->restart(); - highlightYAnimator->restart(); - } - updateTrackedItem(); -} - -void QSGGridViewPrivate::resetHighlightPosition() -{ - if (highlight && currentItem) { - FxGridItemSG *cItem = static_cast(currentItem); - static_cast(highlight)->setPosition(cItem->colPos(), cItem->rowPos()); - } -} - -qreal QSGGridViewPrivate::headerSize() const -{ - if (!header) - return 0.0; - return flow == QSGGridView::LeftToRight ? header->item->height() : header->item->width(); -} - -qreal QSGGridViewPrivate::footerSize() const -{ - if (!footer) - return 0.0; - return flow == QSGGridView::LeftToRight? footer->item->height() : footer->item->width(); -} - -bool QSGGridViewPrivate::showHeaderForIndex(int index) const -{ - return index / columns == 0; -} - -bool QSGGridViewPrivate::showFooterForIndex(int index) const -{ - return index / columns == (model->count()-1) / columns; -} - -void QSGGridViewPrivate::updateFooter() -{ - Q_Q(QSGGridView); - bool created = false; - if (!footer) { - QSGItem *item = createComponentItem(footerComponent, true); - if (!item) - return; - item->setZ(1); - footer = new FxGridItemSG(item, q, true); - created = true; - } - - FxGridItemSG *gridItem = static_cast(footer); - qreal colOffset = 0; - qreal rowOffset = 0; - if (q->effectiveLayoutDirection() == Qt::RightToLeft) { - if (flow == QSGGridView::TopToBottom) - rowOffset = gridItem->item->width() - cellWidth; - else - colOffset = gridItem->item->width() - cellWidth; - } - if (visibleItems.count()) { - qreal endPos = lastPosition(); - if (findLastVisibleIndex() == model->count()-1) { - gridItem->setPosition(colOffset, endPos + rowOffset); - } else { - qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size(); - if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset) - gridItem->setPosition(colOffset, endPos + rowOffset); - } - } else { - gridItem->setPosition(colOffset, rowOffset); - } - - if (created) - emit q->footerItemChanged(); -} - -void QSGGridViewPrivate::updateHeader() -{ - Q_Q(QSGGridView); - bool created = false; - if (!header) { - QSGItem *item = createComponentItem(headerComponent, true); - if (!item) - return; - item->setZ(1); - header = new FxGridItemSG(item, q, true); - created = true; - } - - FxGridItemSG *gridItem = static_cast(header); - qreal colOffset = 0; - qreal rowOffset = -headerSize(); - if (q->effectiveLayoutDirection() == Qt::RightToLeft) { - if (flow == QSGGridView::TopToBottom) - rowOffset += gridItem->item->width()-cellWidth; - else - colOffset = gridItem->item->width()-cellWidth; - } - if (visibleItems.count()) { - qreal startPos = originPosition(); - if (visibleIndex == 0) { - gridItem->setPosition(colOffset, startPos + rowOffset); - } else { - qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position(); - qreal headerPos = isRightToLeftTopToBottom() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos(); - if (tempPos <= startPos || headerPos > startPos + rowOffset) - gridItem->setPosition(colOffset, startPos + rowOffset); - } - } else { - if (isRightToLeftTopToBottom()) - gridItem->setPosition(colOffset, rowOffset); - else - gridItem->setPosition(colOffset, -headerSize()); - } - - if (created) - emit q->headerItemChanged(); -} - -void QSGGridViewPrivate::initializeCurrentItem() -{ - if (currentItem && currentIndex >= 0) { - FxGridItemSG *gridItem = static_cast(currentItem); - if (gridItem) - gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex)); - } -} - -void QSGGridViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_Q(QSGGridView); - QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry); - if (!q->isComponentComplete()) - return; - if (item == q) { - if (newGeometry.height() != oldGeometry.height() || newGeometry.width() != oldGeometry.width()) { - updateViewport(); - forceLayout = true; - q->polish(); - } - } -} - -void QSGGridViewPrivate::fixupPosition() -{ - moveReason = Other; - if (flow == QSGGridView::LeftToRight) - fixupY(); - else - fixupX(); -} - -void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) -{ - if ((flow == QSGGridView::TopToBottom && &data == &vData) - || (flow == QSGGridView::LeftToRight && &data == &hData)) - return; - - fixupMode = moveReason == Mouse ? fixupMode : Immediate; - - qreal viewPos = isRightToLeftTopToBottom() ? -position()-size() : position(); - - bool strictHighlightRange = haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange; - if (snapMode != QSGGridView::NoSnap) { - qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position(); - if (snapMode == QSGGridView::SnapOneRow && moveReason == Mouse) { - // if we've been dragged < rowSize()/2 then bias towards the next row - qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); - qreal bias = 0; - if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2) - bias = rowSize()/2; - else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2) - bias = -rowSize()/2; - if (isRightToLeftTopToBottom()) - bias = -bias; - tempPosition -= bias; - } - FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); - if (!topItem && strictHighlightRange && currentItem) { - // StrictlyEnforceRange always keeps an item in range - updateHighlight(); - topItem = currentItem; - } - FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); - if (!bottomItem && strictHighlightRange && currentItem) { - // StrictlyEnforceRange always keeps an item in range - updateHighlight(); - bottomItem = currentItem; - } - qreal pos; - bool isInBounds = -position() > maxExtent && -position() <= minExtent; - if (topItem && (isInBounds || strictHighlightRange)) { - qreal headerPos = header ? static_cast(header)->rowPos() : 0; - if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) { - pos = isRightToLeftTopToBottom() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart; - } else { - if (isRightToLeftTopToBottom()) - pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent); - else - pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); - } - } else if (bottomItem && isInBounds) { - if (isRightToLeftTopToBottom()) - pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent); - else - pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent); - } else { - QSGItemViewPrivate::fixup(data, minExtent, maxExtent); - return; - } - - qreal dist = qAbs(data.move + pos); - if (dist > 0) { - timeline.reset(data.move); - if (fixupMode != Immediate) { - timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - data.fixingUp = true; - } else { - timeline.set(data.move, -pos); - } - vTime = timeline.time(); - } - } else if (haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) { - if (currentItem) { - updateHighlight(); - qreal pos = static_cast(currentItem)->rowPos(); - if (viewPos < pos + rowSize() - highlightRangeEnd) - viewPos = pos + rowSize() - highlightRangeEnd; - if (viewPos > pos - highlightRangeStart) - viewPos = pos - highlightRangeStart; - if (isRightToLeftTopToBottom()) - viewPos = -viewPos-size(); - timeline.reset(data.move); - if (viewPos != position()) { - if (fixupMode != Immediate) { - timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - data.fixingUp = true; - } else { - timeline.set(data.move, -viewPos); - } - } - vTime = timeline.time(); - } - } else { - QSGItemViewPrivate::fixup(data, minExtent, maxExtent); - } - data.inOvershoot = false; - fixupMode = Normal; -} - -void QSGGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) -{ - Q_Q(QSGGridView); - data.fixingUp = false; - moveReason = Mouse; - if ((!haveHighlightRange || highlightRange != QSGGridView::StrictlyEnforceRange) - && snapMode == QSGGridView::NoSnap) { - QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); - return; - } - qreal maxDistance = 0; - qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value(); - // -ve velocity means list is moving up/left - if (velocity > 0) { - if (data.move.value() < minExtent) { - if (snapMode == QSGGridView::SnapOneRow) { - // if we've been dragged < averageSize/2 then bias towards the next item - qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); - qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0; - if (isRightToLeftTopToBottom()) - bias = -bias; - data.flickTarget = -snapPosAt(-dataValue - bias); - maxDistance = qAbs(data.flickTarget - data.move.value()); - velocity = maxVelocity; - } else { - maxDistance = qAbs(minExtent - data.move.value()); - } - } - if (snapMode == QSGGridView::NoSnap && highlightRange != QSGGridView::StrictlyEnforceRange) - data.flickTarget = minExtent; - } else { - if (data.move.value() > maxExtent) { - if (snapMode == QSGGridView::SnapOneRow) { - // if we've been dragged < averageSize/2 then bias towards the next item - qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); - qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0; - if (isRightToLeftTopToBottom()) - bias = -bias; - data.flickTarget = -snapPosAt(-dataValue + bias); - maxDistance = qAbs(data.flickTarget - data.move.value()); - velocity = -maxVelocity; - } else { - maxDistance = qAbs(maxExtent - data.move.value()); - } - } - if (snapMode == QSGGridView::NoSnap && highlightRange != QSGGridView::StrictlyEnforceRange) - data.flickTarget = maxExtent; - } - bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds; - if (maxDistance > 0 || overShoot) { - // This mode requires the grid to stop exactly on a row boundary. - qreal v = velocity; - if (maxVelocity != -1 && maxVelocity < qAbs(v)) { - if (v < 0) - v = -maxVelocity; - else - v = maxVelocity; - } - qreal accel = deceleration; - qreal v2 = v * v; - qreal overshootDist = 0.0; - if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGGridView::SnapOneRow) { - // + rowSize()/4 to encourage moving at least one item in the flick direction - qreal dist = v2 / (accel * 2.0) + rowSize()/4; - dist = qMin(dist, maxDistance); - if (v > 0) - dist = -dist; - if (snapMode != QSGGridView::SnapOneRow) { - qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist; - data.flickTarget = -snapPosAt(-dataValue + distTemp); - } - data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget; - if (overShoot) { - if (data.flickTarget >= minExtent) { - overshootDist = overShootDistance(vSize); - data.flickTarget += overshootDist; - } else if (data.flickTarget <= maxExtent) { - overshootDist = overShootDistance(vSize); - data.flickTarget -= overshootDist; - } - } - qreal adjDist = -data.flickTarget + data.move.value(); - if (qAbs(adjDist) > qAbs(dist)) { - // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration - qreal adjv2 = accel * 2.0f * qAbs(adjDist); - if (adjv2 > v2) { - v2 = adjv2; - v = qSqrt(v2); - if (dist > 0) - v = -v; - } - } - dist = adjDist; - accel = v2 / (2.0f * qAbs(dist)); - } else { - data.flickTarget = velocity > 0 ? minExtent : maxExtent; - overshootDist = overShoot ? overShootDistance(vSize) : 0; - } - timeline.reset(data.move); - timeline.accel(data.move, v, accel, maxDistance + overshootDist); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); - if (!hData.flicking && q->xflick()) { - hData.flicking = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - emit q->flickStarted(); - } - if (!vData.flicking && q->yflick()) { - vData.flicking = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - emit q->flickStarted(); - } - } else { - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); - } -} - - -//---------------------------------------------------------------------------- -/*! - \qmlclass GridView QSGGridView - \inqmlmodule QtQuick 2 - \ingroup qml-view-elements - - \inherits Flickable - \brief The GridView item provides a grid view of items provided by a model. - - A GridView displays data from models created from built-in QML elements like ListModel - and XmlListModel, or custom model classes defined in C++ that inherit from - QAbstractListModel. - - A GridView has a \l model, which defines the data to be displayed, and - a \l delegate, which defines how the data should be displayed. Items in a - GridView are laid out horizontally or vertically. Grid views are inherently flickable - as GridView inherits from \l Flickable. - - \section1 Example Usage - - The following example shows the definition of a simple list model defined - in a file called \c ContactModel.qml: - - \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0 - - \div {class="float-right"} - \inlineimage gridview-simple.png - \enddiv - - This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules} - for more information about creating reusable components like this. - - Another component can display this model data in a GridView, as in the following - example, which creates a \c ContactModel component for its model, and a \l Column element - (containing \l Image and \l Text elements) for its delegate. - - \clearfloat - \snippet doc/src/snippets/declarative/gridview/gridview.qml import - \codeline - \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple - - \div {class="float-right"} - \inlineimage gridview-highlight.png - \enddiv - - The view will create a new delegate for each item in the model. Note that the delegate - is able to access the model's \c name and \c portrait data directly. - - An improved grid view is shown below. The delegate is visually improved and is moved - into a separate \c contactDelegate component. - - \clearfloat - \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced - - The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, - and \c focus is set to \c true to enable keyboard navigation for the grid view. - The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). - - Delegates are instantiated as needed and may be destroyed at any time. - State should \e never be stored in a delegate. - - GridView attaches a number of properties to the root item of the delegate, for example - \c {GridView.isCurrentItem}. In the following example, the root delegate item can access - this attached property directly as \c GridView.isCurrentItem, while the child - \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem. - - \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem - - \note Views do not set the \l{Item::}{clip} property automatically. - If the view is not clipped by another item or the screen, it will be necessary - to set this property to true in order to clip the items that are partially or - fully outside the view. - - \sa {declarative/modelviews/gridview}{GridView example} -*/ - -QSGGridView::QSGGridView(QSGItem *parent) - : QSGItemView(*(new QSGGridViewPrivate), parent) -{ -} - -QSGGridView::~QSGGridView() -{ -} - -void QSGGridView::setHighlightFollowsCurrentItem(bool autoHighlight) -{ - Q_D(QSGGridView); - if (d->autoHighlight != autoHighlight) { - if (!autoHighlight && d->highlightXAnimator) { - d->highlightXAnimator->stop(); - d->highlightYAnimator->stop(); - } - QSGItemView::setHighlightFollowsCurrentItem(autoHighlight); - } -} - -/*! - \qmlattachedproperty bool QtQuick2::GridView::isCurrentItem - This attached property is true if this delegate is the current item; otherwise false. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty GridView QtQuick2::GridView::view - This attached property holds the view that manages this delegate instance. - - It is attached to each instance of the delegate. - - \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem -*/ - -/*! - \qmlattachedproperty bool QtQuick2::GridView::delayRemove - This attached property holds whether the delegate may be destroyed. - - It is attached to each instance of the delegate. - - It is sometimes necessary to delay the destruction of an item - until an animation completes. - - The example below ensures that the animation completes before - the item is removed from the grid. - - \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove -*/ - -/*! - \qmlattachedsignal QtQuick2::GridView::onAdd() - This attached handler is called immediately after an item is added to the view. -*/ - -/*! - \qmlattachedsignal QtQuick2::GridView::onRemove() - This attached handler is called immediately before an item is removed from the view. -*/ - - -/*! - \qmlproperty model QtQuick2::GridView::model - This property holds the model providing data for the grid. - - The model provides the set of data that is used to create the items - in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel - or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is - used, it must be a subclass of \l QAbstractItemModel or a simple list. - - \sa {qmlmodels}{Data Models} -*/ - -/*! - \qmlproperty Component QtQuick2::GridView::delegate - - The delegate provides a template defining each item instantiated by the view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qmlmodels}{Data Model}. - - The number of elements in the delegate has a direct effect on the - flicking performance of the view. If at all possible, place functionality - that is not needed for the normal display of the delegate in a \l Loader which - can load additional elements when needed. - - The GridView will layout the items based on the size of the root item - in the delegate. - - \note Delegates are instantiated as needed and may be destroyed at any time. - State should \e never be stored in a delegate. -*/ - -/*! - \qmlproperty int QtQuick2::GridView::currentIndex - \qmlproperty Item QtQuick2::GridView::currentItem - - The \c currentIndex property holds the index of the current item, and - \c currentItem holds the current item. Setting the currentIndex to -1 - will clear the highlight and set currentItem to null. - - If highlightFollowsCurrentItem is \c true, setting either of these - properties will smoothly scroll the GridView so that the current - item becomes visible. - - Note that the position of the current item - may only be approximate until it becomes visible in the view. -*/ - - -/*! - \qmlproperty Item QtQuick2::GridView::highlightItem - - This holds the highlight item created from the \l highlight component. - - The highlightItem is managed by the view unless - \l highlightFollowsCurrentItem is set to false. - - \sa highlight, highlightFollowsCurrentItem -*/ - - -/*! - \qmlproperty int QtQuick2::GridView::count - This property holds the number of items in the view. -*/ - - -/*! - \qmlproperty Component QtQuick2::GridView::highlight - This property holds the component to use as the highlight. - - An instance of the highlight component is created for each view. - The geometry of the resulting component instance will be managed by the view - so as to stay with the current item, unless the highlightFollowsCurrentItem property is false. - - \sa highlightItem, highlightFollowsCurrentItem -*/ - -/*! - \qmlproperty bool QtQuick2::GridView::highlightFollowsCurrentItem - This property sets whether the highlight is managed by the view. - - If this property is true (the default value), the highlight is moved smoothly - to follow the current item. Otherwise, the - highlight is not moved by the view, and any movement must be implemented - by the highlight. - - Here is a highlight with its motion defined by a \l {SpringAnimation} item: - - \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem -*/ - - -/*! - \qmlproperty int QtQuick2::GridView::highlightMoveDuration - This property holds the move animation duration of the highlight delegate. - - highlightFollowsCurrentItem must be true for this property - to have effect. - - The default value for the duration is 150ms. - - \sa highlightFollowsCurrentItem -*/ - -/*! - \qmlproperty real QtQuick2::GridView::preferredHighlightBegin - \qmlproperty real QtQuick2::GridView::preferredHighlightEnd - \qmlproperty enumeration QtQuick2::GridView::highlightRangeMode - - These properties define the preferred range of the highlight (for the current item) - within the view. The \c preferredHighlightBegin value must be less than the - \c preferredHighlightEnd value. - - These properties affect the position of the current item when the view is scrolled. - For example, if the currently selected item should stay in the middle of the - view when it is scrolled, set the \c preferredHighlightBegin and - \c preferredHighlightEnd values to the top and bottom coordinates of where the middle - item would be. If the \c currentItem is changed programmatically, the view will - automatically scroll so that the current item is in the middle of the view. - Furthermore, the behavior of the current item index will occur whether or not a - highlight exists. - - Valid values for \c highlightRangeMode are: - - \list - \o GridView.ApplyRange - the view attempts to maintain the highlight within the range. - However, the highlight can move outside of the range at the ends of the view or due - to mouse interaction. - \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range. - The current item changes if a keyboard or mouse action would cause the highlight to move - outside of the range. - \o GridView.NoHighlightRange - this is the default value. - \endlist -*/ - - -/*! - \qmlproperty enumeration QtQuick2::GridView::layoutDirection - This property holds the layout direction of the grid. - - Possible values: - - \list - \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is - dependent on the \l GridView::flow property. - \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent - on the \l GridView::flow property. - \endlist - - \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if - GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply - indicates that the flow is horizontal. -*/ - - -/*! - \qmlproperty enumeration QtQuick2::GridView::effectiveLayoutDirection - This property holds the effective layout direction of the grid. - - When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, - the visual layout direction of the grid will be mirrored. However, the - property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged. - - \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring} -*/ -/*! - \qmlproperty bool QtQuick2::GridView::keyNavigationWraps - This property holds whether the grid wraps key navigation - - If this is true, key navigation that would move the current item selection - past one end of the view instead wraps around and moves the selection to - the other end of the view. - - By default, key navigation is not wrapped. -*/ -/*! - \qmlproperty int QtQuick2::GridView::cacheBuffer - This property determines whether delegates are retained outside the - visible area of the view. - - If non-zero the view will keep as many delegates - instantiated as will fit within the buffer specified. For example, - if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is - set to 40, then up to 2 delegates above and 2 delegates below the visible - area may be retained. - - Note that cacheBuffer is not a pixel buffer - it only maintains additional - instantiated delegates. - - Setting this value can make scrolling the list smoother at the expense - of additional memory usage. It is not a substitute for creating efficient - delegates; the fewer elements in a delegate, the faster a view may be - scrolled. -*/ -void QSGGridView::setHighlightMoveDuration(int duration) -{ - Q_D(QSGGridView); - if (d->highlightMoveDuration != duration) { - if (d->highlightYAnimator) { - d->highlightXAnimator->userDuration = duration; - d->highlightYAnimator->userDuration = duration; - } - QSGItemView::setHighlightMoveDuration(duration); - } -} - -/*! - \qmlproperty enumeration QtQuick2::GridView::flow - This property holds the flow of the grid. - - Possible values: - - \list - \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically - \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally - \endlist -*/ -QSGGridView::Flow QSGGridView::flow() const -{ - Q_D(const QSGGridView); - return d->flow; -} - -void QSGGridView::setFlow(Flow flow) -{ - Q_D(QSGGridView); - if (d->flow != flow) { - d->flow = flow; - if (d->flow == LeftToRight) { - setContentWidth(-1); - setFlickableDirection(VerticalFlick); - } else { - setContentHeight(-1); - setFlickableDirection(HorizontalFlick); - } - setContentX(0); - setContentY(0); - d->regenerate(); - emit flowChanged(); - } -} - - -/*! - \qmlproperty real QtQuick2::GridView::cellWidth - \qmlproperty real QtQuick2::GridView::cellHeight - - These properties holds the width and height of each cell in the grid. - - The default cell size is 100x100. -*/ -qreal QSGGridView::cellWidth() const -{ - Q_D(const QSGGridView); - return d->cellWidth; -} - -void QSGGridView::setCellWidth(qreal cellWidth) -{ - Q_D(QSGGridView); - if (cellWidth != d->cellWidth && cellWidth > 0) { - d->cellWidth = qMax(qreal(1), cellWidth); - d->updateViewport(); - emit cellWidthChanged(); - d->forceLayout = true; - d->layout(); - } -} - -qreal QSGGridView::cellHeight() const -{ - Q_D(const QSGGridView); - return d->cellHeight; -} - -void QSGGridView::setCellHeight(qreal cellHeight) -{ - Q_D(QSGGridView); - if (cellHeight != d->cellHeight && cellHeight > 0) { - d->cellHeight = qMax(qreal(1), cellHeight); - d->updateViewport(); - emit cellHeightChanged(); - d->forceLayout = true; - d->layout(); - } -} -/*! - \qmlproperty enumeration QtQuick2::GridView::snapMode - - This property determines how the view scrolling will settle following a drag or flick. - The possible values are: - - \list - \o GridView.NoSnap (default) - the view stops anywhere within the visible area. - \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow) - aligned with the start of the view. - \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow) - away from the first visible row at the time the mouse button is released. - This mode is particularly useful for moving one page at a time. - \endlist - -*/ -QSGGridView::SnapMode QSGGridView::snapMode() const -{ - Q_D(const QSGGridView); - return d->snapMode; -} - -void QSGGridView::setSnapMode(SnapMode mode) -{ - Q_D(QSGGridView); - if (d->snapMode != mode) { - d->snapMode = mode; - emit snapModeChanged(); - } -} - - -/*! - \qmlproperty Component QtQuick2::GridView::footer - This property holds the component to use as the footer. - - An instance of the footer component is created for each view. The - footer is positioned at the end of the view, after any items. - - \sa header -*/ -/*! - \qmlproperty Component QtQuick2::GridView::header - This property holds the component to use as the header. - - An instance of the header component is created for each view. The - header is positioned at the beginning of the view, before any items. - - \sa footer -*/ -void QSGGridView::viewportMoved() -{ - Q_D(QSGGridView); - QSGItemView::viewportMoved(); - if (!d->itemCount) - return; - if (d->inViewportMoved) - return; - d->inViewportMoved = true; - - d->lazyRelease = true; - if (d->hData.flicking || d->vData.flicking) { - if (yflick()) { - if (d->vData.velocity > 0) - d->bufferMode = QSGGridViewPrivate::BufferBefore; - else if (d->vData.velocity < 0) - d->bufferMode = QSGGridViewPrivate::BufferAfter; - } - - if (xflick()) { - if (d->hData.velocity > 0) - d->bufferMode = QSGGridViewPrivate::BufferBefore; - else if (d->hData.velocity < 0) - d->bufferMode = QSGGridViewPrivate::BufferAfter; - } - } - d->refill(); - if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) - d->moveReason = QSGGridViewPrivate::Mouse; - if (d->moveReason != QSGGridViewPrivate::SetIndex) { - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { - // reposition highlight - qreal pos = d->highlight->position(); - qreal viewPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size() : d->position(); - if (pos > viewPos + d->highlightRangeEnd - d->highlight->size()) - pos = viewPos + d->highlightRangeEnd - d->highlight->size(); - if (pos < viewPos + d->highlightRangeStart) - pos = viewPos + d->highlightRangeStart; - - if (pos != d->highlight->position()) { - d->highlightXAnimator->stop(); - d->highlightYAnimator->stop(); - static_cast(d->highlight)->setPosition(static_cast(d->highlight)->colPos(), pos); - } else { - d->updateHighlight(); - } - - // update current index - int idx = d->snapIndex(); - if (idx >= 0 && idx != d->currentIndex) { - d->updateCurrent(idx); - if (d->currentItem && static_cast(d->currentItem)->colPos() != static_cast(d->highlight)->colPos() && d->autoHighlight) { - if (d->flow == LeftToRight) - d->highlightXAnimator->to = d->currentItem->item->x(); - else - d->highlightYAnimator->to = d->currentItem->item->y(); - } - } - } - } - - d->inViewportMoved = false; -} - -void QSGGridView::keyPressEvent(QKeyEvent *event) -{ - Q_D(QSGGridView); - if (d->model && d->model->count() && d->interactive) { - d->moveReason = QSGGridViewPrivate::SetIndex; - int oldCurrent = currentIndex(); - switch (event->key()) { - case Qt::Key_Up: - moveCurrentIndexUp(); - break; - case Qt::Key_Down: - moveCurrentIndexDown(); - break; - case Qt::Key_Left: - moveCurrentIndexLeft(); - break; - case Qt::Key_Right: - moveCurrentIndexRight(); - break; - default: - break; - } - if (oldCurrent != currentIndex()) { - event->accept(); - return; - } - } - event->ignore(); - QSGItemView::keyPressEvent(event); -} -/*! - \qmlmethod QtQuick2::GridView::moveCurrentIndexUp() - - Move the currentIndex up one item in the view. - The current index will wrap if keyNavigationWraps is true and it - is currently at the end. This method has no effect if the \l count is zero. - - \bold Note: methods should only be called after the Component has completed. -*/ - - -void QSGGridView::moveCurrentIndexUp() -{ - Q_D(QSGGridView); - const int count = d->model ? d->model->count() : 0; - if (!count) - return; - if (d->flow == QSGGridView::LeftToRight) { - if (currentIndex() >= d->columns || d->wrap) { - int index = currentIndex() - d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); - } - } else { - if (currentIndex() > 0 || d->wrap) { - int index = currentIndex() - 1; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); - } - } -} - -/*! - \qmlmethod QtQuick2::GridView::moveCurrentIndexDown() - - Move the currentIndex down one item in the view. - The current index will wrap if keyNavigationWraps is true and it - is currently at the end. This method has no effect if the \l count is zero. - - \bold Note: methods should only be called after the Component has completed. -*/ -void QSGGridView::moveCurrentIndexDown() -{ - Q_D(QSGGridView); - const int count = d->model ? d->model->count() : 0; - if (!count) - return; - if (d->flow == QSGGridView::LeftToRight) { - if (currentIndex() < count - d->columns || d->wrap) { - int index = currentIndex()+d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : 0); - } - } else { - if (currentIndex() < count - 1 || d->wrap) { - int index = currentIndex() + 1; - setCurrentIndex((index >= 0 && index < count) ? index : 0); - } - } -} - -/*! - \qmlmethod QtQuick2::GridView::moveCurrentIndexLeft() - - Move the currentIndex left one item in the view. - The current index will wrap if keyNavigationWraps is true and it - is currently at the end. This method has no effect if the \l count is zero. - - \bold Note: methods should only be called after the Component has completed. -*/ -void QSGGridView::moveCurrentIndexLeft() -{ - Q_D(QSGGridView); - const int count = d->model ? d->model->count() : 0; - if (!count) - return; - if (effectiveLayoutDirection() == Qt::LeftToRight) { - if (d->flow == QSGGridView::LeftToRight) { - if (currentIndex() > 0 || d->wrap) { - int index = currentIndex() - 1; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); - } - } else { - if (currentIndex() >= d->columns || d->wrap) { - int index = currentIndex() - d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); - } - } - } else { - if (d->flow == QSGGridView::LeftToRight) { - if (currentIndex() < count - 1 || d->wrap) { - int index = currentIndex() + 1; - setCurrentIndex((index >= 0 && index < count) ? index : 0); - } - } else { - if (currentIndex() < count - d->columns || d->wrap) { - int index = currentIndex() + d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : 0); - } - } - } -} - - -/*! - \qmlmethod QtQuick2::GridView::moveCurrentIndexRight() - - Move the currentIndex right one item in the view. - The current index will wrap if keyNavigationWraps is true and it - is currently at the end. This method has no effect if the \l count is zero. - - \bold Note: methods should only be called after the Component has completed. -*/ -void QSGGridView::moveCurrentIndexRight() -{ - Q_D(QSGGridView); - const int count = d->model ? d->model->count() : 0; - if (!count) - return; - if (effectiveLayoutDirection() == Qt::LeftToRight) { - if (d->flow == QSGGridView::LeftToRight) { - if (currentIndex() < count - 1 || d->wrap) { - int index = currentIndex() + 1; - setCurrentIndex((index >= 0 && index < count) ? index : 0); - } - } else { - if (currentIndex() < count - d->columns || d->wrap) { - int index = currentIndex()+d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : 0); - } - } - } else { - if (d->flow == QSGGridView::LeftToRight) { - if (currentIndex() > 0 || d->wrap) { - int index = currentIndex() - 1; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); - } - } else { - if (currentIndex() >= d->columns || d->wrap) { - int index = currentIndex() - d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); - } - } - } -} - -bool QSGGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult) -{ - Q_Q(QSGGridView); - - int modelIndex = change.index; - int count = change.count; - - int index = visibleItems.count() ? mapFromModel(modelIndex) : 0; - - if (index < 0) { - int i = visibleItems.count() - 1; - while (i > 0 && visibleItems.at(i)->index == -1) - --i; - if (visibleItems.at(i)->index + 1 == modelIndex) { - // Special case of appending an item to the model. - index = visibleItems.count(); - } else { - if (modelIndex <= visibleIndex) { - // Insert before visible items - visibleIndex += count; - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index != -1 && item->index >= modelIndex) - item->index += count; - } - } - return true; - } - } - - qreal tempPos = isRightToLeftTopToBottom() ? -position()-size()+q->width()+1 : position(); - int colPos = 0; - int rowPos = 0; - if (visibleItems.count()) { - if (index < visibleItems.count()) { - FxGridItemSG *gridItem = static_cast(visibleItems.at(index)); - colPos = gridItem->colPos(); - rowPos = gridItem->rowPos(); - } else { - // appending items to visible list - FxGridItemSG *gridItem = static_cast(visibleItems.at(index-1)); - colPos = gridItem->colPos() + colSize(); - rowPos = gridItem->rowPos(); - if (colPos > colSize() * (columns-1)) { - colPos = 0; - rowPos += rowSize(); - } - } - } - - // Update the indexes of the following visible items. - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index != -1 && item->index >= modelIndex) - item->index += count; - } - - int prevAddedCount = insertResult->addedItems.count(); - if (firstVisible && rowPos < firstVisible->position()) { - // Insert items before the visible item. - int insertionIdx = index; - int i = count - 1; - int from = tempPos - buffer; - - while (i >= 0) { - if (rowPos > from) { - insertResult->sizeAddedBeforeVisible += rowSize(); - } else { - FxViewItem *item = 0; - if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { - if (item->index > modelIndex + i) - insertResult->movedBackwards.append(item); - item->index = modelIndex + i; - } - if (!item) - item = createItem(modelIndex + i); - - visibleItems.insert(insertionIdx, item); - if (!change.isMove()) { - insertResult->addedItems.append(item); - insertResult->sizeAddedBeforeVisible += rowSize(); - } - } - colPos -= colSize(); - if (colPos < 0) { - colPos = colSize() * (columns - 1); - rowPos -= rowSize(); - } - index++; - i--; - } - } else { - int i = 0; - int to = buffer+tempPos+size()-1; - while (i < count && rowPos <= to + rowSize()*(columns - (colPos/colSize()))/qreal(columns)) { - FxViewItem *item = 0; - if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { - if (item->index > modelIndex + i) - insertResult->movedBackwards.append(item); - item->index = modelIndex + i; - } - if (!item) - item = createItem(modelIndex + i); - - visibleItems.insert(index, item); - if (!change.isMove()) - insertResult->addedItems.append(item); - colPos += colSize(); - if (colPos > colSize() * (columns-1)) { - colPos = 0; - rowPos += rowSize(); - } - ++index; - ++i; - } - } - - updateVisibleIndex(); - - return insertResult->addedItems.count() > prevAddedCount; -} - -/*! - \qmlmethod QtQuick2::GridView::positionViewAtIndex(int index, PositionMode mode) - - Positions the view such that the \a index is at the position specified by - \a mode: - - \list - \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view. - \o GridView.Center - position item in the center of the view. - \o GridView.End - position item at bottom (or right for horizontal orientation) of the view. - \o GridView.Visible - if any part of the item is visible then take no action, otherwise - bring the item into view. - \o GridView.Contain - ensure the entire item is visible. If the item is larger than - the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view. - \endlist - - If positioning the view at the index would cause empty space to be displayed at - the beginning or end of the view, the view will be positioned at the boundary. - - It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view - at a particular index. This is unreliable since removing items from the start - of the view does not cause all other items to be repositioned. - The correct way to bring an item into view is with \c positionViewAtIndex. - - \bold Note: methods should only be called after the Component has completed. To position - the view at startup, this method should be called by Component.onCompleted. For - example, to position the view at the end: - - \code - Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning) - \endcode -*/ - -/*! - \qmlmethod QtQuick2::GridView::positionViewAtBeginning() - \qmlmethod QtQuick2::GridView::positionViewAtEnd() - - Positions the view at the beginning or end, taking into account any header or footer. - - It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view - at a particular index. This is unreliable since removing items from the start - of the list does not cause all other items to be repositioned, and because - the actual start of the view can vary based on the size of the delegates. - - \bold Note: methods should only be called after the Component has completed. To position - the view at startup, this method should be called by Component.onCompleted. For - example, to position the view at the end on startup: - - \code - Component.onCompleted: positionViewAtEnd() - \endcode -*/ - -/*! - \qmlmethod int QtQuick2::GridView::indexAt(int x, int y) - - Returns the index of the visible item containing the point \a x, \a y in content - coordinates. If there is no item at the point specified, or the item is - not visible -1 is returned. - - If the item is outside the visible area, -1 is returned, regardless of - whether an item will exist at that point when scrolled into view. - - \bold Note: methods should only be called after the Component has completed. -*/ - -QSGGridViewAttached *QSGGridView::qmlAttachedProperties(QObject *obj) -{ - return new QSGGridViewAttached(obj); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsggridview_p.h b/src/declarative/items/qsggridview_p.h deleted file mode 100644 index c3b92d0448..0000000000 --- a/src/declarative/items/qsggridview_p.h +++ /dev/null @@ -1,146 +0,0 @@ -// Commit: 95814418f9d6adeba365c795462e8afb00138211 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGGRIDVIEW_P_H -#define QSGGRIDVIEW_P_H - -#include "qsgitemview_p.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) -class QSGVisualModel; -class QSGGridViewAttached; -class QSGGridViewPrivate; -class Q_AUTOTEST_EXPORT QSGGridView : public QSGItemView -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGGridView) - - Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) - Q_PROPERTY(qreal cellWidth READ cellWidth WRITE setCellWidth NOTIFY cellWidthChanged) - Q_PROPERTY(qreal cellHeight READ cellHeight WRITE setCellHeight NOTIFY cellHeightChanged) - - Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) - - Q_ENUMS(SnapMode) - Q_ENUMS(Flow) - Q_CLASSINFO("DefaultProperty", "data") - -public: - QSGGridView(QSGItem *parent=0); - ~QSGGridView(); - - virtual void setHighlightFollowsCurrentItem(bool); - virtual void setHighlightMoveDuration(int); - - enum Flow { LeftToRight, TopToBottom }; - Flow flow() const; - void setFlow(Flow); - - qreal cellWidth() const; - void setCellWidth(qreal); - - qreal cellHeight() const; - void setCellHeight(qreal); - - enum SnapMode { NoSnap, SnapToRow, SnapOneRow }; - SnapMode snapMode() const; - void setSnapMode(SnapMode mode); - - static QSGGridViewAttached *qmlAttachedProperties(QObject *); - -public Q_SLOTS: - void moveCurrentIndexUp(); - void moveCurrentIndexDown(); - void moveCurrentIndexLeft(); - void moveCurrentIndexRight(); - -Q_SIGNALS: - void cellWidthChanged(); - void cellHeightChanged(); - void highlightMoveDurationChanged(); - void flowChanged(); - void snapModeChanged(); - -protected: - virtual void viewportMoved(); - virtual void keyPressEvent(QKeyEvent *); -}; - -class QSGGridViewAttached : public QSGItemViewAttached -{ - Q_OBJECT -public: - QSGGridViewAttached(QObject *parent) - : QSGItemViewAttached(parent), m_view(0) {} - ~QSGGridViewAttached() {} - - Q_PROPERTY(QSGGridView *view READ view NOTIFY viewChanged) - QSGGridView *view() { return m_view; } - void setView(QSGGridView *view) { - if (view != m_view) { - m_view = view; - emit viewChanged(); - } - } - -Q_SIGNALS: - void viewChanged(); - -public: - QDeclarativeGuard m_view; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGGridView) -QML_DECLARE_TYPEINFO(QSGGridView, QML_HAS_ATTACHED_PROPERTIES) - -QT_END_HEADER - -#endif // QSGGRIDVIEW_P_H diff --git a/src/declarative/items/qsgimage.cpp b/src/declarative/items/qsgimage.cpp deleted file mode 100644 index 9461de4278..0000000000 --- a/src/declarative/items/qsgimage.cpp +++ /dev/null @@ -1,753 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgimage_p.h" -#include "qsgimage_p_p.h" - -#include - -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGImageTextureProvider : public QSGTextureProvider -{ - Q_OBJECT -public: - QSGImageTextureProvider() - : m_texture(0) - , m_smooth(false) - { - } - - QSGTexture *texture() const { - - if (m_texture->isAtlasTexture()) - const_cast(this)->m_texture = m_texture->removedFromAtlas(); - - if (m_texture) { - m_texture->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest); - m_texture->setMipmapFiltering(QSGTexture::Nearest); - m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge); - m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge); - } - return m_texture; - } - - friend class QSGImage; - - QSGTexture *m_texture; - bool m_smooth; -}; - -#include "qsgimage.moc" - -QSGImagePrivate::QSGImagePrivate() - : fillMode(QSGImage::Stretch) - , paintedWidth(0) - , paintedHeight(0) - , pixmapChanged(false) - , hAlign(QSGImage::AlignHCenter) - , vAlign(QSGImage::AlignVCenter) - , provider(0) -{ -} - -/*! - \qmlclass Image QSGImage - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The Image element displays an image in a declarative user interface - \inherits Item - - The Image element is used to display images in a declarative user interface. - - The source of the image is specified as a URL using the \l source property. - Images can be supplied in any of the standard image formats supported by Qt, - including bitmap formats such as PNG and JPEG, and vector graphics formats - such as SVG. If you need to display animated images, use the \l AnimatedImage - element. - - If the \l{Item::width}{width} and \l{Item::height}{height} properties are not - specified, the Image element automatically uses the size of the loaded image. - By default, specifying the width and height of the element causes the image - to be scaled to that size. This behavior can be changed by setting the - \l fillMode property, allowing the image to be stretched and tiled instead. - - \section1 Example Usage - - The following example shows the simplest usage of the Image element. - - \snippet doc/src/snippets/declarative/image.qml document - - \beginfloatleft - \image declarative-qtlogo.png - \endfloat - - \clearfloat - - \section1 Performance - - By default, locally available images are loaded immediately, and the user interface - is blocked until loading is complete. If a large image is to be loaded, it may be - preferable to load the image in a low priority thread, by enabling the \l asynchronous - property. - - If the image is obtained from a network rather than a local resource, it is - automatically loaded asynchronously, and the \l progress and \l status properties - are updated as appropriate. - - Images are cached and shared internally, so if several Image elements have the same \l source, - only one copy of the image will be loaded. - - \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended - that images which do not form part of the user interface have their - size bounded via the \l sourceSize property. This is especially important for content - that is loaded from external sources or provided by the user. - - \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider -*/ - -QSGImage::QSGImage(QSGItem *parent) - : QSGImageBase(*(new QSGImagePrivate), parent) -{ -} - -QSGImage::QSGImage(QSGImagePrivate &dd, QSGItem *parent) - : QSGImageBase(dd, parent) -{ -} - -QSGImage::~QSGImage() -{ - Q_D(QSGImage); - if (d->provider) - d->provider->deleteLater(); -} - -void QSGImagePrivate::setPixmap(const QPixmap &pixmap) -{ - Q_Q(QSGImage); - pix.setPixmap(pixmap); - - q->pixmapChange(); - status = pix.isNull() ? QSGImageBase::Null : QSGImageBase::Ready; - - q->update(); -} - -/*! - \qmlproperty enumeration QtQuick2::Image::fillMode - - Set this property to define what happens when the source image has a different size - than the item. - - \list - \o Image.Stretch - the image is scaled to fit - \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping - \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary - \o Image.Tile - the image is duplicated horizontally and vertically - \o Image.TileVertically - the image is stretched horizontally and tiled vertically - \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally - \o Image.Pad - the image is not transformed - \endlist - - \table - - \row - \o \image declarative-qtlogo-stretch.png - \o Stretch (default) - \qml - Image { - width: 130; height: 100 - smooth: true - source: "qtlogo.png" - } - \endqml - - \row - \o \image declarative-qtlogo-preserveaspectfit.png - \o PreserveAspectFit - \qml - Image { - width: 130; height: 100 - fillMode: Image.PreserveAspectFit - smooth: true - source: "qtlogo.png" - } - \endqml - - \row - \o \image declarative-qtlogo-preserveaspectcrop.png - \o PreserveAspectCrop - \qml - Image { - width: 130; height: 100 - fillMode: Image.PreserveAspectCrop - smooth: true - source: "qtlogo.png" - clip: true - } - \endqml - - \row - \o \image declarative-qtlogo-tile.png - \o Tile - \qml - Image { - width: 120; height: 120 - fillMode: Image.Tile - source: "qtlogo.png" - } - \endqml - - \row - \o \image declarative-qtlogo-tilevertically.png - \o TileVertically - \qml - Image { - width: 120; height: 120 - fillMode: Image.TileVertically - smooth: true - source: "qtlogo.png" - } - \endqml - - \row - \o \image declarative-qtlogo-tilehorizontally.png - \o TileHorizontally - \qml - Image { - width: 120; height: 120 - fillMode: Image.TileHorizontally - smooth: true - source: "qtlogo.png" - } - \endqml - - \endtable - - Note that \c clip is \c false by default which means that the element might - paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop. - - \sa {declarative/imageelements/image}{Image example} -*/ -QSGImage::FillMode QSGImage::fillMode() const -{ - Q_D(const QSGImage); - return d->fillMode; -} - -void QSGImage::setFillMode(FillMode mode) -{ - Q_D(QSGImage); - if (d->fillMode == mode) - return; - d->fillMode = mode; - update(); - updatePaintedGeometry(); - emit fillModeChanged(); -} - -/*! - - \qmlproperty real QtQuick2::Image::paintedWidth - \qmlproperty real QtQuick2::Image::paintedHeight - - These properties hold the size of the image that is actually painted. - In most cases it is the same as \c width and \c height, but when using a - \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop - \c paintedWidth or \c paintedHeight can be smaller or larger than - \c width and \c height of the Image element. -*/ -qreal QSGImage::paintedWidth() const -{ - Q_D(const QSGImage); - return d->paintedWidth; -} - -qreal QSGImage::paintedHeight() const -{ - Q_D(const QSGImage); - return d->paintedHeight; -} - -/*! - \qmlproperty enumeration QtQuick2::Image::status - - This property holds the status of image loading. It can be one of: - \list - \o Image.Null - no image has been set - \o Image.Ready - the image has been loaded - \o Image.Loading - the image is currently being loaded - \o Image.Error - an error occurred while loading the image - \endlist - - Use this status to provide an update or respond to the status change in some way. - For example, you could: - - \list - \o Trigger a state change: - \qml - State { name: 'loaded'; when: image.status == Image.Ready } - \endqml - - \o Implement an \c onStatusChanged signal handler: - \qml - Image { - id: image - onStatusChanged: if (image.status == Image.Ready) console.log('Loaded') - } - \endqml - - \o Bind to the status value: - \qml - Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' } - \endqml - \endlist - - \sa progress -*/ - -/*! - \qmlproperty real QtQuick2::Image::progress - - This property holds the progress of image loading, from 0.0 (nothing loaded) - to 1.0 (finished). - - \sa status -*/ - -/*! - \qmlproperty bool QtQuick2::Image::smooth - - Set this property if you want the image to be smoothly filtered when scaled or - transformed. Smooth filtering gives better visual quality, but is slower. If - the image is displayed at its natural size, this property has no visual or - performance effect. - - \note Generally scaling artifacts are only visible if the image is stationary on - the screen. A common pattern when animating an image is to disable smooth - filtering at the beginning of the animation and reenable it at the conclusion. -*/ - -/*! - \qmlproperty QSize QtQuick2::Image::sourceSize - - This property holds the actual width and height of the loaded image. - - Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale - the painting of the image, this property sets the actual number of pixels - stored for the loaded image so that large images do not use more - memory than necessary. For example, this ensures the image in memory is no - larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and - \l {Item::}{height} values: - - \code - Rectangle { - width: ... - height: ... - - Image { - anchors.fill: parent - source: "reallyBigImage.jpg" - sourceSize.width: 1024 - sourceSize.height: 1024 - } - } - \endcode - - If the image's actual size is larger than the sourceSize, the image is scaled down. - If only one dimension of the size is set to greater than 0, the - other dimension is set in proportion to preserve the source image's aspect ratio. - (The \l fillMode is independent of this.) - - If the source is an instrinsically scalable image (eg. SVG), this property - determines the size of the loaded image regardless of intrinsic size. - Avoid changing this property dynamically; rendering an SVG is \e slow compared - to an image. - - If the source is a non-scalable image (eg. JPEG), the loaded image will - be no greater than this property specifies. For some formats (currently only JPEG), - the whole image will never actually be loaded into memory. - - Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image - by setting sourceSize to \c undefined. - - \note \e {Changing this property dynamically causes the image source to be reloaded, - potentially even from the network, if it is not in the disk cache.} -*/ - -/*! - \qmlproperty url QtQuick2::Image::source - - Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt. - - The URL may be absolute, or relative to the URL of the component. - - \sa QDeclarativeImageProvider -*/ - -/*! - \qmlproperty bool QtQuick2::Image::asynchronous - - Specifies that images on the local filesystem should be loaded - asynchronously in a separate thread. The default value is - false, causing the user interface thread to block while the - image is loaded. Setting \a asynchronous to true is useful where - maintaining a responsive user interface is more desirable - than having images immediately visible. - - Note that this property is only valid for images read from the - local filesystem. Images loaded via a network resource (e.g. HTTP) - are always loaded asynchonously. -*/ - -/*! - \qmlproperty bool QtQuick2::Image::cache - - Specifies whether the image should be cached. The default value is - true. Setting \a cache to false is useful when dealing with large images, - to make sure that they aren't cached at the expense of small 'ui element' images. -*/ - -/*! - \qmlproperty bool QtQuick2::Image::mirror - - This property holds whether the image should be horizontally inverted - (effectively displaying a mirrored image). - - The default value is false. -*/ - -/*! - \qmlproperty enumeration QtQuick2::Image::horizontalAlignment - \qmlproperty enumeration QtQuick2::Image::verticalAlignment - - Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned. - - The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter. - The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom - and \c Image.AlignVCenter. -*/ -void QSGImage::updatePaintedGeometry() -{ - Q_D(QSGImage); - - if (d->fillMode == PreserveAspectFit) { - if (!d->pix.width() || !d->pix.height()) { - setImplicitWidth(0); - setImplicitHeight(0); - return; - } - qreal w = widthValid() ? width() : d->pix.width(); - qreal widthScale = w / qreal(d->pix.width()); - qreal h = heightValid() ? height() : d->pix.height(); - qreal heightScale = h / qreal(d->pix.height()); - if (widthScale <= heightScale) { - d->paintedWidth = w; - d->paintedHeight = widthScale * qreal(d->pix.height()); - } else if (heightScale < widthScale) { - d->paintedWidth = heightScale * qreal(d->pix.width()); - d->paintedHeight = h; - } - if (widthValid() && !heightValid()) { - setImplicitHeight(d->paintedHeight); - } else { - setImplicitHeight(d->pix.height()); - } - if (heightValid() && !widthValid()) { - setImplicitWidth(d->paintedWidth); - } else { - setImplicitWidth(d->pix.width()); - } - } else if (d->fillMode == PreserveAspectCrop) { - if (!d->pix.width() || !d->pix.height()) - return; - qreal widthScale = width() / qreal(d->pix.width()); - qreal heightScale = height() / qreal(d->pix.height()); - if (widthScale < heightScale) { - widthScale = heightScale; - } else if (heightScale < widthScale) { - heightScale = widthScale; - } - - d->paintedHeight = heightScale * qreal(d->pix.height()); - d->paintedWidth = widthScale * qreal(d->pix.width()); - } else if (d->fillMode == Pad) { - d->paintedWidth = d->pix.width(); - d->paintedHeight = d->pix.height(); - } else { - d->paintedWidth = width(); - d->paintedHeight = height(); - } - emit paintedGeometryChanged(); -} - -void QSGImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - QSGImageBase::geometryChanged(newGeometry, oldGeometry); - updatePaintedGeometry(); -} - -QRectF QSGImage::boundingRect() const -{ - Q_D(const QSGImage); - return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight)); -} - -QSGTextureProvider *QSGImage::textureProvider() const -{ - Q_D(const QSGImage); - if (!d->provider) { - // Make sure it gets thread affinity on the rendering thread so deletion works properly.. - Q_ASSERT_X(d->canvas - && d->sceneGraphContext() - && QThread::currentThread() == d->sceneGraphContext()->thread(), - "QSGImage::textureProvider", - "Cannot be used outside the GUI thread"); - QSGImagePrivate *dd = const_cast(d); - dd->provider = new QSGImageTextureProvider; - dd->provider->m_texture = d->pix.texture(d->sceneGraphContext()); - } - - return d->provider; -} - -QSGNode *QSGImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - Q_D(QSGImage); - - QSGTexture *texture = d->pix.texture(d->sceneGraphContext()); - - // Copy over the current texture state into the texture provider... - if (d->provider) { - d->provider->m_smooth = d->smooth; - d->provider->m_texture = texture; - } - - if (!texture || width() <= 0 || height() <= 0) { - delete oldNode; - return 0; - } - - QSGImageNode *node = static_cast(oldNode); - if (!node) { - d->pixmapChanged = true; - node = d->sceneGraphContext()->createImageNode(); - node->setTexture(texture); - } - - if (d->pixmapChanged) { - // force update the texture in the node to trigger reconstruction of - // geometry and the likes when a atlas segment has changed. - node->setTexture(0); - node->setTexture(texture); - d->pixmapChanged = false; - } - - QRectF targetRect; - QRectF sourceRect; - QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge; - QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge; - - qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width(); - qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height(); - - int xOffset = 0; - if (d->hAlign == QSGImage::AlignHCenter) - xOffset = qCeil((width() - pixWidth) / 2.); - else if (d->hAlign == QSGImage::AlignRight) - xOffset = qCeil(width() - pixWidth); - - int yOffset = 0; - if (d->vAlign == QSGImage::AlignVCenter) - yOffset = qCeil((height() - pixHeight) / 2.); - else if (d->vAlign == QSGImage::AlignBottom) - yOffset = qCeil(height() - pixHeight); - - switch (d->fillMode) { - default: - case Stretch: - targetRect = QRectF(0, 0, width(), height()); - sourceRect = d->pix.rect(); - break; - - case PreserveAspectFit: - targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight); - sourceRect = d->pix.rect(); - break; - - case PreserveAspectCrop: { - targetRect = QRect(0, 0, width(), height()); - qreal wscale = width() / qreal(d->pix.width()); - qreal hscale = height() / qreal(d->pix.height()); - - if (wscale > hscale) { - int src = (hscale / wscale) * qreal(d->pix.height()); - int y = 0; - if (d->vAlign == QSGImage::AlignVCenter) - y = qCeil((d->pix.height() - src) / 2.); - else if (d->vAlign == QSGImage::AlignBottom) - y = qCeil(d->pix.height() - src); - sourceRect = QRectF(0, y, d->pix.width(), src); - - } else { - int src = (wscale / hscale) * qreal(d->pix.width()); - int x = 0; - if (d->hAlign == QSGImage::AlignHCenter) - x = qCeil((d->pix.width() - src) / 2.); - else if (d->hAlign == QSGImage::AlignRight) - x = qCeil(d->pix.width() - src); - sourceRect = QRectF(x, 0, src, d->pix.height()); - } - } - break; - - case Tile: - targetRect = QRectF(0, 0, width(), height()); - sourceRect = QRectF(-xOffset, -yOffset, width(), height()); - hWrap = QSGTexture::Repeat; - vWrap = QSGTexture::Repeat; - break; - - case TileHorizontally: - targetRect = QRectF(0, 0, width(), height()); - sourceRect = QRectF(-xOffset, 0, width(), d->pix.height()); - hWrap = QSGTexture::Repeat; - break; - - case TileVertically: - targetRect = QRectF(0, 0, width(), height()); - sourceRect = QRectF(0, -yOffset, d->pix.width(), height()); - vWrap = QSGTexture::Repeat; - break; - - case Pad: - qreal w = qMin(qreal(d->pix.width()), width()); - qreal h = qMin(qreal(d->pix.height()), height()); - qreal x = (d->pix.width() > width()) ? -xOffset : 0; - qreal y = (d->pix.height() > height()) ? -yOffset : 0; - targetRect = QRectF(x + xOffset, y + yOffset, w, h); - sourceRect = QRectF(x, y, w, h); - break; - }; - - QRectF nsrect(sourceRect.x() / d->pix.width(), - sourceRect.y() / d->pix.height(), - sourceRect.width() / d->pix.width(), - sourceRect.height() / d->pix.height()); - - if (d->mirror) { - qreal oldLeft = nsrect.left(); - nsrect.setLeft(nsrect.right()); - nsrect.setRight(oldLeft); - } - - node->setHorizontalWrapMode(hWrap); - node->setVerticalWrapMode(vWrap); - node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); - - node->setTargetRect(targetRect); - node->setSourceRect(nsrect); - node->update(); - - return node; -} - -void QSGImage::pixmapChange() -{ - Q_D(QSGImage); - // PreserveAspectFit calculates the implicit size differently so we - // don't call our superclass pixmapChange(), since that would - // result in the implicit size being set incorrectly, then updated - // in updatePaintedGeometry() - if (d->fillMode != PreserveAspectFit) - QSGImageBase::pixmapChange(); - updatePaintedGeometry(); - d->pixmapChanged = true; - - // Make sure we update the texture provider when the image has changed. - if (d->provider) - update(); -} - -QSGImage::VAlignment QSGImage::verticalAlignment() const -{ - Q_D(const QSGImage); - return d->vAlign; -} - -void QSGImage::setVerticalAlignment(VAlignment align) -{ - Q_D(QSGImage); - if (d->vAlign == align) - return; - - d->vAlign = align; - update(); - updatePaintedGeometry(); - emit verticalAlignmentChanged(align); -} - -QSGImage::HAlignment QSGImage::horizontalAlignment() const -{ - Q_D(const QSGImage); - return d->hAlign; -} - -void QSGImage::setHorizontalAlignment(HAlignment align) -{ - Q_D(QSGImage); - if (d->hAlign == align) - return; - - d->hAlign = align; - update(); - updatePaintedGeometry(); - emit horizontalAlignmentChanged(align); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgimage_p.h b/src/declarative/items/qsgimage_p.h deleted file mode 100644 index 9bf1d910d1..0000000000 --- a/src/declarative/items/qsgimage_p.h +++ /dev/null @@ -1,122 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGIMAGE_P_H -#define QSGIMAGE_P_H - -#include "qsgimagebase_p.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGImagePrivate; -class Q_AUTOTEST_EXPORT QSGImage : public QSGImageBase -{ - Q_OBJECT - Q_ENUMS(FillMode) - Q_ENUMS(HAlignment) - Q_ENUMS(VAlignment) - - Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) - Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedGeometryChanged) - Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged) - Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged) - Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged) - -public: - QSGImage(QSGItem *parent=0); - ~QSGImage(); - - enum HAlignment { AlignLeft = Qt::AlignLeft, - AlignRight = Qt::AlignRight, - AlignHCenter = Qt::AlignHCenter }; - enum VAlignment { AlignTop = Qt::AlignTop, - AlignBottom = Qt::AlignBottom, - AlignVCenter = Qt::AlignVCenter }; - - enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally, Pad }; - - FillMode fillMode() const; - void setFillMode(FillMode); - - qreal paintedWidth() const; - qreal paintedHeight() const; - - QRectF boundingRect() const; - - HAlignment horizontalAlignment() const; - void setHorizontalAlignment(HAlignment align); - - VAlignment verticalAlignment() const; - void setVerticalAlignment(VAlignment align); - - bool isTextureProvider() const { return true; } - QSGTextureProvider *textureProvider() const; - -Q_SIGNALS: - void fillModeChanged(); - void paintedGeometryChanged(); - void horizontalAlignmentChanged(HAlignment alignment); - void verticalAlignmentChanged(VAlignment alignment); - -protected: - QSGImage(QSGImagePrivate &dd, QSGItem *parent); - void pixmapChange(); - void updatePaintedGeometry(); - - virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - -private: - Q_DISABLE_COPY(QSGImage) - Q_DECLARE_PRIVATE(QSGImage) -}; - -QT_END_NAMESPACE -QML_DECLARE_TYPE(QSGImage) -QT_END_HEADER - -#endif // QSGIMAGE_P_H diff --git a/src/declarative/items/qsgimage_p_p.h b/src/declarative/items/qsgimage_p_p.h deleted file mode 100644 index 9f8b9716c7..0000000000 --- a/src/declarative/items/qsgimage_p_p.h +++ /dev/null @@ -1,85 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGIMAGE_P_P_H -#define QSGIMAGE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgimagebase_p_p.h" -#include "qsgimage_p.h" - -QT_BEGIN_NAMESPACE - -class QSGImageTextureProvider; - -class QSGImagePrivate : public QSGImageBasePrivate -{ - Q_DECLARE_PUBLIC(QSGImage) - -public: - QSGImagePrivate(); - - QSGImage::FillMode fillMode; - qreal paintedWidth; - qreal paintedHeight; - void setPixmap(const QPixmap &pix); - - bool pixmapChanged : 1; - QSGImage::HAlignment hAlign; - QSGImage::VAlignment vAlign; - - QSGImageTextureProvider *provider; -}; - -QT_END_NAMESPACE - -#endif // QSGIMAGE_P_P_H diff --git a/src/declarative/items/qsgimagebase.cpp b/src/declarative/items/qsgimagebase.cpp deleted file mode 100644 index 0c08010d34..0000000000 --- a/src/declarative/items/qsgimagebase.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgimagebase_p.h" -#include "qsgimagebase_p_p.h" - -#include - -QT_BEGIN_NAMESPACE - -QSGImageBase::QSGImageBase(QSGItem *parent) -: QSGImplicitSizeItem(*(new QSGImageBasePrivate), parent) -{ - setFlag(ItemHasContents); -} - -QSGImageBase::QSGImageBase(QSGImageBasePrivate &dd, QSGItem *parent) -: QSGImplicitSizeItem(dd, parent) -{ - setFlag(ItemHasContents); -} - -QSGImageBase::~QSGImageBase() -{ -} - -QSGImageBase::Status QSGImageBase::status() const -{ - Q_D(const QSGImageBase); - return d->status; -} - - -qreal QSGImageBase::progress() const -{ - Q_D(const QSGImageBase); - return d->progress; -} - - -bool QSGImageBase::asynchronous() const -{ - Q_D(const QSGImageBase); - return d->async; -} - -void QSGImageBase::setAsynchronous(bool async) -{ - Q_D(QSGImageBase); - if (d->async != async) { - d->async = async; - emit asynchronousChanged(); - } -} - -QUrl QSGImageBase::source() const -{ - Q_D(const QSGImageBase); - return d->url; -} - -void QSGImageBase::setSource(const QUrl &url) -{ - Q_D(QSGImageBase); - //equality is fairly expensive, so we bypass for simple, common case - if ((d->url.isEmpty() == url.isEmpty()) && url == d->url) - return; - - d->url = url; - emit sourceChanged(d->url); - - if (isComponentComplete()) - load(); -} - -void QSGImageBase::setSourceSize(const QSize& size) -{ - Q_D(QSGImageBase); - if (d->sourcesize == size) - return; - - d->sourcesize = size; - d->explicitSourceSize = true; - emit sourceSizeChanged(); - if (isComponentComplete()) - load(); -} - -QSize QSGImageBase::sourceSize() const -{ - Q_D(const QSGImageBase); - - int width = d->sourcesize.width(); - int height = d->sourcesize.height(); - return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height()); -} - -void QSGImageBase::resetSourceSize() -{ - Q_D(QSGImageBase); - if (!d->explicitSourceSize) - return; - d->explicitSourceSize = false; - d->sourcesize = QSize(); - emit sourceSizeChanged(); - if (isComponentComplete()) - load(); -} - -bool QSGImageBase::cache() const -{ - Q_D(const QSGImageBase); - return d->cache; -} - -void QSGImageBase::setCache(bool cache) -{ - Q_D(QSGImageBase); - if (d->cache == cache) - return; - - d->cache = cache; - emit cacheChanged(); - if (isComponentComplete()) - load(); -} - -QPixmap QSGImageBase::pixmap() const -{ - Q_D(const QSGImageBase); - return d->pix.pixmap(); -} - -void QSGImageBase::setMirror(bool mirror) -{ - Q_D(QSGImageBase); - if (mirror == d->mirror) - return; - - d->mirror = mirror; - - if (isComponentComplete()) - update(); - - emit mirrorChanged(); -} - -bool QSGImageBase::mirror() const -{ - Q_D(const QSGImageBase); - return d->mirror; -} - -void QSGImageBase::load() -{ - Q_D(QSGImageBase); - - if (d->url.isEmpty()) { - d->pix.clear(this); - d->status = Null; - d->progress = 0.0; - pixmapChange(); - emit progressChanged(d->progress); - emit statusChanged(d->status); - update(); - } else { - QDeclarativePixmap::Options options; - if (d->async) - options |= QDeclarativePixmap::Asynchronous; - if (d->cache) - options |= QDeclarativePixmap::Cache; - d->pix.clear(this); - pixmapChange(); - d->pix.load(qmlEngine(this), d->url, d->explicitSourceSize ? sourceSize() : QSize(), options); - - if (d->pix.isLoading()) { - d->progress = 0.0; - d->status = Loading; - emit progressChanged(d->progress); - emit statusChanged(d->status); - - static int thisRequestProgress = -1; - static int thisRequestFinished = -1; - if (thisRequestProgress == -1) { - thisRequestProgress = - QSGImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); - thisRequestFinished = - QSGImageBase::staticMetaObject.indexOfSlot("requestFinished()"); - } - - d->pix.connectFinished(this, thisRequestFinished); - d->pix.connectDownloadProgress(this, thisRequestProgress); - - } else { - requestFinished(); - } - } -} - -void QSGImageBase::requestFinished() -{ - Q_D(QSGImageBase); - - QSGImageBase::Status oldStatus = d->status; - qreal oldProgress = d->progress; - - if (d->pix.isError()) { - d->status = Error; - qmlInfo(this) << d->pix.error(); - } else { - d->status = Ready; - } - - d->progress = 1.0; - - pixmapChange(); - - if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height()) - emit sourceSizeChanged(); - - if (d->status != oldStatus) - emit statusChanged(d->status); - if (d->progress != oldProgress) - emit progressChanged(d->progress); - - update(); -} - -void QSGImageBase::requestProgress(qint64 received, qint64 total) -{ - Q_D(QSGImageBase); - if (d->status == Loading && total > 0) { - d->progress = qreal(received)/total; - emit progressChanged(d->progress); - } -} - -void QSGImageBase::componentComplete() -{ - Q_D(QSGImageBase); - QSGItem::componentComplete(); - if (d->url.isValid()) - load(); -} - -void QSGImageBase::pixmapChange() -{ - Q_D(QSGImageBase); - setImplicitWidth(d->pix.width()); - setImplicitHeight(d->pix.height()); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgimagebase_p.h b/src/declarative/items/qsgimagebase_p.h deleted file mode 100644 index e17ca3b059..0000000000 --- a/src/declarative/items/qsgimagebase_p.h +++ /dev/null @@ -1,119 +0,0 @@ -// Commit: af05f64d3edc860c3cf79c7f0bdf2377faae5f40 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGIMAGEBASE_P_H -#define QSGIMAGEBASE_P_H - -#include "qsgimplicitsizeitem_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QSGImageBasePrivate; -class Q_AUTOTEST_EXPORT QSGImageBase : public QSGImplicitSizeItem -{ - Q_OBJECT - Q_ENUMS(Status) - - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) - Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged) - Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged) - Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged) - -public: - QSGImageBase(QSGItem *parent=0); - ~QSGImageBase(); - enum Status { Null, Ready, Loading, Error }; - Status status() const; - qreal progress() const; - - QUrl source() const; - virtual void setSource(const QUrl &url); - - bool asynchronous() const; - void setAsynchronous(bool); - - bool cache() const; - void setCache(bool); - - QPixmap pixmap() const; - - virtual void setSourceSize(const QSize&); - QSize sourceSize() const; - void resetSourceSize(); - - virtual void setMirror(bool mirror); - bool mirror() const; - -Q_SIGNALS: - void sourceChanged(const QUrl &); - void sourceSizeChanged(); - void statusChanged(QSGImageBase::Status); - void progressChanged(qreal progress); - void asynchronousChanged(); - void cacheChanged(); - void mirrorChanged(); - -protected: - virtual void load(); - virtual void componentComplete(); - virtual void pixmapChange(); - QSGImageBase(QSGImageBasePrivate &dd, QSGItem *parent); - -private Q_SLOTS: - virtual void requestFinished(); - void requestProgress(qint64,qint64); - -private: - Q_DISABLE_COPY(QSGImageBase) - Q_DECLARE_PRIVATE(QSGImageBase) -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QSGIMAGEBASE_P_H diff --git a/src/declarative/items/qsgimagebase_p_p.h b/src/declarative/items/qsgimagebase_p_p.h deleted file mode 100644 index dd1a00fde4..0000000000 --- a/src/declarative/items/qsgimagebase_p_p.h +++ /dev/null @@ -1,93 +0,0 @@ -// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGIMAGEBASE_P_P_H -#define QSGIMAGEBASE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgimplicitsizeitem_p_p.h" -#include "qsgimagebase_p.h" - -#include - -QT_BEGIN_NAMESPACE - -class QNetworkReply; -class QSGImageBasePrivate : public QSGImplicitSizeItemPrivate -{ - Q_DECLARE_PUBLIC(QSGImageBase) - -public: - QSGImageBasePrivate() - : status(QSGImageBase::Null), - progress(0.0), - explicitSourceSize(false), - async(false), - cache(true), - mirror(false) - { - } - - QDeclarativePixmap pix; - QSGImageBase::Status status; - QUrl url; - qreal progress; - QSize sourcesize; - bool explicitSourceSize : 1; - bool async : 1; - bool cache : 1; - bool mirror: 1; -}; - -QT_END_NAMESPACE - -#endif // QSGIMAGEBASE_P_P_H diff --git a/src/declarative/items/qsgimplicitsizeitem.cpp b/src/declarative/items/qsgimplicitsizeitem.cpp deleted file mode 100644 index 163b73b39c..0000000000 --- a/src/declarative/items/qsgimplicitsizeitem.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgimplicitsizeitem_p.h" -#include "qsgimplicitsizeitem_p_p.h" - -QT_BEGIN_NAMESPACE - -void QSGImplicitSizeItemPrivate::implicitWidthChanged() -{ - Q_Q(QSGImplicitSizeItem); - emit q->implicitWidthChanged(); -} - -void QSGImplicitSizeItemPrivate::implicitHeightChanged() -{ - Q_Q(QSGImplicitSizeItem); - emit q->implicitHeightChanged(); -} - -QSGImplicitSizeItem::QSGImplicitSizeItem(QSGItem *parent) - : QSGItem(*(new QSGImplicitSizeItemPrivate), parent) -{ -} - -QSGImplicitSizeItem::QSGImplicitSizeItem(QSGImplicitSizeItemPrivate &dd, QSGItem *parent) - : QSGItem(dd, parent) -{ -} - - -void QSGImplicitSizePaintedItemPrivate::implicitWidthChanged() -{ - Q_Q(QSGImplicitSizePaintedItem); - emit q->implicitWidthChanged(); -} - -void QSGImplicitSizePaintedItemPrivate::implicitHeightChanged() -{ - Q_Q(QSGImplicitSizePaintedItem); - emit q->implicitHeightChanged(); -} - -QSGImplicitSizePaintedItem::QSGImplicitSizePaintedItem(QSGItem *parent) - : QSGPaintedItem(*(new QSGImplicitSizePaintedItemPrivate), parent) -{ -} - -QSGImplicitSizePaintedItem::QSGImplicitSizePaintedItem(QSGImplicitSizePaintedItemPrivate &dd, QSGItem *parent) - : QSGPaintedItem(dd, parent) -{ -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgimplicitsizeitem_p.h b/src/declarative/items/qsgimplicitsizeitem_p.h deleted file mode 100644 index b839fc2f3a..0000000000 --- a/src/declarative/items/qsgimplicitsizeitem_p.h +++ /dev/null @@ -1,101 +0,0 @@ -// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGIMPLICITSIZEITEM_H -#define QSGIMPLICITSIZEITEM_H - -#include "qsgpainteditem.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QSGImplicitSizeItemPrivate; -class Q_AUTOTEST_EXPORT QSGImplicitSizeItem : public QSGItem -{ - Q_OBJECT - Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged) - Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged) - -public: - QSGImplicitSizeItem(QSGItem *parent = 0); - -protected: - QSGImplicitSizeItem(QSGImplicitSizeItemPrivate &dd, QSGItem *parent); - -Q_SIGNALS: - void implicitWidthChanged(); - void implicitHeightChanged(); - -private: - Q_DISABLE_COPY(QSGImplicitSizeItem) - Q_DECLARE_PRIVATE(QSGImplicitSizeItem) -}; - -class QSGImplicitSizePaintedItemPrivate; -class Q_AUTOTEST_EXPORT QSGImplicitSizePaintedItem : public QSGPaintedItem -{ - Q_OBJECT - Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged) - Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged) - -public: - QSGImplicitSizePaintedItem(QSGItem *parent = 0); - -protected: - QSGImplicitSizePaintedItem(QSGImplicitSizePaintedItemPrivate &dd, QSGItem *parent); - virtual void drawContents(QPainter *, const QRect &) {}; - -Q_SIGNALS: - void implicitWidthChanged(); - void implicitHeightChanged(); - -private: - Q_DISABLE_COPY(QSGImplicitSizePaintedItem) - Q_DECLARE_PRIVATE(QSGImplicitSizePaintedItem) -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QSGIMPLICITSIZEITEM_H diff --git a/src/declarative/items/qsgimplicitsizeitem_p_p.h b/src/declarative/items/qsgimplicitsizeitem_p_p.h deleted file mode 100644 index 3ddd69d7d3..0000000000 --- a/src/declarative/items/qsgimplicitsizeitem_p_p.h +++ /dev/null @@ -1,92 +0,0 @@ -// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGIMPLICITSIZEITEM_P_H -#define QSGIMPLICITSIZEITEM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgitem_p.h" -#include "qsgpainteditem_p.h" -#include "qsgimplicitsizeitem_p.h" - -QT_BEGIN_NAMESPACE - -class QSGImplicitSizeItemPrivate : public QSGItemPrivate -{ - Q_DECLARE_PUBLIC(QSGImplicitSizeItem) - -public: - QSGImplicitSizeItemPrivate() - { - } - - virtual void implicitWidthChanged(); - virtual void implicitHeightChanged(); -}; - - -class QSGImplicitSizePaintedItemPrivate : public QSGPaintedItemPrivate -{ - Q_DECLARE_PUBLIC(QSGImplicitSizePaintedItem) - -public: - QSGImplicitSizePaintedItemPrivate() - { - } - - virtual void implicitWidthChanged(); - virtual void implicitHeightChanged(); -}; - -QT_END_NAMESPACE - -#endif // QSGIMPLICITSIZEITEM_P_H diff --git a/src/declarative/items/qsgitem.cpp b/src/declarative/items/qsgitem.cpp deleted file mode 100644 index bdea19b3e0..0000000000 --- a/src/declarative/items/qsgitem.cpp +++ /dev/null @@ -1,5038 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgitem.h" - -#include "qsgcanvas.h" -#include -#include "qsgcanvas_p.h" - -#include "qsgevents_p_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -// XXX todo Readd parentNotifier for faster parent bindings -// XXX todo Check that elements that create items handle memory correctly after visual ownership change - -QT_BEGIN_NAMESPACE - -/*! - \qmlclass Transform QSGTransform - \inqmlmodule QtQuick 2 - \ingroup qml-transform-elements - \brief The Transform elements provide a way of building advanced transformations on Items. - - The Transform element is a base type which cannot be instantiated directly. - The following concrete Transform types are available: - - \list - \o \l Rotation - \o \l Scale - \o \l Translate - \endlist - - The Transform elements let you create and control advanced transformations that can be configured - independently using specialized properties. - - You can assign any number of Transform elements to an \l Item. Each Transform is applied in order, - one at a time. -*/ - -/*! - \qmlclass Translate QSGTranslate - \inqmlmodule QtQuick 2 - \ingroup qml-transform-elements - \brief The Translate object provides a way to move an Item without changing its x or y properties. - - The Translate object provides independent control over position in addition to the Item's x and y properties. - - The following example moves the Y axis of the \l Rectangle elements while still allowing the \l Row element - to lay the items out as if they had not been transformed: - \qml - import QtQuick 1.0 - - Row { - Rectangle { - width: 100; height: 100 - color: "blue" - transform: Translate { y: 20 } - } - Rectangle { - width: 100; height: 100 - color: "red" - transform: Translate { y: -20 } - } - } - \endqml - - \image translate.png -*/ - -/*! - \qmlproperty real QtQuick2::Translate::x - - The translation along the X axis. -*/ - -/*! - \qmlproperty real QtQuick2::Translate::y - - The translation along the Y axis. -*/ - -/*! - \qmlclass Scale QSGScale - \inqmlmodule QtQuick 2 - \ingroup qml-transform-elements - \brief The Scale element provides a way to scale an Item. - - The Scale element gives more control over scaling than using \l Item's \l{Item::scale}{scale} property. Specifically, - it allows a different scale for the x and y axes, and allows the scale to be relative to an - arbitrary point. - - The following example scales the X axis of the Rectangle, relative to its interior point 25, 25: - \qml - Rectangle { - width: 100; height: 100 - color: "blue" - transform: Scale { origin.x: 25; origin.y: 25; xScale: 3} - } - \endqml - - \sa Rotation, Translate -*/ - -/*! - \qmlproperty real QtQuick2::Scale::origin.x - \qmlproperty real QtQuick2::Scale::origin.y - - The point that the item is scaled from (i.e., the point that stays fixed relative to the parent as - the rest of the item grows). By default the origin is 0, 0. -*/ - -/*! - \qmlproperty real QtQuick2::Scale::xScale - - The scaling factor for the X axis. -*/ - -/*! - \qmlproperty real QtQuick2::Scale::yScale - - The scaling factor for the Y axis. -*/ - -/*! - \qmlclass Rotation QSGRotation - \inqmlmodule QtQuick 2 - \ingroup qml-transform-elements - \brief The Rotation object provides a way to rotate an Item. - - The Rotation object gives more control over rotation than using \l Item's \l{Item::rotation}{rotation} property. - Specifically, it allows (z axis) rotation to be relative to an arbitrary point. - - The following example rotates a Rectangle around its interior point 25, 25: - \qml - Rectangle { - width: 100; height: 100 - color: "blue" - transform: Rotation { origin.x: 25; origin.y: 25; angle: 45} - } - \endqml - - Rotation also provides a way to specify 3D-like rotations for Items. For these types of - rotations you must specify the axis to rotate around in addition to the origin point. - - The following example shows various 3D-like rotations applied to an \l Image. - \snippet doc/src/snippets/declarative/rotation.qml 0 - - \image axisrotation.png - - \sa {declarative/ui-components/dialcontrol}{Dial Control example}, {declarative/toys/clocks}{Clocks example} -*/ - -/*! - \qmlproperty real QtQuick2::Rotation::origin.x - \qmlproperty real QtQuick2::Rotation::origin.y - - The origin point of the rotation (i.e., the point that stays fixed relative to the parent as - the rest of the item rotates). By default the origin is 0, 0. -*/ - -/*! - \qmlproperty real QtQuick2::Rotation::axis.x - \qmlproperty real QtQuick2::Rotation::axis.y - \qmlproperty real QtQuick2::Rotation::axis.z - - The axis to rotate around. For simple (2D) rotation around a point, you do not need to specify an axis, - as the default axis is the z axis (\c{ axis { x: 0; y: 0; z: 1 } }). - - For a typical 3D-like rotation you will usually specify both the origin and the axis. - - \image 3d-rotation-axis.png -*/ - -/*! - \qmlproperty real QtQuick2::Rotation::angle - - The angle to rotate, in degrees clockwise. -*/ - -QSGTransformPrivate::QSGTransformPrivate() -{ -} - -QSGTransform::QSGTransform(QObject *parent) -: QObject(*(new QSGTransformPrivate), parent) -{ -} - -QSGTransform::QSGTransform(QSGTransformPrivate &dd, QObject *parent) -: QObject(dd, parent) -{ -} - -QSGTransform::~QSGTransform() -{ - Q_D(QSGTransform); - for (int ii = 0; ii < d->items.count(); ++ii) { - QSGItemPrivate *p = QSGItemPrivate::get(d->items.at(ii)); - p->transforms.removeOne(this); - p->dirty(QSGItemPrivate::Transform); - } -} - -void QSGTransform::update() -{ - Q_D(QSGTransform); - for (int ii = 0; ii < d->items.count(); ++ii) { - QSGItemPrivate *p = QSGItemPrivate::get(d->items.at(ii)); - p->dirty(QSGItemPrivate::Transform); - } -} - -QSGContents::QSGContents(QSGItem *item) -: m_item(item), m_x(0), m_y(0), m_width(0), m_height(0) -{ - //### optimize - connect(this, SIGNAL(rectChanged(QRectF)), m_item, SIGNAL(childrenRectChanged(QRectF))); -} - -QSGContents::~QSGContents() -{ - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QSGItem *child = children.at(i); - QSGItemPrivate::get(child)->removeItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed); - } -} - -QRectF QSGContents::rectF() const -{ - return QRectF(m_x, m_y, m_width, m_height); -} - -void QSGContents::calcHeight(QSGItem *changed) -{ - qreal oldy = m_y; - qreal oldheight = m_height; - - if (changed) { - qreal top = oldy; - qreal bottom = oldy + oldheight; - qreal y = changed->y(); - if (y + changed->height() > bottom) - bottom = y + changed->height(); - if (y < top) - top = y; - m_y = top; - m_height = bottom - top; - } else { - qreal top = FLT_MAX; - qreal bottom = 0; - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QSGItem *child = children.at(i); - qreal y = child->y(); - if (y + child->height() > bottom) - bottom = y + child->height(); - if (y < top) - top = y; - } - if (!children.isEmpty()) - m_y = top; - m_height = qMax(bottom - top, qreal(0.0)); - } - - if (m_height != oldheight || m_y != oldy) - emit rectChanged(rectF()); -} - -void QSGContents::calcWidth(QSGItem *changed) -{ - qreal oldx = m_x; - qreal oldwidth = m_width; - - if (changed) { - qreal left = oldx; - qreal right = oldx + oldwidth; - qreal x = changed->x(); - if (x + changed->width() > right) - right = x + changed->width(); - if (x < left) - left = x; - m_x = left; - m_width = right - left; - } else { - qreal left = FLT_MAX; - qreal right = 0; - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QSGItem *child = children.at(i); - qreal x = child->x(); - if (x + child->width() > right) - right = x + child->width(); - if (x < left) - left = x; - } - if (!children.isEmpty()) - m_x = left; - m_width = qMax(right - left, qreal(0.0)); - } - - if (m_width != oldwidth || m_x != oldx) - emit rectChanged(rectF()); -} - -void QSGContents::complete() -{ - QList children = m_item->childItems(); - for (int i = 0; i < children.count(); ++i) { - QSGItem *child = children.at(i); - QSGItemPrivate::get(child)->addItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed); - //###what about changes to visibility? - } - - calcGeometry(); -} - -void QSGContents::itemGeometryChanged(QSGItem *changed, const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_UNUSED(changed) - //### we can only pass changed if the left edge has moved left, or the right edge has moved right - if (newGeometry.width() != oldGeometry.width() || newGeometry.x() != oldGeometry.x()) - calcWidth(/*changed*/); - if (newGeometry.height() != oldGeometry.height() || newGeometry.y() != oldGeometry.y()) - calcHeight(/*changed*/); -} - -void QSGContents::itemDestroyed(QSGItem *item) -{ - if (item) - QSGItemPrivate::get(item)->removeItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed); - calcGeometry(); -} - -void QSGContents::childRemoved(QSGItem *item) -{ - if (item) - QSGItemPrivate::get(item)->removeItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed); - calcGeometry(); -} - -void QSGContents::childAdded(QSGItem *item) -{ - if (item) - QSGItemPrivate::get(item)->addItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed); - calcWidth(item); - calcHeight(item); -} - -QSGItemKeyFilter::QSGItemKeyFilter(QSGItem *item) -: m_processPost(false), m_next(0) -{ - QSGItemPrivate *p = item?QSGItemPrivate::get(item):0; - if (p) { - m_next = p->keyHandler; - p->keyHandler = this; - } -} - -QSGItemKeyFilter::~QSGItemKeyFilter() -{ -} - -void QSGItemKeyFilter::keyPressed(QKeyEvent *event, bool post) -{ - if (m_next) m_next->keyPressed(event, post); -} - -void QSGItemKeyFilter::keyReleased(QKeyEvent *event, bool post) -{ - if (m_next) m_next->keyReleased(event, post); -} - -void QSGItemKeyFilter::inputMethodEvent(QInputMethodEvent *event, bool post) -{ - if (m_next) - m_next->inputMethodEvent(event, post); - else - event->ignore(); -} - -QVariant QSGItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const -{ - if (m_next) return m_next->inputMethodQuery(query); - return QVariant(); -} - -void QSGItemKeyFilter::componentComplete() -{ - if (m_next) m_next->componentComplete(); -} -/*! - \qmlclass KeyNavigation QSGKeyNavigationAttached - \inqmlmodule QtQuick 2 - \ingroup qml-basic-interaction-elements - \brief The KeyNavigation attached property supports key navigation by arrow keys. - - Key-based user interfaces commonly allow the use of arrow keys to navigate between - focusable items. The KeyNavigation attached property enables this behavior by providing a - convenient way to specify the item that should gain focus when an arrow or tab key is pressed. - - The following example provides key navigation for a 2x2 grid of items: - - \snippet doc/src/snippets/declarative/keynavigation.qml 0 - - The top-left item initially receives focus by setting \l {Item::}{focus} to - \c true. When an arrow key is pressed, the focus will move to the - appropriate item, as defined by the value that has been set for - the KeyNavigation \l left, \l right, \l up or \l down properties. - - Note that if a KeyNavigation attached property receives the key press and release - events for a requested arrow or tab key, the event is accepted and does not - propagate any further. - - By default, KeyNavigation receives key events after the item to which it is attached. - If the item accepts the key event, the KeyNavigation attached property will not - receive an event for that key. Setting the \l priority property to - \c KeyNavigation.BeforeItem allows the event to be used for key navigation - before the item, rather than after. - - If item to which the focus is switching is not enabled or visible, an attempt will - be made to skip this item and focus on the next. This is possible if there are - a chain of items with the same KeyNavigation handler. If multiple items in a row are not enabled - or visible, they will also be skipped. - - KeyNavigation will implicitly set the other direction to return focus to this item. So if you set - \l left to another item, \l right will be set on that item's KeyNavigation to set focus back to this - item. However, if that item's KeyNavigation has had right explicitly set then no change will occur. - This means that the above example could have been written, with the same behaviour, without specifing - KeyNavigation.right or KeyNavigation.down for any of the items. - - \sa {Keys}{Keys attached property} -*/ - -/*! - \qmlproperty Item QtQuick2::KeyNavigation::left - \qmlproperty Item QtQuick2::KeyNavigation::right - \qmlproperty Item QtQuick2::KeyNavigation::up - \qmlproperty Item QtQuick2::KeyNavigation::down - \qmlproperty Item QtQuick2::KeyNavigation::tab - \qmlproperty Item QtQuick2::KeyNavigation::backtab - - These properties hold the item to assign focus to - when the left, right, up or down cursor keys, or the - tab key are pressed. -*/ - -/*! - \qmlproperty Item QtQuick2::KeyNavigation::tab - \qmlproperty Item QtQuick2::KeyNavigation::backtab - - These properties hold the item to assign focus to - when the Tab key or Shift+Tab key combination (Backtab) are pressed. -*/ - -QSGKeyNavigationAttached::QSGKeyNavigationAttached(QObject *parent) -: QObject(*(new QSGKeyNavigationAttachedPrivate), parent), - QSGItemKeyFilter(qobject_cast(parent)) -{ - m_processPost = true; -} - -QSGKeyNavigationAttached * -QSGKeyNavigationAttached::qmlAttachedProperties(QObject *obj) -{ - return new QSGKeyNavigationAttached(obj); -} - -QSGItem *QSGKeyNavigationAttached::left() const -{ - Q_D(const QSGKeyNavigationAttached); - return d->left; -} - -void QSGKeyNavigationAttached::setLeft(QSGItem *i) -{ - Q_D(QSGKeyNavigationAttached); - if (d->left == i) - return; - d->left = i; - d->leftSet = true; - QSGKeyNavigationAttached* other = - qobject_cast(qmlAttachedPropertiesObject(i)); - if (other && !other->d_func()->rightSet){ - other->d_func()->right = qobject_cast(parent()); - emit other->rightChanged(); - } - emit leftChanged(); -} - -QSGItem *QSGKeyNavigationAttached::right() const -{ - Q_D(const QSGKeyNavigationAttached); - return d->right; -} - -void QSGKeyNavigationAttached::setRight(QSGItem *i) -{ - Q_D(QSGKeyNavigationAttached); - if (d->right == i) - return; - d->right = i; - d->rightSet = true; - QSGKeyNavigationAttached* other = - qobject_cast(qmlAttachedPropertiesObject(i)); - if (other && !other->d_func()->leftSet){ - other->d_func()->left = qobject_cast(parent()); - emit other->leftChanged(); - } - emit rightChanged(); -} - -QSGItem *QSGKeyNavigationAttached::up() const -{ - Q_D(const QSGKeyNavigationAttached); - return d->up; -} - -void QSGKeyNavigationAttached::setUp(QSGItem *i) -{ - Q_D(QSGKeyNavigationAttached); - if (d->up == i) - return; - d->up = i; - d->upSet = true; - QSGKeyNavigationAttached* other = - qobject_cast(qmlAttachedPropertiesObject(i)); - if (other && !other->d_func()->downSet){ - other->d_func()->down = qobject_cast(parent()); - emit other->downChanged(); - } - emit upChanged(); -} - -QSGItem *QSGKeyNavigationAttached::down() const -{ - Q_D(const QSGKeyNavigationAttached); - return d->down; -} - -void QSGKeyNavigationAttached::setDown(QSGItem *i) -{ - Q_D(QSGKeyNavigationAttached); - if (d->down == i) - return; - d->down = i; - d->downSet = true; - QSGKeyNavigationAttached* other = - qobject_cast(qmlAttachedPropertiesObject(i)); - if (other && !other->d_func()->upSet) { - other->d_func()->up = qobject_cast(parent()); - emit other->upChanged(); - } - emit downChanged(); -} - -QSGItem *QSGKeyNavigationAttached::tab() const -{ - Q_D(const QSGKeyNavigationAttached); - return d->tab; -} - -void QSGKeyNavigationAttached::setTab(QSGItem *i) -{ - Q_D(QSGKeyNavigationAttached); - if (d->tab == i) - return; - d->tab = i; - d->tabSet = true; - QSGKeyNavigationAttached* other = - qobject_cast(qmlAttachedPropertiesObject(i)); - if (other && !other->d_func()->backtabSet) { - other->d_func()->backtab = qobject_cast(parent()); - emit other->backtabChanged(); - } - emit tabChanged(); -} - -QSGItem *QSGKeyNavigationAttached::backtab() const -{ - Q_D(const QSGKeyNavigationAttached); - return d->backtab; -} - -void QSGKeyNavigationAttached::setBacktab(QSGItem *i) -{ - Q_D(QSGKeyNavigationAttached); - if (d->backtab == i) - return; - d->backtab = i; - d->backtabSet = true; - QSGKeyNavigationAttached* other = - qobject_cast(qmlAttachedPropertiesObject(i)); - if (other && !other->d_func()->tabSet) { - other->d_func()->tab = qobject_cast(parent()); - emit other->tabChanged(); - } - emit backtabChanged(); -} - -/*! - \qmlproperty enumeration QtQuick2::KeyNavigation::priority - - This property determines whether the keys are processed before - or after the attached item's own key handling. - - \list - \o KeyNavigation.BeforeItem - process the key events before normal - item key processing. If the event is used for key navigation, it will be accepted and will not - be passed on to the item. - \o KeyNavigation.AfterItem (default) - process the key events after normal item key - handling. If the item accepts the key event it will not be - handled by the KeyNavigation attached property handler. - \endlist -*/ -QSGKeyNavigationAttached::Priority QSGKeyNavigationAttached::priority() const -{ - return m_processPost ? AfterItem : BeforeItem; -} - -void QSGKeyNavigationAttached::setPriority(Priority order) -{ - bool processPost = order == AfterItem; - if (processPost != m_processPost) { - m_processPost = processPost; - emit priorityChanged(); - } -} - -void QSGKeyNavigationAttached::keyPressed(QKeyEvent *event, bool post) -{ - Q_D(QSGKeyNavigationAttached); - event->ignore(); - - if (post != m_processPost) { - QSGItemKeyFilter::keyPressed(event, post); - return; - } - - bool mirror = false; - switch (event->key()) { - case Qt::Key_Left: { - if (QSGItem *parentItem = qobject_cast(parent())) - mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror; - QSGItem* leftItem = mirror ? d->right : d->left; - if (leftItem) { - setFocusNavigation(leftItem, mirror ? "right" : "left"); - event->accept(); - } - break; - } - case Qt::Key_Right: { - if (QSGItem *parentItem = qobject_cast(parent())) - mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror; - QSGItem* rightItem = mirror ? d->left : d->right; - if (rightItem) { - setFocusNavigation(rightItem, mirror ? "left" : "right"); - event->accept(); - } - break; - } - case Qt::Key_Up: - if (d->up) { - setFocusNavigation(d->up, "up"); - event->accept(); - } - break; - case Qt::Key_Down: - if (d->down) { - setFocusNavigation(d->down, "down"); - event->accept(); - } - break; - case Qt::Key_Tab: - if (d->tab) { - setFocusNavigation(d->tab, "tab"); - event->accept(); - } - break; - case Qt::Key_Backtab: - if (d->backtab) { - setFocusNavigation(d->backtab, "backtab"); - event->accept(); - } - break; - default: - break; - } - - if (!event->isAccepted()) QSGItemKeyFilter::keyPressed(event, post); -} - -void QSGKeyNavigationAttached::keyReleased(QKeyEvent *event, bool post) -{ - Q_D(QSGKeyNavigationAttached); - event->ignore(); - - if (post != m_processPost) { - QSGItemKeyFilter::keyReleased(event, post); - return; - } - - bool mirror = false; - switch (event->key()) { - case Qt::Key_Left: - if (QSGItem *parentItem = qobject_cast(parent())) - mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror; - if (mirror ? d->right : d->left) - event->accept(); - break; - case Qt::Key_Right: - if (QSGItem *parentItem = qobject_cast(parent())) - mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror; - if (mirror ? d->left : d->right) - event->accept(); - break; - case Qt::Key_Up: - if (d->up) { - event->accept(); - } - break; - case Qt::Key_Down: - if (d->down) { - event->accept(); - } - break; - case Qt::Key_Tab: - if (d->tab) { - event->accept(); - } - break; - case Qt::Key_Backtab: - if (d->backtab) { - event->accept(); - } - break; - default: - break; - } - - if (!event->isAccepted()) QSGItemKeyFilter::keyReleased(event, post); -} - -void QSGKeyNavigationAttached::setFocusNavigation(QSGItem *currentItem, const char *dir) -{ - QSGItem *initialItem = currentItem; - bool isNextItem = false; - do { - isNextItem = false; - if (currentItem->isVisible() && currentItem->isEnabled()) { - currentItem->setFocus(true); - } else { - QObject *attached = - qmlAttachedPropertiesObject(currentItem, false); - if (attached) { - QSGItem *tempItem = qvariant_cast(attached->property(dir)); - if (tempItem) { - currentItem = tempItem; - isNextItem = true; - } - } - } - } - while (currentItem != initialItem && isNextItem); -} - -const QSGKeysAttached::SigMap QSGKeysAttached::sigMap[] = { - { Qt::Key_Left, "leftPressed" }, - { Qt::Key_Right, "rightPressed" }, - { Qt::Key_Up, "upPressed" }, - { Qt::Key_Down, "downPressed" }, - { Qt::Key_Tab, "tabPressed" }, - { Qt::Key_Backtab, "backtabPressed" }, - { Qt::Key_Asterisk, "asteriskPressed" }, - { Qt::Key_NumberSign, "numberSignPressed" }, - { Qt::Key_Escape, "escapePressed" }, - { Qt::Key_Return, "returnPressed" }, - { Qt::Key_Enter, "enterPressed" }, - { Qt::Key_Delete, "deletePressed" }, - { Qt::Key_Space, "spacePressed" }, - { Qt::Key_Back, "backPressed" }, - { Qt::Key_Cancel, "cancelPressed" }, - { Qt::Key_Select, "selectPressed" }, - { Qt::Key_Yes, "yesPressed" }, - { Qt::Key_No, "noPressed" }, - { Qt::Key_Context1, "context1Pressed" }, - { Qt::Key_Context2, "context2Pressed" }, - { Qt::Key_Context3, "context3Pressed" }, - { Qt::Key_Context4, "context4Pressed" }, - { Qt::Key_Call, "callPressed" }, - { Qt::Key_Hangup, "hangupPressed" }, - { Qt::Key_Flip, "flipPressed" }, - { Qt::Key_Menu, "menuPressed" }, - { Qt::Key_VolumeUp, "volumeUpPressed" }, - { Qt::Key_VolumeDown, "volumeDownPressed" }, - { 0, 0 } -}; - -bool QSGKeysAttachedPrivate::isConnected(const char *signalName) -{ - return isSignalConnected(signalIndex(signalName)); -} - -/*! - \qmlclass Keys QSGKeysAttached - \inqmlmodule QtQuick 2 - \ingroup qml-basic-interaction-elements - \brief The Keys attached property provides key handling to Items. - - All visual primitives support key handling via the Keys - attached property. Keys can be handled via the onPressed - and onReleased signal properties. - - The signal properties have a \l KeyEvent parameter, named - \e event which contains details of the event. If a key is - handled \e event.accepted should be set to true to prevent the - event from propagating up the item hierarchy. - - \section1 Example Usage - - The following example shows how the general onPressed handler can - be used to test for a certain key; in this case, the left cursor - key: - - \snippet doc/src/snippets/declarative/keys/keys-pressed.qml key item - - Some keys may alternatively be handled via specific signal properties, - for example \e onSelectPressed. These handlers automatically set - \e event.accepted to true. - - \snippet doc/src/snippets/declarative/keys/keys-handler.qml key item - - See \l{Qt::Key}{Qt.Key} for the list of keyboard codes. - - \section1 Key Handling Priorities - - The Keys attached property can be configured to handle key events - before or after the item it is attached to. This makes it possible - to intercept events in order to override an item's default behavior, - or act as a fallback for keys not handled by the item. - - If \l priority is Keys.BeforeItem (default) the order of key event processing is: - - \list 1 - \o Items specified in \c forwardTo - \o specific key handlers, e.g. onReturnPressed - \o onKeyPress, onKeyRelease handlers - \o Item specific key handling, e.g. TextInput key handling - \o parent item - \endlist - - If priority is Keys.AfterItem the order of key event processing is: - - \list 1 - \o Item specific key handling, e.g. TextInput key handling - \o Items specified in \c forwardTo - \o specific key handlers, e.g. onReturnPressed - \o onKeyPress, onKeyRelease handlers - \o parent item - \endlist - - If the event is accepted during any of the above steps, key - propagation stops. - - \sa KeyEvent, {KeyNavigation}{KeyNavigation attached property} -*/ - -/*! - \qmlproperty bool QtQuick2::Keys::enabled - - This flags enables key handling if true (default); otherwise - no key handlers will be called. -*/ - -/*! - \qmlproperty enumeration QtQuick2::Keys::priority - - This property determines whether the keys are processed before - or after the attached item's own key handling. - - \list - \o Keys.BeforeItem (default) - process the key events before normal - item key processing. If the event is accepted it will not - be passed on to the item. - \o Keys.AfterItem - process the key events after normal item key - handling. If the item accepts the key event it will not be - handled by the Keys attached property handler. - \endlist -*/ - -/*! - \qmlproperty list QtQuick2::Keys::forwardTo - - This property provides a way to forward key presses, key releases, and keyboard input - coming from input methods to other items. This can be useful when you want - one item to handle some keys (e.g. the up and down arrow keys), and another item to - handle other keys (e.g. the left and right arrow keys). Once an item that has been - forwarded keys accepts the event it is no longer forwarded to items later in the - list. - - This example forwards key events to two lists: - \qml - Item { - ListView { - id: list1 - // ... - } - ListView { - id: list2 - // ... - } - Keys.forwardTo: [list1, list2] - focus: true - } - \endqml -*/ - -/*! - \qmlsignal QtQuick2::Keys::onPressed(KeyEvent event) - - This handler is called when a key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onReleased(KeyEvent event) - - This handler is called when a key has been released. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit0Pressed(KeyEvent event) - - This handler is called when the digit '0' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit1Pressed(KeyEvent event) - - This handler is called when the digit '1' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit2Pressed(KeyEvent event) - - This handler is called when the digit '2' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit3Pressed(KeyEvent event) - - This handler is called when the digit '3' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit4Pressed(KeyEvent event) - - This handler is called when the digit '4' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit5Pressed(KeyEvent event) - - This handler is called when the digit '5' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit6Pressed(KeyEvent event) - - This handler is called when the digit '6' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit7Pressed(KeyEvent event) - - This handler is called when the digit '7' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit8Pressed(KeyEvent event) - - This handler is called when the digit '8' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDigit9Pressed(KeyEvent event) - - This handler is called when the digit '9' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onLeftPressed(KeyEvent event) - - This handler is called when the Left arrow has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onRightPressed(KeyEvent event) - - This handler is called when the Right arrow has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onUpPressed(KeyEvent event) - - This handler is called when the Up arrow has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDownPressed(KeyEvent event) - - This handler is called when the Down arrow has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onTabPressed(KeyEvent event) - - This handler is called when the Tab key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onBacktabPressed(KeyEvent event) - - This handler is called when the Shift+Tab key combination (Backtab) has - been pressed. The \a event parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onAsteriskPressed(KeyEvent event) - - This handler is called when the Asterisk '*' has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onEscapePressed(KeyEvent event) - - This handler is called when the Escape key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onReturnPressed(KeyEvent event) - - This handler is called when the Return key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onEnterPressed(KeyEvent event) - - This handler is called when the Enter key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onDeletePressed(KeyEvent event) - - This handler is called when the Delete key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onSpacePressed(KeyEvent event) - - This handler is called when the Space key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onBackPressed(KeyEvent event) - - This handler is called when the Back key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onCancelPressed(KeyEvent event) - - This handler is called when the Cancel key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onSelectPressed(KeyEvent event) - - This handler is called when the Select key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onYesPressed(KeyEvent event) - - This handler is called when the Yes key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onNoPressed(KeyEvent event) - - This handler is called when the No key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onContext1Pressed(KeyEvent event) - - This handler is called when the Context1 key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onContext2Pressed(KeyEvent event) - - This handler is called when the Context2 key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onContext3Pressed(KeyEvent event) - - This handler is called when the Context3 key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onContext4Pressed(KeyEvent event) - - This handler is called when the Context4 key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onCallPressed(KeyEvent event) - - This handler is called when the Call key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onHangupPressed(KeyEvent event) - - This handler is called when the Hangup key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onFlipPressed(KeyEvent event) - - This handler is called when the Flip key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onMenuPressed(KeyEvent event) - - This handler is called when the Menu key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onVolumeUpPressed(KeyEvent event) - - This handler is called when the VolumeUp key has been pressed. The \a event - parameter provides information about the event. -*/ - -/*! - \qmlsignal QtQuick2::Keys::onVolumeDownPressed(KeyEvent event) - - This handler is called when the VolumeDown key has been pressed. The \a event - parameter provides information about the event. -*/ - -QSGKeysAttached::QSGKeysAttached(QObject *parent) -: QObject(*(new QSGKeysAttachedPrivate), parent), - QSGItemKeyFilter(qobject_cast(parent)) -{ - Q_D(QSGKeysAttached); - m_processPost = false; - d->item = qobject_cast(parent); -} - -QSGKeysAttached::~QSGKeysAttached() -{ -} - -QSGKeysAttached::Priority QSGKeysAttached::priority() const -{ - return m_processPost ? AfterItem : BeforeItem; -} - -void QSGKeysAttached::setPriority(Priority order) -{ - bool processPost = order == AfterItem; - if (processPost != m_processPost) { - m_processPost = processPost; - emit priorityChanged(); - } -} - -void QSGKeysAttached::componentComplete() -{ - Q_D(QSGKeysAttached); - if (d->item) { - for (int ii = 0; ii < d->targets.count(); ++ii) { - QSGItem *targetItem = d->targets.at(ii); - if (targetItem && (targetItem->flags() & QSGItem::ItemAcceptsInputMethod)) { - d->item->setFlag(QSGItem::ItemAcceptsInputMethod); - break; - } - } - } -} - -void QSGKeysAttached::keyPressed(QKeyEvent *event, bool post) -{ - Q_D(QSGKeysAttached); - if (post != m_processPost || !d->enabled || d->inPress) { - event->ignore(); - QSGItemKeyFilter::keyPressed(event, post); - return; - } - - // first process forwards - if (d->item && d->item->canvas()) { - d->inPress = true; - for (int ii = 0; ii < d->targets.count(); ++ii) { - QSGItem *i = d->targets.at(ii); - if (i && i->isVisible()) { - d->item->canvas()->sendEvent(i, event); - if (event->isAccepted()) { - d->inPress = false; - return; - } - } - } - d->inPress = false; - } - - QSGKeyEvent ke(*event); - QByteArray keySignal = keyToSignal(event->key()); - if (!keySignal.isEmpty()) { - keySignal += "(QSGKeyEvent*)"; - if (d->isConnected(keySignal)) { - // If we specifically handle a key then default to accepted - ke.setAccepted(true); - int idx = QSGKeysAttached::staticMetaObject.indexOfSignal(keySignal); - metaObject()->method(idx).invoke(this, Qt::DirectConnection, Q_ARG(QSGKeyEvent*, &ke)); - } - } - if (!ke.isAccepted()) - emit pressed(&ke); - event->setAccepted(ke.isAccepted()); - - if (!event->isAccepted()) QSGItemKeyFilter::keyPressed(event, post); -} - -void QSGKeysAttached::keyReleased(QKeyEvent *event, bool post) -{ - Q_D(QSGKeysAttached); - if (post != m_processPost || !d->enabled || d->inRelease) { - event->ignore(); - QSGItemKeyFilter::keyReleased(event, post); - return; - } - - if (d->item && d->item->canvas()) { - d->inRelease = true; - for (int ii = 0; ii < d->targets.count(); ++ii) { - QSGItem *i = d->targets.at(ii); - if (i && i->isVisible()) { - d->item->canvas()->sendEvent(i, event); - if (event->isAccepted()) { - d->inRelease = false; - return; - } - } - } - d->inRelease = false; - } - - QSGKeyEvent ke(*event); - emit released(&ke); - event->setAccepted(ke.isAccepted()); - - if (!event->isAccepted()) QSGItemKeyFilter::keyReleased(event, post); -} - -void QSGKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) -{ - Q_D(QSGKeysAttached); - if (post == m_processPost && d->item && !d->inIM && d->item->canvas()) { - d->inIM = true; - for (int ii = 0; ii < d->targets.count(); ++ii) { - QSGItem *i = d->targets.at(ii); - if (i && i->isVisible() && (i->flags() & QSGItem::ItemAcceptsInputMethod)) { - d->item->canvas()->sendEvent(i, event); - if (event->isAccepted()) { - d->imeItem = i; - d->inIM = false; - return; - } - } - } - d->inIM = false; - } - QSGItemKeyFilter::inputMethodEvent(event, post); -} - -QVariant QSGKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const -{ - Q_D(const QSGKeysAttached); - if (d->item) { - for (int ii = 0; ii < d->targets.count(); ++ii) { - QSGItem *i = d->targets.at(ii); - if (i && i->isVisible() && (i->flags() & QSGItem::ItemAcceptsInputMethod) && i == d->imeItem) { - //### how robust is i == d->imeItem check? - QVariant v = i->inputMethodQuery(query); - if (v.userType() == QVariant::RectF) - v = d->item->mapRectFromItem(i, v.toRectF()); //### cost? - return v; - } - } - } - return QSGItemKeyFilter::inputMethodQuery(query); -} - -QSGKeysAttached *QSGKeysAttached::qmlAttachedProperties(QObject *obj) -{ - return new QSGKeysAttached(obj); -} - -/*! - \qmlclass LayoutMirroring QSGLayoutMirroringAttached - \inqmlmodule QtQuick 2 - \ingroup qml-utility-elements - \brief The LayoutMirroring attached property is used to mirror layout behavior. - - The LayoutMirroring attached property is used to horizontally mirror \l {anchor-layout}{Item anchors}, - \l{Using QML Positioner and Repeater Items}{positioner} elements (such as \l Row and \l Grid) - and views (such as \l GridView and horizontal \l ListView). Mirroring is a visual change: left - anchors become right anchors, and positioner elements like \l Grid and \l Row reverse the - horizontal layout of child items. - - Mirroring is enabled for an item by setting the \l enabled property to true. By default, this - only affects the item itself; setting the \l childrenInherit property to true propagates the mirroring - behavior to all child elements as well. If the \c LayoutMirroring attached property has not been defined - for an item, mirroring is not enabled. - - The following example shows mirroring in action. The \l Row below is specified as being anchored - to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally - reversed and it is now anchored to the right. Also, since items in a \l Row are positioned - from left to right by default, they are now positioned from right to left instead, as demonstrated - by the numbering and opacity of the items: - - \snippet doc/src/snippets/declarative/layoutmirroring.qml 0 - - \image layoutmirroring.png - - Layout mirroring is useful when it is necessary to support both left-to-right and right-to-left - layout versions of an application to target different language areas. The \l childrenInherit - property allows layout mirroring to be applied without manually setting layout configurations - for every item in an application. Keep in mind, however, that mirroring does not affect any - positioning that is defined by the \l Item \l {Item::}{x} coordinate value, so even with - mirroring enabled, it will often be necessary to apply some layout fixes to support the - desired layout direction. Also, it may be necessary to disable the mirroring of individual - child items (by setting \l {enabled}{LayoutMirroring.enabled} to false for such items) if - mirroring is not the desired behavior, or if the child item already implements mirroring in - some custom way. - - See \l {QML Right-to-left User Interfaces} for further details on using \c LayoutMirroring and - other related features to implement right-to-left support for an application. -*/ - -/*! - \qmlproperty bool QtQuick2::LayoutMirroring::enabled - - This property holds whether the item's layout is mirrored horizontally. Setting this to true - horizontally reverses \l {anchor-layout}{anchor} settings such that left anchors become right, - and right anchors become left. For \l{Using QML Positioner and Repeater Items}{positioner} elements - (such as \l Row and \l Grid) and view elements (such as \l {GridView}{GridView} and \l {ListView}{ListView}) - this also mirrors the horizontal layout direction of the item. - - The default value is false. -*/ - -/*! - \qmlproperty bool QtQuick2::LayoutMirroring::childrenInherit - - This property holds whether the \l {enabled}{LayoutMirroring.enabled} value for this item - is inherited by its children. - - The default value is false. -*/ - - -QSGLayoutMirroringAttached::QSGLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0) -{ - if (QSGItem *item = qobject_cast(parent)) { - itemPrivate = QSGItemPrivate::get(item); - itemPrivate->attachedLayoutDirection = this; - } else - qmlInfo(parent) << tr("LayoutDirection attached property only works with Items"); -} - -QSGLayoutMirroringAttached * QSGLayoutMirroringAttached::qmlAttachedProperties(QObject *object) -{ - return new QSGLayoutMirroringAttached(object); -} - -bool QSGLayoutMirroringAttached::enabled() const -{ - return itemPrivate ? itemPrivate->effectiveLayoutMirror : false; -} - -void QSGLayoutMirroringAttached::setEnabled(bool enabled) -{ - if (!itemPrivate) - return; - - itemPrivate->isMirrorImplicit = false; - if (enabled != itemPrivate->effectiveLayoutMirror) { - itemPrivate->setLayoutMirror(enabled); - if (itemPrivate->inheritMirrorFromItem) - itemPrivate->resolveLayoutMirror(); - } -} - -void QSGLayoutMirroringAttached::resetEnabled() -{ - if (itemPrivate && !itemPrivate->isMirrorImplicit) { - itemPrivate->isMirrorImplicit = true; - itemPrivate->resolveLayoutMirror(); - } -} - -bool QSGLayoutMirroringAttached::childrenInherit() const -{ - return itemPrivate ? itemPrivate->inheritMirrorFromItem : false; -} - -void QSGLayoutMirroringAttached::setChildrenInherit(bool childrenInherit) { - if (itemPrivate && childrenInherit != itemPrivate->inheritMirrorFromItem) { - itemPrivate->inheritMirrorFromItem = childrenInherit; - itemPrivate->resolveLayoutMirror(); - childrenInheritChanged(); - } -} - -void QSGItemPrivate::resolveLayoutMirror() -{ - Q_Q(QSGItem); - if (QSGItem *parentItem = q->parentItem()) { - QSGItemPrivate *parentPrivate = QSGItemPrivate::get(parentItem); - setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); - } else { - setImplicitLayoutMirror(isMirrorImplicit ? false : effectiveLayoutMirror, inheritMirrorFromItem); - } -} - -void QSGItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit) -{ - inherit = inherit || inheritMirrorFromItem; - if (!isMirrorImplicit && inheritMirrorFromItem) - mirror = effectiveLayoutMirror; - if (mirror == inheritedLayoutMirror && inherit == inheritMirrorFromParent) - return; - - inheritMirrorFromParent = inherit; - inheritedLayoutMirror = inheritMirrorFromParent ? mirror : false; - - if (isMirrorImplicit) - setLayoutMirror(inherit ? inheritedLayoutMirror : false); - for (int i = 0; i < childItems.count(); ++i) { - if (QSGItem *child = qobject_cast(childItems.at(i))) { - QSGItemPrivate *childPrivate = QSGItemPrivate::get(child); - childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); - } - } -} - -void QSGItemPrivate::setLayoutMirror(bool mirror) -{ - if (mirror != effectiveLayoutMirror) { - effectiveLayoutMirror = mirror; - if (_anchors) { - QSGAnchorsPrivate *anchor_d = QSGAnchorsPrivate::get(_anchors); - anchor_d->fillChanged(); - anchor_d->centerInChanged(); - anchor_d->updateHorizontalAnchors(); - emit _anchors->mirroredChanged(); - } - mirrorChange(); - if (attachedLayoutDirection) { - emit attachedLayoutDirection->enabledChanged(); - } - } -} - -/*! - \class QSGItem - \brief The QSGItem class provides the most basic of all visual items in QML. - - All visual items in Qt Declarative inherit from QSGItem. Although QSGItem - has no visual appearance, it defines all the properties that are - common across visual items - such as the x and y position, the - width and height, \l {anchor-layout}{anchoring} and key handling. - - You can subclass QSGItem to provide your own custom visual item that inherits - these features. Note that, because it does not draw anything, QSGItem sets the - QGraphicsItem::ItemHasNoContents flag. If you subclass QSGItem to create a visual - item, you will need to unset this flag. - -*/ - -/*! - \qmlclass Item QSGItem - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The Item is the most basic of all visual items in QML. - - All visual items in Qt Declarative inherit from Item. Although Item - has no visual appearance, it defines all the properties that are - common across visual items - such as the x and y position, the - width and height, \l {anchor-layout}{anchoring} and key handling. - - Item is also useful for grouping items together. - - \qml - Item { - Image { - source: "tile.png" - } - Image { - x: 80 - width: 100 - height: 100 - source: "tile.png" - } - Image { - x: 190 - width: 100 - height: 100 - fillMode: Image.Tile - source: "tile.png" - } - } - \endqml - - - \section1 Key Handling - - Key handling is available to all Item-based visual elements via the \l {Keys}{Keys} - attached property. The \e Keys attached property provides basic handlers such - as \l {Keys::onPressed}{onPressed} and \l {Keys::onReleased}{onReleased}, - as well as handlers for specific keys, such as - \l {Keys::onCancelPressed}{onCancelPressed}. The example below - assigns \l {qmlfocus}{focus} to the item and handles - the Left key via the general \e onPressed handler and the Select key via the - onSelectPressed handler: - - \qml - Item { - focus: true - Keys.onPressed: { - if (event.key == Qt.Key_Left) { - console.log("move left"); - event.accepted = true; - } - } - Keys.onSelectPressed: console.log("Selected"); - } - \endqml - - See the \l {Keys}{Keys} attached property for detailed documentation. - - \section1 Layout Mirroring - - Item layouts can be mirrored using the \l {LayoutMirroring}{LayoutMirroring} attached property. - -*/ - -/*! - \fn void QSGItem::childrenRectChanged(const QRectF &) - \internal -*/ - -/*! - \fn void QSGItem::baselineOffsetChanged(qreal) - \internal -*/ - -/*! - \fn void QSGItem::stateChanged(const QString &state) - \internal -*/ - -/*! - \fn void QSGItem::parentChanged(QSGItem *) - \internal -*/ - -/*! - \fn void QSGItem::smoothChanged(bool) - \internal -*/ - -/*! - \fn void QSGItem::clipChanged(bool) - \internal -*/ - -/*! \fn void QSGItem::transformOriginChanged(TransformOrigin) - \internal -*/ - -/*! - \fn void QSGItem::focusChanged(bool) - \internal -*/ - -/*! - \fn void QSGItem::activeFocusChanged(bool) - \internal -*/ -/*! - \fn QSGItem::QSGItem(QSGItem *parent) - - Constructs a QSGItem with the given \a parent. -*/ -QSGItem::QSGItem(QSGItem* parent) -: QObject(*(new QSGItemPrivate), parent) -{ - Q_D(QSGItem); - d->init(parent); -} - -/*! \internal -*/ -QSGItem::QSGItem(QSGItemPrivate &dd, QSGItem *parent) -: QObject(dd, parent) -{ - Q_D(QSGItem); - d->init(parent); -} - -#ifndef QT_NO_DEBUG -static int qt_item_count = 0; - -static void qt_print_item_count() -{ - qDebug("Number of leaked items: %i", qt_item_count); - qt_item_count = -1; -} -#endif - -/*! - Destroys the QSGItem. -*/ -QSGItem::~QSGItem() -{ -#ifndef QT_NO_DEBUG - --qt_item_count; - if (qt_item_count < 0) - qDebug("Item destroyed after qt_print_item_count() was called."); -#endif - - Q_D(QSGItem); - - if (d->parentItem) - setParentItem(0); - else if (d->canvas && d->itemNodeInstance) - QSGCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance); // cleanup root - // XXX todo - optimize - while (!d->childItems.isEmpty()) - d->childItems.first()->setParentItem(0); - - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - QSGAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); - if (anchor) - anchor->clearItem(this); - } - - // XXX todo - the original checks if the parent is being destroyed - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - QSGAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); - if (anchor && anchor->item && anchor->item->parent() != this) //child will be deleted anyway - anchor->updateOnComplete(); - } - - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = d->changeListeners.at(ii); - if (change.types & QSGItemPrivate::Destroyed) - change.listener->itemDestroyed(this); - } - d->changeListeners.clear(); - delete d->_anchorLines; d->_anchorLines = 0; - delete d->_anchors; d->_anchors = 0; - delete d->_stateGroup; d->_stateGroup = 0; - delete d->_contents; d->_contents = 0; -} - -/*! - \qmlproperty enumeration QtQuick2::Item::transformOrigin - This property holds the origin point around which scale and rotation transform. - - Nine transform origins are available, as shown in the image below. - - \image declarative-transformorigin.png - - This example rotates an image around its bottom-right corner. - \qml - Image { - source: "myimage.png" - transformOrigin: Item.BottomRight - rotation: 45 - } - \endqml - - The default transform origin is \c Item.Center. - - To set an arbitrary transform origin point use the \l Scale or \l Rotation - transform elements. -*/ - -/*! - \qmlproperty Item QtQuick2::Item::parent - This property holds the parent of the item. -*/ - -/*! - \property QSGItem::parent - This property holds the parent of the item. -*/ -void QSGItem::setParentItem(QSGItem *parentItem) -{ - Q_D(QSGItem); - if (parentItem == d->parentItem) - return; - - d->removeFromDirtyList(); - - QSGItem *oldParentItem = d->parentItem; - QSGItem *scopeFocusedItem = 0; - - if (oldParentItem) { - QSGItemPrivate *op = QSGItemPrivate::get(oldParentItem); - - QSGItem *scopeItem = 0; - - if (d->canvas && hasFocus()) { - scopeItem = oldParentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); - scopeFocusedItem = this; - } else if (d->canvas && !isFocusScope() && d->subFocusItem) { - scopeItem = oldParentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); - scopeFocusedItem = d->subFocusItem; - } - - if (scopeFocusedItem) - QSGCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem, - QSGCanvasPrivate::DontChangeFocusProperty); - - op->removeChild(this); - } - - d->parentItem = parentItem; - - QSGCanvas *parentCanvas = parentItem?QSGItemPrivate::get(parentItem)->canvas:0; - if (d->canvas != parentCanvas) { - QSGItemPrivate::InitializationState initState; - initState.clear(); - d->initCanvas(&initState, parentCanvas); - } - - d->dirty(QSGItemPrivate::ParentChanged); - - if (d->parentItem) - QSGItemPrivate::get(d->parentItem)->addChild(this); - - d->setEffectiveVisibleRecur(d->calcEffectiveVisible()); - d->setEffectiveEnableRecur(d->calcEffectiveEnable()); - - if (scopeFocusedItem && d->parentItem && d->canvas) { - // We need to test whether this item becomes scope focused - QSGItem *scopeItem = 0; - scopeItem = d->parentItem; - while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem(); - - if (scopeItem->scopedFocusItem()) { - QSGItemPrivate::get(scopeFocusedItem)->focus = false; - emit scopeFocusedItem->focusChanged(false); - } else { - QSGCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem, - QSGCanvasPrivate::DontChangeFocusProperty); - } - } - - d->resolveLayoutMirror(); - - d->itemChange(ItemParentHasChanged, d->parentItem); - - emit parentChanged(d->parentItem); -} - -void QSGItem::stackBefore(const QSGItem *sibling) -{ - Q_D(QSGItem); - if (!sibling || sibling == this || !d->parentItem || d->parentItem != QSGItemPrivate::get(sibling)->parentItem) { - qWarning("QSGItem::stackBefore: Cannot stack before %p, which must be a sibling", sibling); - return; - } - - QSGItemPrivate *parentPrivate = QSGItemPrivate::get(d->parentItem); - - int myIndex = parentPrivate->childItems.indexOf(this); - int siblingIndex = parentPrivate->childItems.indexOf(const_cast(sibling)); - - Q_ASSERT(myIndex != -1 && siblingIndex != -1); - - if (myIndex == siblingIndex - 1) - return; - - parentPrivate->childItems.removeAt(myIndex); - - if (myIndex < siblingIndex) --siblingIndex; - - parentPrivate->childItems.insert(siblingIndex, this); - - parentPrivate->dirty(QSGItemPrivate::ChildrenStackingChanged); - - for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.count(); ++ii) - QSGItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged(); -} - -void QSGItem::stackAfter(const QSGItem *sibling) -{ - Q_D(QSGItem); - if (!sibling || sibling == this || !d->parentItem || d->parentItem != QSGItemPrivate::get(sibling)->parentItem) { - qWarning("QSGItem::stackAfter: Cannot stack after %p, which must be a sibling", sibling); - return; - } - - QSGItemPrivate *parentPrivate = QSGItemPrivate::get(d->parentItem); - - int myIndex = parentPrivate->childItems.indexOf(this); - int siblingIndex = parentPrivate->childItems.indexOf(const_cast(sibling)); - - Q_ASSERT(myIndex != -1 && siblingIndex != -1); - - if (myIndex == siblingIndex + 1) - return; - - parentPrivate->childItems.removeAt(myIndex); - - if (myIndex < siblingIndex) --siblingIndex; - - parentPrivate->childItems.insert(siblingIndex + 1, this); - - parentPrivate->dirty(QSGItemPrivate::ChildrenStackingChanged); - - for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.count(); ++ii) - QSGItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged(); -} - -/*! - Returns the QSGItem parent of this item. -*/ -QSGItem *QSGItem::parentItem() const -{ - Q_D(const QSGItem); - return d->parentItem; -} - -QSGEngine *QSGItem::sceneGraphEngine() const -{ - return canvas()->sceneGraphEngine(); -} - -QSGCanvas *QSGItem::canvas() const -{ - Q_D(const QSGItem); - return d->canvas; -} - -static bool itemZOrder_sort(QSGItem *lhs, QSGItem *rhs) -{ - return lhs->z() < rhs->z(); -} - -QList QSGItemPrivate::paintOrderChildItems() const -{ - // XXX todo - optimize, don't sort and return items that are - // ignored anyway, like invisible or disabled items. - QList items = childItems; - qStableSort(items.begin(), items.end(), itemZOrder_sort); - return items; -} - -void QSGItemPrivate::addChild(QSGItem *child) -{ - Q_Q(QSGItem); - - Q_ASSERT(!childItems.contains(child)); - - childItems.append(child); - - dirty(QSGItemPrivate::ChildrenChanged); - - itemChange(QSGItem::ItemChildAddedChange, child); - - emit q->childrenChanged(); -} - -void QSGItemPrivate::removeChild(QSGItem *child) -{ - Q_Q(QSGItem); - - Q_ASSERT(child); - Q_ASSERT(childItems.contains(child)); - childItems.removeOne(child); - Q_ASSERT(!childItems.contains(child)); - - dirty(QSGItemPrivate::ChildrenChanged); - - itemChange(QSGItem::ItemChildRemovedChange, child); - - emit q->childrenChanged(); -} - -void QSGItemPrivate::InitializationState::clear() -{ - focusScope = 0; -} - -void QSGItemPrivate::InitializationState::clear(QSGItem *fs) -{ - focusScope = fs; -} - -QSGItem *QSGItemPrivate::InitializationState::getFocusScope(QSGItem *item) -{ - if (!focusScope) { - QSGItem *fs = item->parentItem(); - while (!fs->isFocusScope()) - fs = fs->parentItem(); - focusScope = fs; - } - return focusScope; -} - -void QSGItemPrivate::initCanvas(InitializationState *state, QSGCanvas *c) -{ - Q_Q(QSGItem); - - if (canvas) { - removeFromDirtyList(); - QSGCanvasPrivate *c = QSGCanvasPrivate::get(canvas); - if (polishScheduled) - c->itemsToPolish.remove(q); - if (c->mouseGrabberItem == q) - c->mouseGrabberItem = 0; - if ( hoverEnabled ) - c->hoverItems.removeAll(q); - if (itemNodeInstance) - c->cleanup(itemNodeInstance); - } - - canvas = c; - - if (canvas && polishScheduled) - QSGCanvasPrivate::get(canvas)->itemsToPolish.insert(q); - - itemNodeInstance = 0; - opacityNode = 0; - clipNode = 0; - rootNode = 0; - groupNode = 0; - paintNode = 0; - beforePaintNode = 0; - - InitializationState _dummy; - InitializationState *childState = state; - - if (c && q->isFocusScope()) { - _dummy.clear(q); - childState = &_dummy; - } - - for (int ii = 0; ii < childItems.count(); ++ii) { - QSGItem *child = childItems.at(ii); - QSGItemPrivate::get(child)->initCanvas(childState, c); - } - - if (c && focus) { - // Fixup - if (state->getFocusScope(q)->scopedFocusItem()) { - focus = false; - emit q->focusChanged(false); - } else { - QSGCanvasPrivate::get(canvas)->setFocusInScope(state->getFocusScope(q), q); - } - } - - dirty(Canvas); - - itemChange(QSGItem::ItemSceneChange, c); -} - -/*! -Returns a transform that maps points from canvas space into item space. -*/ -QTransform QSGItemPrivate::canvasToItemTransform() const -{ - // XXX todo - optimize - return itemToCanvasTransform().inverted(); -} - -/*! -Returns a transform that maps points from item space into canvas space. -*/ -QTransform QSGItemPrivate::itemToCanvasTransform() const -{ - // XXX todo - QTransform rv = parentItem?QSGItemPrivate::get(parentItem)->itemToCanvasTransform():QTransform(); - itemToParentTransform(rv); - return rv; -} - -/*! -Motifies \a t with this items local transform relative to its parent. -*/ -void QSGItemPrivate::itemToParentTransform(QTransform &t) const -{ - if (x || y) - t.translate(x, y); - - if (!transforms.isEmpty()) { - QMatrix4x4 m(t); - for (int ii = transforms.count() - 1; ii >= 0; --ii) - transforms.at(ii)->applyTo(&m); - t = m.toTransform(); - } - - if (scale != 1. || rotation != 0.) { - QPointF tp = computeTransformOrigin(); - t.translate(tp.x(), tp.y()); - t.scale(scale, scale); - t.rotate(rotation); - t.translate(-tp.x(), -tp.y()); - } -} - - -/*! - \qmlproperty real QtQuick2::Item::childrenRect.x - \qmlproperty real QtQuick2::Item::childrenRect.y - \qmlproperty real QtQuick2::Item::childrenRect.width - \qmlproperty real QtQuick2::Item::childrenRect.height - - The childrenRect properties allow an item access to the geometry of its - children. This property is useful if you have an item that needs to be - sized to fit its children. -*/ - - -/*! - \qmlproperty list QtQuick2::Item::children - \qmlproperty list QtQuick2::Item::resources - - The children property contains the list of visual children of this item. - The resources property contains non-visual resources that you want to - reference by name. - - Generally you can rely on Item's default property to handle all this for - you, but it can come in handy in some cases. - - \qml - Item { - children: [ - Text {}, - Rectangle {} - ] - resources: [ - Component { - id: myComponent - Text {} - } - ] - } - \endqml -*/ - -/*! - Returns true if construction of the QML component is complete; otherwise - returns false. - - It is often desirable to delay some processing until the component is - completed. - - \sa componentComplete() -*/ -bool QSGItem::isComponentComplete() const -{ - Q_D(const QSGItem); - return d->componentComplete; -} - -QSGItemPrivate::QSGItemPrivate() -: _anchors(0), _contents(0), baselineOffset(0), _anchorLines(0), _stateGroup(0), origin(QSGItem::Center), - - flags(0), widthValid(false), heightValid(false), componentComplete(true), - keepMouse(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false), - notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true), - effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false), - inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), - inheritMirrorFromParent(false), inheritMirrorFromItem(false), childrenDoNotOverlap(false), - - canvas(0), parentItem(0), - - subFocusItem(0), - - x(0), y(0), width(0), height(0), implicitWidth(0), implicitHeight(0), - z(0), scale(1), rotation(0), opacity(1), - - attachedLayoutDirection(0), acceptedMouseButtons(0), - imHints(Qt::ImhMultiLine), - - keyHandler(0), - - dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0), - - itemNodeInstance(0), opacityNode(0), clipNode(0), rootNode(0), groupNode(0), paintNode(0) - , beforePaintNode(0), effectRefCount(0), hideRefCount(0) -{ -} - -void QSGItemPrivate::init(QSGItem *parent) -{ -#ifndef QT_NO_DEBUG - ++qt_item_count; - static bool atexit_registered = false; - if (!atexit_registered) { - atexit(qt_print_item_count); - atexit_registered = true; - } -#endif - - Q_Q(QSGItem); - baselineOffset.invalidate(); - - if (parent) { - q->setParentItem(parent); - QSGItemPrivate *parentPrivate = QSGItemPrivate::get(parent); - setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); - } -} - -void QSGItemPrivate::data_append(QDeclarativeListProperty *prop, QObject *o) -{ - if (!o) - return; - - QSGItem *that = static_cast(prop->object); - - // This test is measurably (albeit only slightly) faster than qobject_cast<>() - const QMetaObject *mo = o->metaObject(); - while (mo && mo != &QSGItem::staticMetaObject) { - mo = mo->d.superdata; - } - - if (mo) { - QSGItem *item = static_cast(o); - item->setParentItem(that); - } else { - if (o->inherits("QGraphicsItem")) - qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className()); - - // XXX todo - do we really want this behavior? - o->setParent(that); - } -} - -/*! - \qmlproperty list QtQuick2::Item::data - \default - - The data property allows you to freely mix visual children and resources - in an item. If you assign a visual item to the data list it becomes - a child and if you assign any other object type, it is added as a resource. - - So you can write: - \qml - Item { - Text {} - Rectangle {} - Timer {} - } - \endqml - - instead of: - \qml - Item { - children: [ - Text {}, - Rectangle {} - ] - resources: [ - Timer {} - ] - } - \endqml - - data is a behind-the-scenes property: you should never need to explicitly - specify it. - */ - -int QSGItemPrivate::data_count(QDeclarativeListProperty *prop) -{ - Q_UNUSED(prop); - // XXX todo - return 0; -} - -QObject *QSGItemPrivate::data_at(QDeclarativeListProperty *prop, int i) -{ - Q_UNUSED(prop); - Q_UNUSED(i); - // XXX todo - return 0; -} - -void QSGItemPrivate::data_clear(QDeclarativeListProperty *prop) -{ - Q_UNUSED(prop); - // XXX todo -} - -QObject *QSGItemPrivate::resources_at(QDeclarativeListProperty *prop, int index) -{ - const QObjectList children = prop->object->children(); - if (index < children.count()) - return children.at(index); - else - return 0; -} - -void QSGItemPrivate::resources_append(QDeclarativeListProperty *prop, QObject *o) -{ - // XXX todo - do we really want this behavior? - o->setParent(prop->object); -} - -int QSGItemPrivate::resources_count(QDeclarativeListProperty *prop) -{ - return prop->object->children().count(); -} - -void QSGItemPrivate::resources_clear(QDeclarativeListProperty *prop) -{ - // XXX todo - do we really want this behavior? - const QObjectList children = prop->object->children(); - for (int index = 0; index < children.count(); index++) - children.at(index)->setParent(0); -} - -QSGItem *QSGItemPrivate::children_at(QDeclarativeListProperty *prop, int index) -{ - QSGItemPrivate *p = QSGItemPrivate::get(static_cast(prop->object)); - if (index >= p->childItems.count() || index < 0) - return 0; - else - return p->childItems.at(index); -} - -void QSGItemPrivate::children_append(QDeclarativeListProperty *prop, QSGItem *o) -{ - if (!o) - return; - - QSGItem *that = static_cast(prop->object); - if (o->parentItem() == that) - o->setParentItem(0); - - o->setParentItem(that); -} - -int QSGItemPrivate::children_count(QDeclarativeListProperty *prop) -{ - QSGItemPrivate *p = QSGItemPrivate::get(static_cast(prop->object)); - return p->childItems.count(); -} - -void QSGItemPrivate::children_clear(QDeclarativeListProperty *prop) -{ - QSGItem *that = static_cast(prop->object); - QSGItemPrivate *p = QSGItemPrivate::get(that); - while (!p->childItems.isEmpty()) - p->childItems.at(0)->setParentItem(0); -} - -int QSGItemPrivate::transform_count(QDeclarativeListProperty *prop) -{ - QSGItem *that = static_cast(prop->object); - return QSGItemPrivate::get(that)->transforms.count(); -} - -void QSGTransform::appendToItem(QSGItem *item) -{ - Q_D(QSGTransform); - if (!item) - return; - - QSGItemPrivate *p = QSGItemPrivate::get(item); - - if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) { - p->transforms.removeOne(this); - p->transforms.append(this); - } else { - p->transforms.append(this); - d->items.append(item); - } - - p->dirty(QSGItemPrivate::Transform); -} - -void QSGTransform::prependToItem(QSGItem *item) -{ - Q_D(QSGTransform); - if (!item) - return; - - QSGItemPrivate *p = QSGItemPrivate::get(item); - - if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) { - p->transforms.removeOne(this); - p->transforms.prepend(this); - } else { - p->transforms.prepend(this); - d->items.append(item); - } - - p->dirty(QSGItemPrivate::Transform); -} - -void QSGItemPrivate::transform_append(QDeclarativeListProperty *prop, QSGTransform *transform) -{ - if (!transform) - return; - - QSGItem *that = static_cast(prop->object); - transform->appendToItem(that); -} - -QSGTransform *QSGItemPrivate::transform_at(QDeclarativeListProperty *prop, int idx) -{ - QSGItem *that = static_cast(prop->object); - QSGItemPrivate *p = QSGItemPrivate::get(that); - - if (idx < 0 || idx >= p->transforms.count()) - return 0; - else - return p->transforms.at(idx); -} - -void QSGItemPrivate::transform_clear(QDeclarativeListProperty *prop) -{ - QSGItem *that = static_cast(prop->object); - QSGItemPrivate *p = QSGItemPrivate::get(that); - - for (int ii = 0; ii < p->transforms.count(); ++ii) { - QSGTransform *t = p->transforms.at(ii); - QSGTransformPrivate *tp = QSGTransformPrivate::get(t); - tp->items.removeOne(that); - } - - p->transforms.clear(); - - p->dirty(QSGItemPrivate::Transform); -} - -/*! - \property QSGItem::childrenRect - \brief The geometry of an item's children. - - This property holds the (collective) position and size of the item's children. -*/ - -/*! - \qmlproperty real QtQuick2::Item::x - \qmlproperty real QtQuick2::Item::y - \qmlproperty real QtQuick2::Item::width - \qmlproperty real QtQuick2::Item::height - - Defines the item's position and size relative to its parent. - - \qml - Item { x: 100; y: 100; width: 100; height: 100 } - \endqml - */ - -/*! - \qmlproperty real QtQuick2::Item::z - - Sets the stacking order of sibling items. By default the stacking order is 0. - - Items with a higher stacking value are drawn on top of siblings with a - lower stacking order. Items with the same stacking value are drawn - bottom up in the order they appear. Items with a negative stacking - value are drawn under their parent's content. - - The following example shows the various effects of stacking order. - - \table - \row - \o \image declarative-item_stacking1.png - \o Same \c z - later children above earlier children: - \qml - Item { - Rectangle { - color: "red" - width: 100; height: 100 - } - Rectangle { - color: "blue" - x: 50; y: 50; width: 100; height: 100 - } - } - \endqml - \row - \o \image declarative-item_stacking2.png - \o Higher \c z on top: - \qml - Item { - Rectangle { - z: 1 - color: "red" - width: 100; height: 100 - } - Rectangle { - color: "blue" - x: 50; y: 50; width: 100; height: 100 - } - } - \endqml - \row - \o \image declarative-item_stacking3.png - \o Same \c z - children above parents: - \qml - Item { - Rectangle { - color: "red" - width: 100; height: 100 - Rectangle { - color: "blue" - x: 50; y: 50; width: 100; height: 100 - } - } - } - \endqml - \row - \o \image declarative-item_stacking4.png - \o Lower \c z below: - \qml - Item { - Rectangle { - color: "red" - width: 100; height: 100 - Rectangle { - z: -1 - color: "blue" - x: 50; y: 50; width: 100; height: 100 - } - } - } - \endqml - \endtable - */ - -/*! - \qmlproperty bool QtQuick2::Item::visible - - This property holds whether the item is visible. By default this is true. - - Setting this property directly affects the \c visible value of child - items. When set to \c false, the \c visible values of all child items also - become \c false. When set to \c true, the \c visible values of child items - are returned to \c true, unless they have explicitly been set to \c false. - - (Because of this flow-on behavior, using the \c visible property may not - have the intended effect if a property binding should only respond to - explicit property changes. In such cases it may be better to use the - \l opacity property instead.) - - Setting this property to \c false automatically causes \l focus to be set - to \c false, and this item will longer receive mouse and keyboard events. - (In contrast, setting the \l opacity to 0 does not affect the \l focus - property and the receiving of key events.) - - \note This property's value is only affected by changes to this property or - the parent's \c visible property. It does not change, for example, if this - item moves off-screen, or if the \l opacity changes to 0. -*/ - - -/*! - \qmlproperty AnchorLine QtQuick2::Item::anchors.top - \qmlproperty AnchorLine QtQuick2::Item::anchors.bottom - \qmlproperty AnchorLine QtQuick2::Item::anchors.left - \qmlproperty AnchorLine QtQuick2::Item::anchors.right - \qmlproperty AnchorLine QtQuick2::Item::anchors.horizontalCenter - \qmlproperty AnchorLine QtQuick2::Item::anchors.verticalCenter - \qmlproperty AnchorLine QtQuick2::Item::anchors.baseline - - \qmlproperty Item QtQuick2::Item::anchors.fill - \qmlproperty Item QtQuick2::Item::anchors.centerIn - - \qmlproperty real QtQuick2::Item::anchors.margins - \qmlproperty real QtQuick2::Item::anchors.topMargin - \qmlproperty real QtQuick2::Item::anchors.bottomMargin - \qmlproperty real QtQuick2::Item::anchors.leftMargin - \qmlproperty real QtQuick2::Item::anchors.rightMargin - \qmlproperty real QtQuick2::Item::anchors.horizontalCenterOffset - \qmlproperty real QtQuick2::Item::anchors.verticalCenterOffset - \qmlproperty real QtQuick2::Item::anchors.baselineOffset - - \qmlproperty bool QtQuick2::Item::anchors.mirrored - - Anchors provide a way to position an item by specifying its - relationship with other items. - - Margins apply to top, bottom, left, right, and fill anchors. - The \c anchors.margins property can be used to set all of the various margins at once, to the same value. - Note that margins are anchor-specific and are not applied if an item does not - use anchors. - - Offsets apply for horizontal center, vertical center, and baseline anchors. - - \table - \row - \o \image declarative-anchors_example.png - \o Text anchored to Image, horizontally centered and vertically below, with a margin. - \qml - Item { - Image { - id: pic - // ... - } - Text { - id: label - anchors.horizontalCenter: pic.horizontalCenter - anchors.top: pic.bottom - anchors.topMargin: 5 - // ... - } - } - \endqml - \row - \o \image declarative-anchors_example2.png - \o - Left of Text anchored to right of Image, with a margin. The y - property of both defaults to 0. - - \qml - Item { - Image { - id: pic - // ... - } - Text { - id: label - anchors.left: pic.right - anchors.leftMargin: 5 - // ... - } - } - \endqml - \endtable - - \c anchors.fill provides a convenient way for one item to have the - same geometry as another item, and is equivalent to connecting all - four directional anchors. - - To clear an anchor value, set it to \c undefined. - - \c anchors.mirrored returns true it the layout has been \l {LayoutMirroring}{mirrored}. - - \note You can only anchor an item to siblings or a parent. - - For more information see \l {anchor-layout}{Anchor Layouts}. -*/ - -/*! - \property QSGItem::baselineOffset - \brief The position of the item's baseline in local coordinates. - - The baseline of a \l Text item is the imaginary line on which the text - sits. Controls containing text usually set their baseline to the - baseline of their text. - - For non-text items, a default baseline offset of 0 is used. -*/ -QSGAnchors *QSGItemPrivate::anchors() const -{ - if (!_anchors) { - Q_Q(const QSGItem); - _anchors = new QSGAnchors(const_cast(q)); - if (!componentComplete) - _anchors->classBegin(); - } - return _anchors; -} - -QSGItemPrivate::AnchorLines *QSGItemPrivate::anchorLines() const -{ - Q_Q(const QSGItem); - if (!_anchorLines) _anchorLines = - new AnchorLines(const_cast(q)); - return _anchorLines; -} - -void QSGItemPrivate::siblingOrderChanged() -{ - Q_Q(QSGItem); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::SiblingOrder) { - change.listener->itemSiblingOrderChanged(q); - } - } -} - -QDeclarativeListProperty QSGItemPrivate::data() -{ - return QDeclarativeListProperty(q_func(), 0, QSGItemPrivate::data_append, - QSGItemPrivate::data_count, - QSGItemPrivate::data_at, - QSGItemPrivate::data_clear); -} - -QRectF QSGItem::childrenRect() -{ - Q_D(QSGItem); - if (!d->_contents) { - d->_contents = new QSGContents(this); - if (d->componentComplete) - d->_contents->complete(); - } - return d->_contents->rectF(); -} - -QList QSGItem::childItems() const -{ - Q_D(const QSGItem); - return d->childItems; -} - -bool QSGItem::clip() const -{ - return flags() & ItemClipsChildrenToShape; -} - -void QSGItem::setClip(bool c) -{ - if (clip() == c) - return; - - setFlag(ItemClipsChildrenToShape, c); - - emit clipChanged(c); -} - - -/*! - This function is called to handle this item's changes in - geometry from \a oldGeometry to \a newGeometry. If the two - geometries are the same, it doesn't do anything. - */ -void QSGItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_D(QSGItem); - - if (d->_anchors) - QSGAnchorsPrivate::get(d->_anchors)->updateMe(); - - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = d->changeListeners.at(ii); - if (change.types & QSGItemPrivate::Geometry) - change.listener->itemGeometryChanged(this, newGeometry, oldGeometry); - } - - if (newGeometry.x() != oldGeometry.x()) - emit xChanged(); - if (newGeometry.y() != oldGeometry.y()) - emit yChanged(); - if (newGeometry.width() != oldGeometry.width()) - emit widthChanged(); - if (newGeometry.height() != oldGeometry.height()) - emit heightChanged(); -} - -/*! - Called by the rendering thread when it is time to sync the state of the QML objects with the - scene graph objects. The function should return the root of the scene graph subtree for - this item. \a oldNode is the node that was returned the last time the function was called. - - The main thread is blocked while this function is executed so it is safe to read - values from the QSGItem instance and other objects in the main thread. - - \warning This is the only function in which it is allowed to make use of scene graph - objects from the main thread. Use of scene graph objects outside this function will - result in race conditions and potential crashes. - */ - -QSGNode *QSGItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - delete oldNode; - return 0; -} - -QSGTransformNode *QSGItemPrivate::createTransformNode() -{ - return new QSGTransformNode; -} - -void QSGItem::updatePolish() -{ -} - -void QSGItemPrivate::removeItemChangeListener(QSGItemChangeListener *listener, ChangeTypes types) -{ - ChangeListener change(listener, types); - changeListeners.removeOne(change); -} - -void QSGItem::keyPressEvent(QKeyEvent *event) -{ - event->ignore(); -} - -void QSGItem::keyReleaseEvent(QKeyEvent *event) -{ - event->ignore(); -} - -void QSGItem::inputMethodEvent(QInputMethodEvent *event) -{ - event->ignore(); -} - -void QSGItem::focusInEvent(QFocusEvent *) -{ -} - -void QSGItem::focusOutEvent(QFocusEvent *) -{ -} - -void QSGItem::mousePressEvent(QMouseEvent *event) -{ - event->ignore(); -} - -void QSGItem::mouseMoveEvent(QMouseEvent *event) -{ - event->ignore(); -} - -void QSGItem::mouseReleaseEvent(QMouseEvent *event) -{ - event->ignore(); -} - -void QSGItem::mouseDoubleClickEvent(QMouseEvent *event) -{ - mousePressEvent(event); -} - -void QSGItem::mouseUngrabEvent() -{ - // XXX todo -} - -void QSGItem::wheelEvent(QWheelEvent *event) -{ - event->ignore(); -} - -void QSGItem::touchEvent(QTouchEvent *event) -{ - event->ignore(); -} - -void QSGItem::hoverEnterEvent(QHoverEvent *event) -{ - Q_UNUSED(event); -} - -void QSGItem::hoverMoveEvent(QHoverEvent *event) -{ - Q_UNUSED(event); -} - -void QSGItem::hoverLeaveEvent(QHoverEvent *event) -{ - Q_UNUSED(event); -} - -void QSGItem::dragEnterEvent(QDragEnterEvent *event) -{ - Q_UNUSED(event); -} - -void QSGItem::dragMoveEvent(QDragMoveEvent *event) -{ - - Q_UNUSED(event); -} - -void QSGItem::dragLeaveEvent(QDragLeaveEvent *event) -{ - - Q_UNUSED(event); -} - -void QSGItem::dropEvent(QDropEvent *event) -{ - Q_UNUSED(event); -} - -bool QSGItem::childMouseEventFilter(QSGItem *, QEvent *) -{ - return false; -} - -void QSGItem::windowDeactivateEvent() -{ - foreach (QSGItem* item, childItems()) { - item->windowDeactivateEvent(); - } -} - -Qt::InputMethodHints QSGItem::inputMethodHints() const -{ - Q_D(const QSGItem); - return d->imHints; -} - -void QSGItem::setInputMethodHints(Qt::InputMethodHints hints) -{ - Q_D(QSGItem); - d->imHints = hints; - - if (!d->canvas || d->canvas->activeFocusItem() != this) - return; - - QInputPanel *p = qApp->inputPanel(); - if (p->inputItem() == this) - qApp->inputPanel()->update(Qt::ImHints); -} - -void QSGItem::updateMicroFocus() -{ - QInputPanel *p = qApp->inputPanel(); - if (p->inputItem() == this) - qApp->inputPanel()->update(Qt::ImQueryInput); -} - -QVariant QSGItem::inputMethodQuery(Qt::InputMethodQuery query) const -{ - Q_D(const QSGItem); - QVariant v; - - switch (query) { - case Qt::ImEnabled: - v = (bool)(flags() & ItemAcceptsInputMethod); - break; - case Qt::ImHints: - v = (int)inputMethodHints(); - break; - case Qt::ImCursorRectangle: - case Qt::ImFont: - case Qt::ImCursorPosition: - case Qt::ImSurroundingText: - case Qt::ImCurrentSelection: - case Qt::ImMaximumTextLength: - case Qt::ImAnchorPosition: - case Qt::ImPreferredLanguage: - if (d->keyHandler) - v = d->keyHandler->inputMethodQuery(query); - default: - break; - } - - return v; -} - -QSGAnchorLine QSGItemPrivate::left() const -{ - return anchorLines()->left; -} - -QSGAnchorLine QSGItemPrivate::right() const -{ - return anchorLines()->right; -} - -QSGAnchorLine QSGItemPrivate::horizontalCenter() const -{ - return anchorLines()->hCenter; -} - -QSGAnchorLine QSGItemPrivate::top() const -{ - return anchorLines()->top; -} - -QSGAnchorLine QSGItemPrivate::bottom() const -{ - return anchorLines()->bottom; -} - -QSGAnchorLine QSGItemPrivate::verticalCenter() const -{ - return anchorLines()->vCenter; -} - -QSGAnchorLine QSGItemPrivate::baseline() const -{ - return anchorLines()->baseline; -} - -qreal QSGItem::baselineOffset() const -{ - Q_D(const QSGItem); - if (!d->baselineOffset.isValid()) { - return 0.0; - } else - return d->baselineOffset; -} - -void QSGItem::setBaselineOffset(qreal offset) -{ - Q_D(QSGItem); - if (offset == d->baselineOffset) - return; - - d->baselineOffset = offset; - - for (int ii = 0; ii < d->changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = d->changeListeners.at(ii); - if (change.types & QSGItemPrivate::Geometry) { - QSGAnchorsPrivate *anchor = change.listener->anchorPrivate(); - if (anchor) - anchor->updateVerticalAnchors(); - } - } - emit baselineOffsetChanged(offset); -} - -void QSGItem::update() -{ - Q_D(QSGItem); - Q_ASSERT(flags() & ItemHasContents); - d->dirty(QSGItemPrivate::Content); -} - -void QSGItem::polish() -{ - Q_D(QSGItem); - if (!d->polishScheduled) { - d->polishScheduled = true; - if (d->canvas) { - QSGCanvasPrivate *p = QSGCanvasPrivate::get(d->canvas); - bool maybeupdate = p->itemsToPolish.isEmpty(); - p->itemsToPolish.insert(this); - if (maybeupdate) d->canvas->maybeUpdate(); - } - } -} - -void QSGItem::mapFromItem(QDeclarativeV8Function *args) const -{ - if (args->Length() != 0) { - v8::Local item = (*args)[0]; - QV8Engine *engine = args->engine(); - - QSGItem *itemObj = 0; - if (!item->IsNull()) - itemObj = qobject_cast(engine->toQObject(item)); - - if (!itemObj && !item->IsNull()) { - qmlInfo(this) << "mapFromItem() given argument \"" << engine->toString(item->ToString()) - << "\" which is neither null nor an Item"; - return; - } - - v8::Local rv = v8::Object::New(); - args->returnValue(rv); - - qreal x = (args->Length() > 1)?(*args)[1]->NumberValue():0; - qreal y = (args->Length() > 2)?(*args)[2]->NumberValue():0; - - QPointF p = mapFromItem(itemObj, QPointF(x, y)); - - rv->Set(v8::String::New("x"), v8::Number::New(p.x())); - rv->Set(v8::String::New("y"), v8::Number::New(p.y())); - } -} - -QTransform QSGItem::itemTransform(QSGItem *other, bool *ok) const -{ - Q_D(const QSGItem); - - // XXX todo - we need to be able to handle common parents better and detect - // invalid cases - if (ok) *ok = true; - - QTransform t = d->itemToCanvasTransform(); - if (other) t *= QSGItemPrivate::get(other)->canvasToItemTransform(); - - return t; -} - -void QSGItem::mapToItem(QDeclarativeV8Function *args) const -{ - if (args->Length() != 0) { - v8::Local item = (*args)[0]; - QV8Engine *engine = args->engine(); - - QSGItem *itemObj = 0; - if (!item->IsNull()) - itemObj = qobject_cast(engine->toQObject(item)); - - if (!itemObj && !item->IsNull()) { - qmlInfo(this) << "mapToItem() given argument \"" << engine->toString(item->ToString()) - << "\" which is neither null nor an Item"; - return; - } - - v8::Local rv = v8::Object::New(); - args->returnValue(rv); - - qreal x = (args->Length() > 1)?(*args)[1]->NumberValue():0; - qreal y = (args->Length() > 2)?(*args)[2]->NumberValue():0; - - QPointF p = mapToItem(itemObj, QPointF(x, y)); - - rv->Set(v8::String::New("x"), v8::Number::New(p.x())); - rv->Set(v8::String::New("y"), v8::Number::New(p.y())); - } -} - -void QSGItem::forceActiveFocus() -{ - setFocus(true); - QSGItem *parent = parentItem(); - while (parent) { - if (parent->flags() & QSGItem::ItemIsFocusScope) { - parent->setFocus(true); - } - parent = parent->parentItem(); - } -} - -QSGItem *QSGItem::childAt(qreal x, qreal y) const -{ - // XXX todo - should this include transform etc.? - const QList children = childItems(); - for (int i = children.count()-1; i >= 0; --i) { - QSGItem *child = children.at(i); - if (child->isVisible() && child->x() <= x - && child->x() + child->width() >= x - && child->y() <= y - && child->y() + child->height() >= y) - return child; - } - return 0; -} - -QDeclarativeListProperty QSGItemPrivate::resources() -{ - return QDeclarativeListProperty(q_func(), 0, QSGItemPrivate::resources_append, - QSGItemPrivate::resources_count, - QSGItemPrivate::resources_at, - QSGItemPrivate::resources_clear); -} - -QDeclarativeListProperty QSGItemPrivate::children() -{ - return QDeclarativeListProperty(q_func(), 0, QSGItemPrivate::children_append, - QSGItemPrivate::children_count, - QSGItemPrivate::children_at, - QSGItemPrivate::children_clear); - -} - -QDeclarativeListProperty QSGItemPrivate::states() -{ - return _states()->statesProperty(); -} - -QDeclarativeListProperty QSGItemPrivate::transitions() -{ - return _states()->transitionsProperty(); -} - -QString QSGItemPrivate::state() const -{ - if (!_stateGroup) - return QString(); - else - return _stateGroup->state(); -} - -void QSGItemPrivate::setState(const QString &state) -{ - _states()->setState(state); -} - -QString QSGItem::state() const -{ - Q_D(const QSGItem); - return d->state(); -} - -void QSGItem::setState(const QString &state) -{ - Q_D(QSGItem); - d->setState(state); -} - -QDeclarativeListProperty QSGItem::transform() -{ - Q_D(QSGItem); - return QDeclarativeListProperty(this, 0, d->transform_append, d->transform_count, - d->transform_at, d->transform_clear); -} - -void QSGItem::classBegin() -{ - Q_D(QSGItem); - d->componentComplete = false; - if (d->_stateGroup) - d->_stateGroup->classBegin(); - if (d->_anchors) - d->_anchors->classBegin(); -} - -void QSGItem::componentComplete() -{ - Q_D(QSGItem); - d->componentComplete = true; - if (d->_stateGroup) - d->_stateGroup->componentComplete(); - if (d->_anchors) { - d->_anchors->componentComplete(); - QSGAnchorsPrivate::get(d->_anchors)->updateOnComplete(); - } - if (d->keyHandler) - d->keyHandler->componentComplete(); - if (d->_contents) - d->_contents->complete(); -} - -QDeclarativeStateGroup *QSGItemPrivate::_states() -{ - Q_Q(QSGItem); - if (!_stateGroup) { - _stateGroup = new QDeclarativeStateGroup; - if (!componentComplete) - _stateGroup->classBegin(); - FAST_CONNECT(_stateGroup, SIGNAL(stateChanged(QString)), - q, SIGNAL(stateChanged(QString))) - } - - return _stateGroup; -} - -QSGItemPrivate::AnchorLines::AnchorLines(QSGItem *q) -{ - left.item = q; - left.anchorLine = QSGAnchorLine::Left; - right.item = q; - right.anchorLine = QSGAnchorLine::Right; - hCenter.item = q; - hCenter.anchorLine = QSGAnchorLine::HCenter; - top.item = q; - top.anchorLine = QSGAnchorLine::Top; - bottom.item = q; - bottom.anchorLine = QSGAnchorLine::Bottom; - vCenter.item = q; - vCenter.anchorLine = QSGAnchorLine::VCenter; - baseline.item = q; - baseline.anchorLine = QSGAnchorLine::Baseline; -} - -QPointF QSGItemPrivate::computeTransformOrigin() const -{ - switch (origin) { - default: - case QSGItem::TopLeft: - return QPointF(0, 0); - case QSGItem::Top: - return QPointF(width / 2., 0); - case QSGItem::TopRight: - return QPointF(width, 0); - case QSGItem::Left: - return QPointF(0, height / 2.); - case QSGItem::Center: - return QPointF(width / 2., height / 2.); - case QSGItem::Right: - return QPointF(width, height / 2.); - case QSGItem::BottomLeft: - return QPointF(0, height); - case QSGItem::Bottom: - return QPointF(width / 2., height); - case QSGItem::BottomRight: - return QPointF(width, height); - } -} - -void QSGItemPrivate::transformChanged() -{ -} - -void QSGItemPrivate::deliverKeyEvent(QKeyEvent *e) -{ - Q_Q(QSGItem); - - Q_ASSERT(e->isAccepted()); - if (keyHandler) { - if (e->type() == QEvent::KeyPress) - keyHandler->keyPressed(e, false); - else - keyHandler->keyReleased(e, false); - - if (e->isAccepted()) - return; - else - e->accept(); - } - - if (e->type() == QEvent::KeyPress) - q->keyPressEvent(e); - else - q->keyReleaseEvent(e); - - if (e->isAccepted()) - return; - - if (keyHandler) { - e->accept(); - - if (e->type() == QEvent::KeyPress) - keyHandler->keyPressed(e, true); - else - keyHandler->keyReleased(e, true); - } -} - -void QSGItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e) -{ - Q_Q(QSGItem); - - Q_ASSERT(e->isAccepted()); - if (keyHandler) { - keyHandler->inputMethodEvent(e, false); - - if (e->isAccepted()) - return; - else - e->accept(); - } - - q->inputMethodEvent(e); - - if (e->isAccepted()) - return; - - if (keyHandler) { - e->accept(); - - keyHandler->inputMethodEvent(e, true); - } -} - -void QSGItemPrivate::deliverFocusEvent(QFocusEvent *e) -{ - Q_Q(QSGItem); - - if (e->type() == QEvent::FocusIn) { - q->focusInEvent(e); - } else { - q->focusOutEvent(e); - } -} - -void QSGItemPrivate::deliverMouseEvent(QMouseEvent *e) -{ - Q_Q(QSGItem); - - Q_ASSERT(e->isAccepted()); - - switch (e->type()) { - default: - Q_ASSERT(!"Unknown event type"); - case QEvent::MouseMove: - q->mouseMoveEvent(e); - break; - case QEvent::MouseButtonPress: - q->mousePressEvent(e); - break; - case QEvent::MouseButtonRelease: - q->mouseReleaseEvent(e); - break; - case QEvent::MouseButtonDblClick: - q->mouseDoubleClickEvent(e); - break; - } -} - -void QSGItemPrivate::deliverWheelEvent(QWheelEvent *e) -{ - Q_Q(QSGItem); - q->wheelEvent(e); -} - -void QSGItemPrivate::deliverTouchEvent(QTouchEvent *e) -{ - Q_Q(QSGItem); - q->touchEvent(e); -} - -void QSGItemPrivate::deliverHoverEvent(QHoverEvent *e) -{ - Q_Q(QSGItem); - switch (e->type()) { - default: - Q_ASSERT(!"Unknown event type"); - case QEvent::HoverEnter: - q->hoverEnterEvent(e); - break; - case QEvent::HoverLeave: - q->hoverLeaveEvent(e); - break; - case QEvent::HoverMove: - q->hoverMoveEvent(e); - break; - } -} - -void QSGItemPrivate::deliverDragEvent(QEvent *e) -{ - Q_Q(QSGItem); - switch (e->type()) { - default: - Q_ASSERT(!"Unknown event type"); - case QEvent::DragEnter: - q->dragEnterEvent(static_cast(e)); - break; - case QEvent::DragLeave: - q->dragLeaveEvent(static_cast(e)); - break; - case QEvent::DragMove: - q->dragMoveEvent(static_cast(e)); - break; - case QEvent::Drop: - q->dropEvent(static_cast(e)); - break; - } -} - -void QSGItem::itemChange(ItemChange change, const ItemChangeData &value) -{ - Q_UNUSED(change); - Q_UNUSED(value); -} - -/*! \internal */ -// XXX todo - do we want/need this anymore? -// Note that it's now used for varying clip rect -QRectF QSGItem::boundingRect() const -{ - Q_D(const QSGItem); - return QRectF(0, 0, d->width, d->height); -} - -QSGItem::TransformOrigin QSGItem::transformOrigin() const -{ - Q_D(const QSGItem); - return d->origin; -} - -void QSGItem::setTransformOrigin(TransformOrigin origin) -{ - Q_D(QSGItem); - if (origin == d->origin) - return; - - d->origin = origin; - d->dirty(QSGItemPrivate::TransformOrigin); - - emit transformOriginChanged(d->origin); -} - -QPointF QSGItem::transformOriginPoint() const -{ - Q_D(const QSGItem); - if (!d->transformOriginPoint.isNull()) - return d->transformOriginPoint; - return d->computeTransformOrigin(); -} - -void QSGItem::setTransformOriginPoint(const QPointF &point) -{ - Q_D(QSGItem); - if (d->transformOriginPoint == point) - return; - - d->transformOriginPoint = point; - d->dirty(QSGItemPrivate::TransformOrigin); -} - -qreal QSGItem::z() const -{ - Q_D(const QSGItem); - return d->z; -} - -void QSGItem::setZ(qreal v) -{ - Q_D(QSGItem); - if (d->z == v) - return; - - d->z = v; - - d->dirty(QSGItemPrivate::ZValue); - if (d->parentItem) - QSGItemPrivate::get(d->parentItem)->dirty(QSGItemPrivate::ChildrenStackingChanged); - - emit zChanged(); -} - - -/*! - \qmlproperty real QtQuick2::Item::rotation - This property holds the rotation of the item in degrees clockwise. - - This specifies how many degrees to rotate the item around its transformOrigin. - The default rotation is 0 degrees (i.e. not rotated at all). - - \table - \row - \o \image declarative-rotation.png - \o - \qml - Rectangle { - color: "blue" - width: 100; height: 100 - Rectangle { - color: "red" - x: 25; y: 25; width: 50; height: 50 - rotation: 30 - } - } - \endqml - \endtable - - \sa transform, Rotation -*/ - -/*! - \qmlproperty real QtQuick2::Item::scale - This property holds the scale of the item. - - A scale of less than 1 means the item will be displayed smaller than - normal, and a scale of greater than 1 means the item will be - displayed larger than normal. A negative scale means the item will - be mirrored. - - By default, items are displayed at a scale of 1 (i.e. at their - normal size). - - Scaling is from the item's transformOrigin. - - \table - \row - \o \image declarative-scale.png - \o - \qml - Rectangle { - color: "blue" - width: 100; height: 100 - Rectangle { - color: "green" - width: 25; height: 25 - } - Rectangle { - color: "red" - x: 25; y: 25; width: 50; height: 50 - scale: 1.4 - } - } - \endqml - \endtable - - \sa transform, Scale -*/ - -/*! - \qmlproperty real QtQuick2::Item::opacity - - This property holds the opacity of the item. Opacity is specified as a - number between 0 (fully transparent) and 1 (fully opaque). The default is 1. - - When this property is set, the specified opacity is also applied - individually to child items. In almost all cases this is what you want, - but in some cases it may produce undesired results. For example in the - second set of rectangles below, the red rectangle has specified an opacity - of 0.5, which affects the opacity of its blue child rectangle even though - the child has not specified an opacity. - - \table - \row - \o \image declarative-item_opacity1.png - \o - \qml - Item { - Rectangle { - color: "red" - width: 100; height: 100 - Rectangle { - color: "blue" - x: 50; y: 50; width: 100; height: 100 - } - } - } - \endqml - \row - \o \image declarative-item_opacity2.png - \o - \qml - Item { - Rectangle { - opacity: 0.5 - color: "red" - width: 100; height: 100 - Rectangle { - color: "blue" - x: 50; y: 50; width: 100; height: 100 - } - } - } - \endqml - \endtable - - If an item's opacity is set to 0, the item will no longer receive mouse - events, but will continue to receive key events and will retain the keyboard - \l focus if it has been set. (In contrast, setting the \l visible property - to \c false stops both mouse and keyboard events, and also removes focus - from the item.) -*/ - -/*! - Returns a value indicating whether mouse input should - remain with this item exclusively. - - \sa setKeepMouseGrab() - */ - -qreal QSGItem::rotation() const -{ - Q_D(const QSGItem); - return d->rotation; -} - -void QSGItem::setRotation(qreal r) -{ - Q_D(QSGItem); - if (d->rotation == r) - return; - - d->rotation = r; - - d->dirty(QSGItemPrivate::BasicTransform); - - d->itemChange(ItemRotationHasChanged, r); - - emit rotationChanged(); -} - -qreal QSGItem::scale() const -{ - Q_D(const QSGItem); - return d->scale; -} - -void QSGItem::setScale(qreal s) -{ - Q_D(QSGItem); - if (d->scale == s) - return; - - d->scale = s; - - d->dirty(QSGItemPrivate::BasicTransform); - - emit scaleChanged(); -} - -qreal QSGItem::opacity() const -{ - Q_D(const QSGItem); - return d->opacity; -} - -void QSGItem::setOpacity(qreal o) -{ - Q_D(QSGItem); - if (d->opacity == o) - return; - - d->opacity = o; - - d->dirty(QSGItemPrivate::OpacityValue); - - d->itemChange(ItemOpacityHasChanged, o); - - emit opacityChanged(); -} - -bool QSGItem::isVisible() const -{ - Q_D(const QSGItem); - return d->effectiveVisible; -} - -void QSGItem::setVisible(bool v) -{ - Q_D(QSGItem); - if (v == d->explicitVisible) - return; - - d->explicitVisible = v; - - d->setEffectiveVisibleRecur(d->calcEffectiveVisible()); -} - -bool QSGItem::isEnabled() const -{ - Q_D(const QSGItem); - return d->effectiveEnable; -} - -void QSGItem::setEnabled(bool e) -{ - Q_D(QSGItem); - if (e == d->explicitEnable) - return; - - d->explicitEnable = e; - - d->setEffectiveEnableRecur(d->calcEffectiveEnable()); -} - -bool QSGItemPrivate::calcEffectiveVisible() const -{ - // XXX todo - Should the effective visible of an element with no parent just be the current - // effective visible? This would prevent pointless re-processing in the case of an element - // moving to/from a no-parent situation, but it is different from what graphics view does. - return explicitVisible && (!parentItem || QSGItemPrivate::get(parentItem)->effectiveVisible); -} - -void QSGItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible) -{ - Q_Q(QSGItem); - - if (newEffectiveVisible && !explicitVisible) { - // This item locally overrides visibility - return; - } - - if (newEffectiveVisible == effectiveVisible) { - // No change necessary - return; - } - - effectiveVisible = newEffectiveVisible; - dirty(Visible); - if (parentItem) QSGItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); - - if (canvas) { - QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(canvas); - if (canvasPriv->mouseGrabberItem == q) - q->ungrabMouse(); - } - - for (int ii = 0; ii < childItems.count(); ++ii) - QSGItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible); - - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::Visibility) - change.listener->itemVisibilityChanged(q); - } - - emit q->visibleChanged(); -} - -bool QSGItemPrivate::calcEffectiveEnable() const -{ - // XXX todo - Should the effective enable of an element with no parent just be the current - // effective enable? This would prevent pointless re-processing in the case of an element - // moving to/from a no-parent situation, but it is different from what graphics view does. - return explicitEnable && (!parentItem || QSGItemPrivate::get(parentItem)->effectiveEnable); -} - -void QSGItemPrivate::setEffectiveEnableRecur(bool newEffectiveEnable) -{ - Q_Q(QSGItem); - - // XXX todo - need to fixup focus - - if (newEffectiveEnable && !explicitEnable) { - // This item locally overrides enable - return; - } - - if (newEffectiveEnable == effectiveEnable) { - // No change necessary - return; - } - - effectiveEnable = newEffectiveEnable; - - if (canvas) { - QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(canvas); - if (canvasPriv->mouseGrabberItem == q) - q->ungrabMouse(); - } - - for (int ii = 0; ii < childItems.count(); ++ii) - QSGItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(newEffectiveEnable); - - emit q->enabledChanged(); -} - -QString QSGItemPrivate::dirtyToString() const -{ -#define DIRTY_TO_STRING(value) if (dirtyAttributes & value) { \ - if (!rv.isEmpty()) \ - rv.append(QLatin1String("|")); \ - rv.append(QLatin1String(#value)); \ -} - -// QString rv = QLatin1String("0x") + QString::number(dirtyAttributes, 16); - QString rv; - - DIRTY_TO_STRING(TransformOrigin); - DIRTY_TO_STRING(Transform); - DIRTY_TO_STRING(BasicTransform); - DIRTY_TO_STRING(Position); - DIRTY_TO_STRING(Size); - DIRTY_TO_STRING(ZValue); - DIRTY_TO_STRING(Content); - DIRTY_TO_STRING(Smooth); - DIRTY_TO_STRING(OpacityValue); - DIRTY_TO_STRING(ChildrenChanged); - DIRTY_TO_STRING(ChildrenStackingChanged); - DIRTY_TO_STRING(ParentChanged); - DIRTY_TO_STRING(Clip); - DIRTY_TO_STRING(Canvas); - DIRTY_TO_STRING(EffectReference); - DIRTY_TO_STRING(Visible); - DIRTY_TO_STRING(HideReference); - - return rv; -} - -void QSGItemPrivate::dirty(DirtyType type) -{ - Q_Q(QSGItem); - if (type & (TransformOrigin | Transform | BasicTransform | Position | Size)) - transformChanged(); - - if (!(dirtyAttributes & type) || (canvas && !prevDirtyItem)) { - dirtyAttributes |= type; - if (canvas) { - addToDirtyList(); - QSGCanvasPrivate::get(canvas)->dirtyItem(q); - } - } -} - -void QSGItemPrivate::addToDirtyList() -{ - Q_Q(QSGItem); - - Q_ASSERT(canvas); - if (!prevDirtyItem) { - Q_ASSERT(!nextDirtyItem); - - QSGCanvasPrivate *p = QSGCanvasPrivate::get(canvas); - nextDirtyItem = p->dirtyItemList; - if (nextDirtyItem) QSGItemPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem; - prevDirtyItem = &p->dirtyItemList; - p->dirtyItemList = q; - p->dirtyItem(q); - } - Q_ASSERT(prevDirtyItem); -} - -void QSGItemPrivate::removeFromDirtyList() -{ - if (prevDirtyItem) { - if (nextDirtyItem) QSGItemPrivate::get(nextDirtyItem)->prevDirtyItem = prevDirtyItem; - *prevDirtyItem = nextDirtyItem; - prevDirtyItem = 0; - nextDirtyItem = 0; - } - Q_ASSERT(!prevDirtyItem); - Q_ASSERT(!nextDirtyItem); -} - -void QSGItemPrivate::refFromEffectItem(bool hide) -{ - ++effectRefCount; - if (1 == effectRefCount) { - dirty(EffectReference); - if (parentItem) QSGItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); - } - if (hide) { - if (++hideRefCount == 1) - dirty(HideReference); - } -} - -void QSGItemPrivate::derefFromEffectItem(bool unhide) -{ - Q_ASSERT(effectRefCount); - --effectRefCount; - if (0 == effectRefCount) { - dirty(EffectReference); - if (parentItem) QSGItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged); - } - if (unhide) { - if (--hideRefCount == 0) - dirty(HideReference); - } -} - -void QSGItemPrivate::itemChange(QSGItem::ItemChange change, const QSGItem::ItemChangeData &data) -{ - Q_Q(QSGItem); - switch (change) { - case QSGItem::ItemChildAddedChange: - q->itemChange(change, data); - if (_contents && componentComplete) - _contents->childAdded(data.item); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::Children) { - change.listener->itemChildAdded(q, data.item); - } - } - break; - case QSGItem::ItemChildRemovedChange: - q->itemChange(change, data); - if (_contents && componentComplete) - _contents->childRemoved(data.item); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::Children) { - change.listener->itemChildRemoved(q, data.item); - } - } - break; - case QSGItem::ItemSceneChange: - q->itemChange(change, data); - break; - case QSGItem::ItemVisibleHasChanged: - q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::Visibility) { - change.listener->itemVisibilityChanged(q); - } - } - break; - case QSGItem::ItemParentHasChanged: - q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::Parent) { - change.listener->itemParentChanged(q, data.item); - } - } - break; - case QSGItem::ItemOpacityHasChanged: - q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::Opacity) { - change.listener->itemOpacityChanged(q); - } - } - break; - case QSGItem::ItemActiveFocusHasChanged: - q->itemChange(change, data); - break; - case QSGItem::ItemRotationHasChanged: - q->itemChange(change, data); - for (int ii = 0; ii < changeListeners.count(); ++ii) { - const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii); - if (change.types & QSGItemPrivate::Rotation) { - change.listener->itemRotationChanged(q); - } - } - break; - } -} - -/*! - \property QSGItem::smooth - \brief whether the item is smoothly transformed. - - This property is provided purely for the purpose of optimization. Turning - smooth transforms off is faster, but looks worse; turning smooth - transformations on is slower, but looks better. - - By default smooth transformations are off. -*/ - -/*! - Returns true if the item should be drawn with antialiasing and - smooth pixmap filtering, false otherwise. - - The default is false. - - \sa setSmooth() -*/ -bool QSGItem::smooth() const -{ - Q_D(const QSGItem); - return d->smooth; -} - -/*! - Sets whether the item should be drawn with antialiasing and - smooth pixmap filtering to \a smooth. - - \sa smooth() -*/ -void QSGItem::setSmooth(bool smooth) -{ - Q_D(QSGItem); - if (d->smooth == smooth) - return; - - d->smooth = smooth; - d->dirty(QSGItemPrivate::Smooth); - - emit smoothChanged(smooth); -} - -QSGItem::Flags QSGItem::flags() const -{ - Q_D(const QSGItem); - return (QSGItem::Flags)d->flags; -} - -void QSGItem::setFlag(Flag flag, bool enabled) -{ - Q_D(QSGItem); - if (enabled) - setFlags((Flags)(d->flags | (quint32)flag)); - else - setFlags((Flags)(d->flags & ~(quint32)flag)); -} - -void QSGItem::setFlags(Flags flags) -{ - Q_D(QSGItem); - - if ((flags & ItemIsFocusScope) != (d->flags & ItemIsFocusScope)) { - if (flags & ItemIsFocusScope && !d->childItems.isEmpty() && d->canvas) { - qWarning("QSGItem: Cannot set FocusScope once item has children and is in a canvas."); - flags &= ~ItemIsFocusScope; - } else if (d->flags & ItemIsFocusScope) { - qWarning("QSGItem: Cannot unset FocusScope flag."); - flags |= ItemIsFocusScope; - } - } - - if ((flags & ItemClipsChildrenToShape ) != (d->flags & ItemClipsChildrenToShape)) - d->dirty(QSGItemPrivate::Clip); - - d->flags = flags; -} - -qreal QSGItem::x() const -{ - Q_D(const QSGItem); - return d->x; -} - -qreal QSGItem::y() const -{ - Q_D(const QSGItem); - return d->y; -} - -QPointF QSGItem::pos() const -{ - Q_D(const QSGItem); - return QPointF(d->x, d->y); -} - -void QSGItem::setX(qreal v) -{ - Q_D(QSGItem); - if (d->x == v) - return; - - qreal oldx = d->x; - d->x = v; - - d->dirty(QSGItemPrivate::Position); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(oldx, y(), width(), height())); -} - -void QSGItem::setY(qreal v) -{ - Q_D(QSGItem); - if (d->y == v) - return; - - qreal oldy = d->y; - d->y = v; - - d->dirty(QSGItemPrivate::Position); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(x(), oldy, width(), height())); -} - -void QSGItem::setPos(const QPointF &pos) -{ - Q_D(QSGItem); - if (QPointF(d->x, d->y) == pos) - return; - - qreal oldx = d->x; - qreal oldy = d->y; - - d->x = pos.x(); - d->y = pos.y(); - - d->dirty(QSGItemPrivate::Position); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(oldx, oldy, width(), height())); -} - -qreal QSGItem::width() const -{ - Q_D(const QSGItem); - return d->width; -} - -void QSGItem::setWidth(qreal w) -{ - Q_D(QSGItem); - if (qIsNaN(w)) - return; - - d->widthValid = true; - if (d->width == w) - return; - - qreal oldWidth = d->width; - d->width = w; - - d->dirty(QSGItemPrivate::Size); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(x(), y(), oldWidth, height())); -} - -void QSGItem::resetWidth() -{ - Q_D(QSGItem); - d->widthValid = false; - setImplicitWidth(implicitWidth()); -} - -void QSGItemPrivate::implicitWidthChanged() -{ - Q_Q(QSGItem); - emit q->implicitWidthChanged(); -} - -qreal QSGItemPrivate::getImplicitWidth() const -{ - return implicitWidth; -} -/*! - Returns the width of the item that is implied by other properties that determine the content. -*/ -qreal QSGItem::implicitWidth() const -{ - Q_D(const QSGItem); - return d->getImplicitWidth(); -} - -/*! - \qmlproperty real QtQuick2::Item::implicitWidth - \qmlproperty real QtQuick2::Item::implicitHeight - - Defines the natural width or height of the Item if no \l width or \l height is specified. - - The default implicit size for most items is 0x0, however some elements have an inherent - implicit size which cannot be overridden, e.g. Image, Text. - - Setting the implicit size is useful for defining components that have a preferred size - based on their content, for example: - - \qml - // Label.qml - import QtQuick 1.1 - - Item { - property alias icon: image.source - property alias label: text.text - implicitWidth: text.implicitWidth + image.implicitWidth - implicitHeight: Math.max(text.implicitHeight, image.implicitHeight) - Image { id: image } - Text { - id: text - wrapMode: Text.Wrap - anchors.left: image.right; anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - } - } - \endqml - - \bold Note: using implicitWidth of Text or TextEdit and setting the width explicitly - incurs a performance penalty as the text must be laid out twice. -*/ - -/*! - Sets the implied width of the item to \a w. - This is the width implied by other properties that determine the content. -*/ -void QSGItem::setImplicitWidth(qreal w) -{ - Q_D(QSGItem); - bool changed = w != d->implicitWidth; - d->implicitWidth = w; - if (d->width == w || widthValid()) { - if (changed) - d->implicitWidthChanged(); - return; - } - - qreal oldWidth = d->width; - d->width = w; - - d->dirty(QSGItemPrivate::Size); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(x(), y(), oldWidth, height())); - - if (changed) - d->implicitWidthChanged(); -} - -/*! - Returns whether the width property has been set explicitly. -*/ -bool QSGItem::widthValid() const -{ - Q_D(const QSGItem); - return d->widthValid; -} - -qreal QSGItem::height() const -{ - Q_D(const QSGItem); - return d->height; -} - -void QSGItem::setHeight(qreal h) -{ - Q_D(QSGItem); - if (qIsNaN(h)) - return; - - d->heightValid = true; - if (d->height == h) - return; - - qreal oldHeight = d->height; - d->height = h; - - d->dirty(QSGItemPrivate::Size); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(x(), y(), width(), oldHeight)); -} - -void QSGItem::resetHeight() -{ - Q_D(QSGItem); - d->heightValid = false; - setImplicitHeight(implicitHeight()); -} - -void QSGItemPrivate::implicitHeightChanged() -{ - Q_Q(QSGItem); - emit q->implicitHeightChanged(); -} - -qreal QSGItemPrivate::getImplicitHeight() const -{ - return implicitHeight; -} - -/*! - Returns the height of the item that is implied by other properties that determine the content. -*/ -qreal QSGItem::implicitHeight() const -{ - Q_D(const QSGItem); - return d->getImplicitHeight(); -} - - -/*! - Sets the implied height of the item to \a h. - This is the height implied by other properties that determine the content. -*/ -void QSGItem::setImplicitHeight(qreal h) -{ - Q_D(QSGItem); - bool changed = h != d->implicitHeight; - d->implicitHeight = h; - if (d->height == h || heightValid()) { - if (changed) - d->implicitHeightChanged(); - return; - } - - qreal oldHeight = d->height; - d->height = h; - - d->dirty(QSGItemPrivate::Size); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(x(), y(), width(), oldHeight)); - - if (changed) - d->implicitHeightChanged(); -} - -/*! - Returns whether the height property has been set explicitly. -*/ -bool QSGItem::heightValid() const -{ - Q_D(const QSGItem); - return d->heightValid; -} - -void QSGItem::setSize(const QSizeF &size) -{ - Q_D(QSGItem); - d->heightValid = true; - d->widthValid = true; - - if (QSizeF(d->width, d->height) == size) - return; - - qreal oldHeight = d->height; - qreal oldWidth = d->width; - d->height = size.height(); - d->width = size.width(); - - d->dirty(QSGItemPrivate::Size); - - geometryChanged(QRectF(x(), y(), width(), height()), - QRectF(x(), y(), oldWidth, oldHeight)); -} - -bool QSGItem::hasActiveFocus() const -{ - Q_D(const QSGItem); - return d->activeFocus; -} - -bool QSGItem::hasFocus() const -{ - Q_D(const QSGItem); - return d->focus; -} - -void QSGItem::setFocus(bool focus) -{ - Q_D(QSGItem); - if (d->focus == focus) - return; - - if (d->canvas) { - // Need to find our nearest focus scope - QSGItem *scope = parentItem(); - while (scope && !scope->isFocusScope()) - scope = scope->parentItem(); - if (focus) - QSGCanvasPrivate::get(d->canvas)->setFocusInScope(scope, this); - else - QSGCanvasPrivate::get(d->canvas)->clearFocusInScope(scope, this); - } else { - d->focus = focus; - emit focusChanged(focus); - } -} - -bool QSGItem::isFocusScope() const -{ - return flags() & ItemIsFocusScope; -} - -QSGItem *QSGItem::scopedFocusItem() const -{ - Q_D(const QSGItem); - if (!isFocusScope()) - return 0; - else - return d->subFocusItem; -} - - -Qt::MouseButtons QSGItem::acceptedMouseButtons() const -{ - Q_D(const QSGItem); - return d->acceptedMouseButtons; -} - -void QSGItem::setAcceptedMouseButtons(Qt::MouseButtons buttons) -{ - Q_D(QSGItem); - d->acceptedMouseButtons = buttons; -} - -bool QSGItem::filtersChildMouseEvents() const -{ - Q_D(const QSGItem); - return d->filtersChildMouseEvents; -} - -void QSGItem::setFiltersChildMouseEvents(bool filter) -{ - Q_D(QSGItem); - d->filtersChildMouseEvents = filter; -} - -bool QSGItem::isUnderMouse() const -{ - Q_D(const QSGItem); - if (!d->canvas) - return false; - - QPoint cursorPos = QCursor::pos(); - if (QRectF(0, 0, width(), height()).contains(mapFromScene(cursorPos))) // ### refactor: d->canvas->mapFromGlobal(cursorPos)))) - return true; - return false; -} - -bool QSGItem::acceptHoverEvents() const -{ - Q_D(const QSGItem); - return d->hoverEnabled; -} - -void QSGItem::setAcceptHoverEvents(bool enabled) -{ - Q_D(QSGItem); - d->hoverEnabled = enabled; -} - -void QSGItem::grabMouse() -{ - Q_D(QSGItem); - if (!d->canvas) - return; - QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(d->canvas); - if (canvasPriv->mouseGrabberItem == this) - return; - - QSGItem *oldGrabber = canvasPriv->mouseGrabberItem; - canvasPriv->mouseGrabberItem = this; - if (oldGrabber) - oldGrabber->mouseUngrabEvent(); -} - -void QSGItem::ungrabMouse() -{ - Q_D(QSGItem); - if (!d->canvas) - return; - QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(d->canvas); - if (canvasPriv->mouseGrabberItem != this) { - qWarning("QSGItem::ungrabMouse(): Item is not the mouse grabber."); - return; - } - - canvasPriv->mouseGrabberItem = 0; - mouseUngrabEvent(); -} - -bool QSGItem::keepMouseGrab() const -{ - Q_D(const QSGItem); - return d->keepMouse; -} - -/*! - The flag indicating whether the mouse should remain - with this item is set to \a keep. - - This is useful for items that wish to grab and keep mouse - interaction following a predefined gesture. For example, - an item that is interested in horizontal mouse movement - may set keepMouseGrab to true once a threshold has been - exceeded. Once keepMouseGrab has been set to true, filtering - items will not react to mouse events. - - If the item does not indicate that it wishes to retain mouse grab, - a filtering item may steal the grab. For example, Flickable may attempt - to steal a mouse grab if it detects that the user has begun to - move the viewport. - - \sa keepMouseGrab() - */ -void QSGItem::setKeepMouseGrab(bool keep) -{ - Q_D(QSGItem); - d->keepMouse = keep; -} - -/*! - \qmlmethod object QtQuick2::Item::mapFromItem(Item item, real x, real y) - - Maps the point (\a x, \a y), which is in \a item's coordinate system, to - this item's coordinate system, and returns an object with \c x and \c y - properties matching the mapped cooordinate. - - If \a item is a \c null value, this maps the point from the coordinate - system of the root QML view. -*/ -/*! - \qmlmethod object QtQuick2::Item::mapToItem(Item item, real x, real y) - - Maps the point (\a x, \a y), which is in this item's coordinate system, to - \a item's coordinate system, and returns an object with \c x and \c y - properties matching the mapped cooordinate. - - If \a item is a \c null value, this maps \a x and \a y to the coordinate - system of the root QML view. -*/ -QPointF QSGItem::mapToItem(const QSGItem *item, const QPointF &point) const -{ - QPointF p = mapToScene(point); - if (item) - p = item->mapFromScene(p); - return p; -} - -QPointF QSGItem::mapToScene(const QPointF &point) const -{ - Q_D(const QSGItem); - return d->itemToCanvasTransform().map(point); -} - -QRectF QSGItem::mapRectToItem(const QSGItem *item, const QRectF &rect) const -{ - Q_D(const QSGItem); - QTransform t = d->itemToCanvasTransform(); - if (item) - t *= QSGItemPrivate::get(item)->canvasToItemTransform(); - return t.mapRect(rect); -} - -QRectF QSGItem::mapRectToScene(const QRectF &rect) const -{ - Q_D(const QSGItem); - return d->itemToCanvasTransform().mapRect(rect); -} - -QPointF QSGItem::mapFromItem(const QSGItem *item, const QPointF &point) const -{ - QPointF p = item?item->mapToScene(point):point; - return mapFromScene(p); -} - -QPointF QSGItem::mapFromScene(const QPointF &point) const -{ - Q_D(const QSGItem); - return d->canvasToItemTransform().map(point); -} - -QRectF QSGItem::mapRectFromItem(const QSGItem *item, const QRectF &rect) const -{ - Q_D(const QSGItem); - QTransform t = item?QSGItemPrivate::get(item)->itemToCanvasTransform():QTransform(); - t *= d->canvasToItemTransform(); - return t.mapRect(rect); -} - -QRectF QSGItem::mapRectFromScene(const QRectF &rect) const -{ - Q_D(const QSGItem); - return d->canvasToItemTransform().mapRect(rect); -} - - -/*! - \qmlmethod QtQuick2::Item::forceActiveFocus() - - Forces active focus on the item. - - This method sets focus on the item and makes sure that all the focus scopes - higher in the object hierarchy are also given the focus. -*/ - -/*! - Forces active focus on the item. - - This method sets focus on the item and makes sure that all the focus scopes - higher in the object hierarchy are also given the focus. -*/ - -/*! - \qmlmethod QtQuick2::Item::childAt(real x, real y) - - Returns the visible child item at point (\a x, \a y), which is in this - item's coordinate system, or \c null if there is no such item. -*/ - -/*! - Returns the visible child item at point (\a x, \a y), which is in this - item's coordinate system, or 0 if there is no such item. -*/ - -/*! - \qmlproperty list QtQuick2::Item::states - This property holds a list of states defined by the item. - - \qml - Item { - states: [ - State { - // ... - }, - State { - // ... - } - // ... - ] - } - \endqml - - \sa {qmlstate}{States} -*/ -/*! - \qmlproperty list QtQuick2::Item::transitions - This property holds a list of transitions defined by the item. - - \qml - Item { - transitions: [ - Transition { - // ... - }, - Transition { - // ... - } - // ... - ] - } - \endqml - - \sa {QML Animation and Transitions}{Transitions} -*/ -/* - \qmlproperty list QtQuick2::Item::filter - This property holds a list of graphical filters to be applied to the item. - - \l {Filter}{Filters} include things like \l {Blur}{blurring} - the item, or giving it a \l Reflection. Some - filters may not be available on all canvases; if a filter is not - available on a certain canvas, it will simply not be applied for - that canvas (but the QML will still be considered valid). - - \qml - Item { - filter: [ - Blur { - // ... - }, - Reflection { - // ... - } - // ... - ] - } - \endqml -*/ - -/*! - \qmlproperty bool QtQuick2::Item::clip - This property holds whether clipping is enabled. The default clip value is \c false. - - If clipping is enabled, an item will clip its own painting, as well - as the painting of its children, to its bounding rectangle. - - Non-rectangular clipping regions are not supported for performance reasons. -*/ - -/*! - \property QSGItem::clip - This property holds whether clipping is enabled. The default clip value is \c false. - - If clipping is enabled, an item will clip its own painting, as well - as the painting of its children, to its bounding rectangle. If you set - clipping during an item's paint operation, remember to re-set it to - prevent clipping the rest of your scene. - - Non-rectangular clipping regions are not supported for performance reasons. -*/ - -/*! - \qmlproperty string QtQuick2::Item::state - - This property holds the name of the current state of the item. - - This property is often used in scripts to change between states. For - example: - - \js - function toggle() { - if (button.state == 'On') - button.state = 'Off'; - else - button.state = 'On'; - } - \endjs - - If the item is in its base state (i.e. no explicit state has been - set), \c state will be a blank string. Likewise, you can return an - item to its base state by setting its current state to \c ''. - - \sa {qmlstates}{States} -*/ - -/*! - \qmlproperty list QtQuick2::Item::transform - This property holds the list of transformations to apply. - - For more information see \l Transform. -*/ - -/*! - \enum QSGItem::TransformOrigin - - Controls the point about which simple transforms like scale apply. - - \value TopLeft The top-left corner of the item. - \value Top The center point of the top of the item. - \value TopRight The top-right corner of the item. - \value Left The left most point of the vertical middle. - \value Center The center of the item. - \value Right The right most point of the vertical middle. - \value BottomLeft The bottom-left corner of the item. - \value Bottom The center point of the bottom of the item. - \value BottomRight The bottom-right corner of the item. -*/ - - -/*! - \qmlproperty bool QtQuick2::Item::activeFocus - - This property indicates whether the item has active focus. - - An item with active focus will receive keyboard input, - or is a FocusScope ancestor of the item that will receive keyboard input. - - Usually, activeFocus is gained by setting focus on an item and its enclosing - FocusScopes. In the following example \c input will have activeFocus. - \qml - Rectangle { - FocusScope { - focus: true - TextInput { - id: input - focus: true - } - } - } - \endqml - - \sa focus, {qmlfocus}{Keyboard Focus} -*/ - -/*! - \qmlproperty bool QtQuick2::Item::focus - This property indicates whether the item has focus within the enclosing focus scope. If true, this item - will gain active focus when the enclosing focus scope gains active focus. - In the following example, \c input will be given active focus when \c scope gains active focus. - \qml - Rectangle { - FocusScope { - id: scope - TextInput { - id: input - focus: true - } - } - } - \endqml - - For the purposes of this property, the scene as a whole is assumed to act like a focus scope. - On a practical level, that means the following QML will give active focus to \c input on startup. - - \qml - Rectangle { - TextInput { - id: input - focus: true - } - } - \endqml - - \sa activeFocus, {qmlfocus}{Keyboard Focus} -*/ - - -/*! - \property QSGItem::anchors - \internal -*/ - -/*! - \property QSGItem::left - \internal -*/ - -/*! - \property QSGItem::right - \internal -*/ - -/*! - \property QSGItem::horizontalCenter - \internal -*/ - -/*! - \property QSGItem::top - \internal -*/ - -/*! - \property QSGItem::bottom - \internal -*/ - -/*! - \property QSGItem::verticalCenter - \internal -*/ - -/*! - \property QSGItem::focus - \internal -*/ - -/*! - \property QSGItem::transform - \internal -*/ - -/*! - \property QSGItem::transformOrigin - \internal -*/ - -/*! - \property QSGItem::activeFocus - \internal -*/ - -/*! - \property QSGItem::baseline - \internal -*/ - -/*! - \property QSGItem::data - \internal -*/ - -/*! - \property QSGItem::resources - \internal -*/ - -/*! - \property QSGItem::state - \internal -*/ - -/*! - \property QSGItem::states - \internal -*/ - -/*! - \property QSGItem::transformOriginPoint - \internal -*/ - -/*! - \property QSGItem::transitions - \internal -*/ - -bool QSGItem::event(QEvent *ev) -{ -#if 0 - if (ev->type() == QEvent::PolishRequest) { - Q_D(QSGItem); - d->polishScheduled = false; - updatePolish(); - return true; - } else { - return QObject::event(ev); - } -#endif - if (ev->type() == QEvent::InputMethodQuery) { - QInputMethodQueryEvent *query = static_cast(ev); - Qt::InputMethodQueries queries = query->queries(); - for (uint i = 0; i < 32; ++i) { - Qt::InputMethodQuery q = (Qt::InputMethodQuery)(int)(queries & (1<setValue(q, v); - } - } - query->accept(); - return true; - } else if (ev->type() == QEvent::InputMethod) { - inputMethodEvent(static_cast(ev)); - return true; - } - return QObject::event(ev); -} - -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug debug, QSGItem *item) -{ - if (!item) { - debug << "QSGItem(0)"; - return debug; - } - - debug << item->metaObject()->className() << "(this =" << ((void*)item) - << ", name=" << item->objectName() - << ", parent =" << ((void*)item->parentItem()) - << ", geometry =" << QRectF(item->pos(), QSizeF(item->width(), item->height())) - << ", z =" << item->z() << ')'; - return debug; -} -#endif - -qint64 QSGItemPrivate::consistentTime = -1; -void QSGItemPrivate::setConsistentTime(qint64 t) -{ - consistentTime = t; -} - -class QElapsedTimerConsistentTimeHack -{ -public: - void start() { - t1 = QSGItemPrivate::consistentTime; - t2 = 0; - } - qint64 elapsed() { - return QSGItemPrivate::consistentTime - t1; - } - qint64 restart() { - qint64 val = QSGItemPrivate::consistentTime - t1; - t1 = QSGItemPrivate::consistentTime; - t2 = 0; - return val; - } - -private: - qint64 t1; - qint64 t2; -}; - -void QSGItemPrivate::start(QElapsedTimer &t) -{ - if (QSGItemPrivate::consistentTime == -1) - t.start(); - else - ((QElapsedTimerConsistentTimeHack*)&t)->start(); -} - -qint64 QSGItemPrivate::elapsed(QElapsedTimer &t) -{ - if (QSGItemPrivate::consistentTime == -1) - return t.elapsed(); - else - return ((QElapsedTimerConsistentTimeHack*)&t)->elapsed(); -} - -qint64 QSGItemPrivate::restart(QElapsedTimer &t) -{ - if (QSGItemPrivate::consistentTime == -1) - return t.restart(); - else - return ((QElapsedTimerConsistentTimeHack*)&t)->restart(); -} - -/*! - \fn bool QSGItem::isTextureProvider() const - - Returns true if this item is a texture provider. The default - implementation returns false. - - This function can be called from any thread. - */ - -/*! - \fn QSGTextureProvider *QSGItem::textureProvider() const - - Returns the texture provider for an item. The default implementation - returns 0. - - This function may only be called on the rendering thread. - */ - -QT_END_NAMESPACE - -#include diff --git a/src/declarative/items/qsgitem.h b/src/declarative/items/qsgitem.h deleted file mode 100644 index 018453f68f..0000000000 --- a/src/declarative/items/qsgitem.h +++ /dev/null @@ -1,415 +0,0 @@ -// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGITEM_H -#define QSGITEM_H - -#include -#include - -#include -#include -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGItem; -class QSGTransformPrivate; -class QSGTransform : public QObject -{ - Q_OBJECT -public: - QSGTransform(QObject *parent = 0); - ~QSGTransform(); - - void appendToItem(QSGItem *); - void prependToItem(QSGItem *); - - virtual void applyTo(QMatrix4x4 *matrix) const = 0; - -protected Q_SLOTS: - void update(); - -protected: - QSGTransform(QSGTransformPrivate &dd, QObject *parent); - -private: - Q_DECLARE_PRIVATE(QSGTransform) -}; - -class QDeclarativeV8Function; -class QDeclarativeState; -class QSGAnchorLine; -class QDeclarativeTransition; -class QSGKeyEvent; -class QSGAnchors; -class QSGItemPrivate; -class QSGCanvas; -class QSGDragEvent; -class QSGEngine; -class QTouchEvent; -class QSGNode; -class QSGTransformNode; -class QSGTextureProvider; - -class Q_DECLARATIVE_EXPORT QSGItem : public QObject, public QDeclarativeParserStatus -{ - Q_OBJECT - Q_INTERFACES(QDeclarativeParserStatus) - - Q_PROPERTY(QSGItem *parent READ parentItem WRITE setParentItem NOTIFY parentChanged DESIGNABLE false FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty data READ data DESIGNABLE false) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty resources READ resources DESIGNABLE false) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) - - Q_PROPERTY(QPointF pos READ pos FINAL) - Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged FINAL) - Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged FINAL) - Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY zChanged FINAL) - Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged RESET resetWidth FINAL) - Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged RESET resetHeight FINAL) - - Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL) - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) - Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL) - - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty states READ states DESIGNABLE false) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty transitions READ transitions DESIGNABLE false) - Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) - Q_PROPERTY(QRectF childrenRect READ childrenRect NOTIFY childrenRectChanged DESIGNABLE false FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchors * anchors READ anchors DESIGNABLE false CONSTANT FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine left READ left CONSTANT FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine right READ right CONSTANT FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine horizontalCenter READ horizontalCenter CONSTANT FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine top READ top CONSTANT FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine bottom READ bottom CONSTANT FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine verticalCenter READ verticalCenter CONSTANT FINAL) - Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine baseline READ baseline CONSTANT FINAL) - Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged) - - Q_PROPERTY(bool clip READ clip WRITE setClip NOTIFY clipChanged) - - Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL) - Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL) - - Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) - Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) - Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin NOTIFY transformOriginChanged) - Q_PROPERTY(QPointF transformOriginPoint READ transformOriginPoint) // XXX todo - notify? - Q_PROPERTY(QDeclarativeListProperty transform READ transform DESIGNABLE false FINAL) - - Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) - Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged) - Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged) - - Q_ENUMS(TransformOrigin) - Q_CLASSINFO("DefaultProperty", "data") - -public: - enum Flag { - ItemClipsChildrenToShape = 0x01, - ItemAcceptsInputMethod = 0x02, - ItemIsFocusScope = 0x04, - ItemHasContents = 0x08, - ItemAcceptsDrops = 0x10 - // Remember to increment the size of QSGItemPrivate::flags - }; - Q_DECLARE_FLAGS(Flags, Flag) - - enum ItemChange { - ItemChildAddedChange, // value.item - ItemChildRemovedChange, // value.item - ItemSceneChange, // value.canvas - ItemVisibleHasChanged, // value.realValue - ItemParentHasChanged, // value.item - ItemOpacityHasChanged, // value.realValue - ItemActiveFocusHasChanged, // value.boolValue - ItemRotationHasChanged // value.realValue - }; - - union ItemChangeData { - ItemChangeData(QSGItem *v) : item(v) {} - ItemChangeData(QSGCanvas *v) : canvas(v) {} - ItemChangeData(qreal v) : realValue(v) {} - ItemChangeData(bool v) : boolValue(v) {} - - QSGItem *item; - QSGCanvas *canvas; - qreal realValue; - bool boolValue; - }; - - enum TransformOrigin { - TopLeft, Top, TopRight, - Left, Center, Right, - BottomLeft, Bottom, BottomRight - }; - - QSGItem(QSGItem *parent = 0); - virtual ~QSGItem(); - - QSGEngine *sceneGraphEngine() const; - - QSGCanvas *canvas() const; - QSGItem *parentItem() const; - void setParentItem(QSGItem *parent); - void stackBefore(const QSGItem *); - void stackAfter(const QSGItem *); - - QRectF childrenRect(); - QList childItems() const; - - bool clip() const; - void setClip(bool); - - QString state() const; - void setState(const QString &); - - qreal baselineOffset() const; - void setBaselineOffset(qreal); - - QDeclarativeListProperty transform(); - - qreal x() const; - qreal y() const; - QPointF pos() const; - void setX(qreal); - void setY(qreal); - void setPos(const QPointF &); - - qreal width() const; - void setWidth(qreal); - void resetWidth(); - qreal implicitWidth() const; - - qreal height() const; - void setHeight(qreal); - void resetHeight(); - qreal implicitHeight() const; - - void setSize(const QSizeF &size); - - TransformOrigin transformOrigin() const; - void setTransformOrigin(TransformOrigin); - QPointF transformOriginPoint() const; - void setTransformOriginPoint(const QPointF &); - - qreal z() const; - void setZ(qreal); - - qreal rotation() const; - void setRotation(qreal); - qreal scale() const; - void setScale(qreal); - - qreal opacity() const; - void setOpacity(qreal); - - bool isVisible() const; - void setVisible(bool); - - bool isEnabled() const; - void setEnabled(bool); - - bool smooth() const; - void setSmooth(bool); - - Flags flags() const; - void setFlag(Flag flag, bool enabled = true); - void setFlags(Flags flags); - - virtual QRectF boundingRect() const; - - bool hasActiveFocus() const; - bool hasFocus() const; - void setFocus(bool); - bool isFocusScope() const; - QSGItem *scopedFocusItem() const; - - Qt::MouseButtons acceptedMouseButtons() const; - void setAcceptedMouseButtons(Qt::MouseButtons buttons); - bool acceptHoverEvents() const; - void setAcceptHoverEvents(bool enabled); - - bool isUnderMouse() const; - void grabMouse(); - void ungrabMouse(); - bool keepMouseGrab() const; - void setKeepMouseGrab(bool); - bool filtersChildMouseEvents() const; - void setFiltersChildMouseEvents(bool filter); - - QTransform itemTransform(QSGItem *, bool *) const; - QPointF mapToItem(const QSGItem *item, const QPointF &point) const; - QPointF mapToScene(const QPointF &point) const; - QRectF mapRectToItem(const QSGItem *item, const QRectF &rect) const; - QRectF mapRectToScene(const QRectF &rect) const; - QPointF mapFromItem(const QSGItem *item, const QPointF &point) const; - QPointF mapFromScene(const QPointF &point) const; - QRectF mapRectFromItem(const QSGItem *item, const QRectF &rect) const; - QRectF mapRectFromScene(const QRectF &rect) const; - - void polish(); - - Q_INVOKABLE void mapFromItem(QDeclarativeV8Function*) const; - Q_INVOKABLE void mapToItem(QDeclarativeV8Function*) const; - Q_INVOKABLE void forceActiveFocus(); - Q_INVOKABLE QSGItem *childAt(qreal x, qreal y) const; - - Qt::InputMethodHints inputMethodHints() const; - void setInputMethodHints(Qt::InputMethodHints hints); - virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; - - struct UpdatePaintNodeData { - QSGTransformNode *transformNode; - private: - friend class QSGCanvasPrivate; - UpdatePaintNodeData(); - }; - - virtual bool isTextureProvider() const { return false; } - virtual QSGTextureProvider *textureProvider() const { return 0; } - -public Q_SLOTS: - void update(); - void updateMicroFocus(); - -Q_SIGNALS: - void childrenRectChanged(const QRectF &); - void baselineOffsetChanged(qreal); - void stateChanged(const QString &); - void focusChanged(bool); - void activeFocusChanged(bool); - void parentChanged(QSGItem *); - void transformOriginChanged(TransformOrigin); - void smoothChanged(bool); - void clipChanged(bool); - - // XXX todo - void childrenChanged(); - void opacityChanged(); - void enabledChanged(); - void visibleChanged(); - void rotationChanged(); - void scaleChanged(); - - void xChanged(); - void yChanged(); - void widthChanged(); - void heightChanged(); - void zChanged(); - void implicitWidthChanged(); - void implicitHeightChanged(); - -protected: - virtual bool event(QEvent *); - - bool isComponentComplete() const; - virtual void itemChange(ItemChange, const ItemChangeData &); - - void setImplicitWidth(qreal); - bool widthValid() const; // ### better name? - void setImplicitHeight(qreal); - bool heightValid() const; // ### better name? - - virtual void classBegin(); - virtual void componentComplete(); - - virtual void keyPressEvent(QKeyEvent *event); - virtual void keyReleaseEvent(QKeyEvent *event); - virtual void inputMethodEvent(QInputMethodEvent *); - virtual void focusInEvent(QFocusEvent *); - virtual void focusOutEvent(QFocusEvent *); - virtual void mousePressEvent(QMouseEvent *event); - virtual void mouseMoveEvent(QMouseEvent *event); - virtual void mouseReleaseEvent(QMouseEvent *event); - virtual void mouseDoubleClickEvent(QMouseEvent *event); - virtual void mouseUngrabEvent(); // XXX todo - params? - virtual void wheelEvent(QWheelEvent *event); - virtual void touchEvent(QTouchEvent *event); - virtual void hoverEnterEvent(QHoverEvent *event); - virtual void hoverMoveEvent(QHoverEvent *event); - virtual void hoverLeaveEvent(QHoverEvent *event); - virtual void dragEnterEvent(QDragEnterEvent *); - virtual void dragMoveEvent(QDragMoveEvent *); - virtual void dragLeaveEvent(QDragLeaveEvent *); - virtual void dropEvent(QDropEvent *); - virtual bool childMouseEventFilter(QSGItem *, QEvent *); - virtual void windowDeactivateEvent(); - - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - virtual void updatePolish(); - -protected: - QSGItem(QSGItemPrivate &dd, QSGItem *parent = 0); - -private: - friend class QSGCanvas; - friend class QSGCanvasPrivate; - friend class QSGRenderer; - Q_DISABLE_COPY(QSGItem) - Q_DECLARE_PRIVATE(QSGItem) -}; - -// XXX todo -Q_DECLARE_OPERATORS_FOR_FLAGS(QSGItem::Flags) - -#ifndef QT_NO_DEBUG_STREAM -QDebug Q_DECLARATIVE_EXPORT operator<<(QDebug debug, QSGItem *item); -#endif - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGItem) -QML_DECLARE_TYPE(QSGTransform) - -QT_END_HEADER - -#endif // QSGITEM_H diff --git a/src/declarative/items/qsgitem_p.h b/src/declarative/items/qsgitem_p.h deleted file mode 100644 index c0ed1dbc8b..0000000000 --- a/src/declarative/items/qsgitem_p.h +++ /dev/null @@ -1,716 +0,0 @@ -// Commit: 5c783d0a9a912816813945387903857a314040b5 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGITEM_P_H -#define QSGITEM_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgitem.h" - -#include "qsganchors_p.h" -#include "qsganchors_p_p.h" -#include "qsgitemchangelistener_p.h" - -#include "qsgcanvas_p.h" - -#include "qsgnode.h" -#include "qsgclipnode_p.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QNetworkReply; -class QSGItemKeyFilter; -class QSGLayoutMirroringAttached; - -//### merge into private? -class QSGContents : public QObject, public QSGItemChangeListener -{ - Q_OBJECT -public: - QSGContents(QSGItem *item); - ~QSGContents(); - - QRectF rectF() const; - - void childRemoved(QSGItem *item); - void childAdded(QSGItem *item); - - void calcGeometry() { calcWidth(); calcHeight(); } - void complete(); - -Q_SIGNALS: - void rectChanged(QRectF); - -protected: - void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); - void itemDestroyed(QSGItem *item); - //void itemVisibilityChanged(QSGItem *item) - -private: - void calcHeight(QSGItem *changed = 0); - void calcWidth(QSGItem *changed = 0); - - QSGItem *m_item; - qreal m_x; - qreal m_y; - qreal m_width; - qreal m_height; -}; - -class QSGTransformPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QSGTransform); -public: - static QSGTransformPrivate* get(QSGTransform *transform) { return transform->d_func(); } - - QSGTransformPrivate(); - - QList items; -}; - -class Q_DECLARATIVE_EXPORT QSGItemPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QSGItem) - -public: - static QSGItemPrivate* get(QSGItem *item) { return item->d_func(); } - static const QSGItemPrivate* get(const QSGItem *item) { return item->d_func(); } - - QSGItemPrivate(); - void init(QSGItem *parent); - - QDeclarativeListProperty data(); - QDeclarativeListProperty resources(); - QDeclarativeListProperty children(); - - QDeclarativeListProperty states(); - QDeclarativeListProperty transitions(); - - QString state() const; - void setState(const QString &); - - QSGAnchorLine left() const; - QSGAnchorLine right() const; - QSGAnchorLine horizontalCenter() const; - QSGAnchorLine top() const; - QSGAnchorLine bottom() const; - QSGAnchorLine verticalCenter() const; - QSGAnchorLine baseline() const; - - // data property - static void data_append(QDeclarativeListProperty *, QObject *); - static int data_count(QDeclarativeListProperty *); - static QObject *data_at(QDeclarativeListProperty *, int); - static void data_clear(QDeclarativeListProperty *); - - // resources property - static QObject *resources_at(QDeclarativeListProperty *, int); - static void resources_append(QDeclarativeListProperty *, QObject *); - static int resources_count(QDeclarativeListProperty *); - static void resources_clear(QDeclarativeListProperty *); - - // children property - static void children_append(QDeclarativeListProperty *, QSGItem *); - static int children_count(QDeclarativeListProperty *); - static QSGItem *children_at(QDeclarativeListProperty *, int); - static void children_clear(QDeclarativeListProperty *); - - // transform property - static int transform_count(QDeclarativeListProperty *list); - static void transform_append(QDeclarativeListProperty *list, QSGTransform *); - static QSGTransform *transform_at(QDeclarativeListProperty *list, int); - static void transform_clear(QDeclarativeListProperty *list); - - QSGAnchors *anchors() const; - mutable QSGAnchors *_anchors; - QSGContents *_contents; - - QDeclarativeNullableValue baselineOffset; - - struct AnchorLines { - AnchorLines(QSGItem *); - QSGAnchorLine left; - QSGAnchorLine right; - QSGAnchorLine hCenter; - QSGAnchorLine top; - QSGAnchorLine bottom; - QSGAnchorLine vCenter; - QSGAnchorLine baseline; - }; - mutable AnchorLines *_anchorLines; - AnchorLines *anchorLines() const; - - enum ChangeType { - Geometry = 0x01, - SiblingOrder = 0x02, - Visibility = 0x04, - Opacity = 0x08, - Destroyed = 0x10, - Parent = 0x20, - Children = 0x40, - Rotation = 0x80, - }; - - Q_DECLARE_FLAGS(ChangeTypes, ChangeType) - - struct ChangeListener { - ChangeListener(QSGItemChangeListener *l, QSGItemPrivate::ChangeTypes t) : listener(l), types(t) {} - QSGItemChangeListener *listener; - QSGItemPrivate::ChangeTypes types; - bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; } - }; - - void addItemChangeListener(QSGItemChangeListener *listener, ChangeTypes types) { - changeListeners.append(ChangeListener(listener, types)); - } - void removeItemChangeListener(QSGItemChangeListener *, ChangeTypes types); - QPODVector changeListeners; - - QDeclarativeStateGroup *_states(); - QDeclarativeStateGroup *_stateGroup; - - QSGItem::TransformOrigin origin:5; - quint32 flags:5; - bool widthValid:1; - bool heightValid:1; - bool componentComplete:1; - bool keepMouse:1; - bool hoverEnabled:1; - bool smooth:1; - bool focus:1; - bool activeFocus:1; - bool notifiedFocus:1; - bool notifiedActiveFocus:1; - bool filtersChildMouseEvents:1; - bool explicitVisible:1; - bool effectiveVisible:1; - bool explicitEnable:1; - bool effectiveEnable:1; - bool polishScheduled:1; - bool inheritedLayoutMirror:1; - bool effectiveLayoutMirror:1; - bool isMirrorImplicit:1; - bool inheritMirrorFromParent:1; - bool inheritMirrorFromItem:1; - bool childrenDoNotOverlap:1; - quint32 dummy:1; - - QSGCanvas *canvas; - QSGContext *sceneGraphContext() const { Q_ASSERT(canvas); return static_cast(QObjectPrivate::get(canvas))->context; } - - QSGItem *parentItem; - QList childItems; - QList paintOrderChildItems() const; - void addChild(QSGItem *); - void removeChild(QSGItem *); - void siblingOrderChanged(); - - class InitializationState { - public: - QSGItem *getFocusScope(QSGItem *item); - void clear(); - void clear(QSGItem *focusScope); - private: - QSGItem *focusScope; - }; - void initCanvas(InitializationState *, QSGCanvas *); - - QSGItem *subFocusItem; - - QTransform canvasToItemTransform() const; - QTransform itemToCanvasTransform() const; - void itemToParentTransform(QTransform &) const; - - qreal x; - qreal y; - qreal width; - qreal height; - qreal implicitWidth; - qreal implicitHeight; - - qreal z; - qreal scale; - qreal rotation; - qreal opacity; - - QSGLayoutMirroringAttached* attachedLayoutDirection; - - Qt::MouseButtons acceptedMouseButtons; - Qt::InputMethodHints imHints; - - QPointF transformOriginPoint; - - virtual qreal getImplicitWidth() const; - virtual qreal getImplicitHeight() const; - virtual void implicitWidthChanged(); - virtual void implicitHeightChanged(); - - void resolveLayoutMirror(); - void setImplicitLayoutMirror(bool mirror, bool inherit); - void setLayoutMirror(bool mirror); - bool isMirrored() const { - return effectiveLayoutMirror; - } - - QPointF computeTransformOrigin() const; - QList transforms; - virtual void transformChanged(); - - QSGItemKeyFilter *keyHandler; - void deliverKeyEvent(QKeyEvent *); - void deliverInputMethodEvent(QInputMethodEvent *); - void deliverFocusEvent(QFocusEvent *); - void deliverMouseEvent(QMouseEvent *); - void deliverWheelEvent(QWheelEvent *); - void deliverTouchEvent(QTouchEvent *); - void deliverHoverEvent(QHoverEvent *); - void deliverDragEvent(QEvent *); - - bool calcEffectiveVisible() const; - void setEffectiveVisibleRecur(bool); - bool calcEffectiveEnable() const; - void setEffectiveEnableRecur(bool); - - // XXX todo - enum DirtyType { - TransformOrigin = 0x00000001, - Transform = 0x00000002, - BasicTransform = 0x00000004, - Position = 0x00000008, - Size = 0x00000010, - - ZValue = 0x00000020, - Content = 0x00000040, - Smooth = 0x00000080, - OpacityValue = 0x00000100, - ChildrenChanged = 0x00000200, - ChildrenStackingChanged = 0x00000400, - ParentChanged = 0x00000800, - - Clip = 0x00001000, - Canvas = 0x00002000, - - EffectReference = 0x00008000, - Visible = 0x00010000, - HideReference = 0x00020000, - // When you add an attribute here, don't forget to update - // dirtyToString() - - TransformUpdateMask = TransformOrigin | Transform | BasicTransform | Position | Size | Canvas, - ComplexTransformUpdateMask = Transform | Canvas, - ContentUpdateMask = Size | Content | Smooth | Canvas, - ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Canvas, - - }; - quint32 dirtyAttributes; - QString dirtyToString() const; - void dirty(DirtyType); - void addToDirtyList(); - void removeFromDirtyList(); - QSGItem *nextDirtyItem; - QSGItem**prevDirtyItem; - - inline QSGTransformNode *itemNode(); - inline QSGNode *childContainerNode(); - - /* - QSGNode order is: - - itemNode - - (opacityNode) - - (clipNode) - - (effectNode) - - groupNode - */ - - QSGTransformNode *itemNodeInstance; - QSGOpacityNode *opacityNode; - QSGDefaultClipNode *clipNode; - QSGRootNode *rootNode; - QSGNode *groupNode; - QSGNode *paintNode; - QSGNode *beforePaintNode; - - virtual QSGTransformNode *createTransformNode(); - - // A reference from an effect item means that this item is used by the effect, so - // it should insert a root node. - void refFromEffectItem(bool hide); - void derefFromEffectItem(bool unhide); - int effectRefCount; - int hideRefCount; - - void itemChange(QSGItem::ItemChange, const QSGItem::ItemChangeData &); - - virtual void mirrorChange() {} - - static qint64 consistentTime; - static void setConsistentTime(qint64 t); - static void start(QElapsedTimer &); - static qint64 elapsed(QElapsedTimer &); - static qint64 restart(QElapsedTimer &); -}; - -/* - Key filters can be installed on a QSGItem, but not removed. Currently they - are only used by attached objects (which are only destroyed on Item - destruction), so this isn't a problem. If in future this becomes any form - of public API, they will have to support removal too. -*/ -class QSGItemKeyFilter -{ -public: - QSGItemKeyFilter(QSGItem * = 0); - virtual ~QSGItemKeyFilter(); - - virtual void keyPressed(QKeyEvent *event, bool post); - virtual void keyReleased(QKeyEvent *event, bool post); - virtual void inputMethodEvent(QInputMethodEvent *event, bool post); - virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; - virtual void componentComplete(); - - bool m_processPost; - -private: - QSGItemKeyFilter *m_next; -}; - -class QSGKeyNavigationAttachedPrivate : public QObjectPrivate -{ -public: - QSGKeyNavigationAttachedPrivate() - : QObjectPrivate(), - left(0), right(0), up(0), down(0), tab(0), backtab(0), - leftSet(false), rightSet(false), upSet(false), downSet(false), - tabSet(false), backtabSet(false) {} - - QSGItem *left; - QSGItem *right; - QSGItem *up; - QSGItem *down; - QSGItem *tab; - QSGItem *backtab; - bool leftSet : 1; - bool rightSet : 1; - bool upSet : 1; - bool downSet : 1; - bool tabSet : 1; - bool backtabSet : 1; -}; - -class QSGKeyNavigationAttached : public QObject, public QSGItemKeyFilter -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGKeyNavigationAttached) - - Q_PROPERTY(QSGItem *left READ left WRITE setLeft NOTIFY leftChanged) - Q_PROPERTY(QSGItem *right READ right WRITE setRight NOTIFY rightChanged) - Q_PROPERTY(QSGItem *up READ up WRITE setUp NOTIFY upChanged) - Q_PROPERTY(QSGItem *down READ down WRITE setDown NOTIFY downChanged) - Q_PROPERTY(QSGItem *tab READ tab WRITE setTab NOTIFY tabChanged) - Q_PROPERTY(QSGItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged) - Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) - - Q_ENUMS(Priority) - -public: - QSGKeyNavigationAttached(QObject * = 0); - - QSGItem *left() const; - void setLeft(QSGItem *); - QSGItem *right() const; - void setRight(QSGItem *); - QSGItem *up() const; - void setUp(QSGItem *); - QSGItem *down() const; - void setDown(QSGItem *); - QSGItem *tab() const; - void setTab(QSGItem *); - QSGItem *backtab() const; - void setBacktab(QSGItem *); - - enum Priority { BeforeItem, AfterItem }; - Priority priority() const; - void setPriority(Priority); - - static QSGKeyNavigationAttached *qmlAttachedProperties(QObject *); - -Q_SIGNALS: - void leftChanged(); - void rightChanged(); - void upChanged(); - void downChanged(); - void tabChanged(); - void backtabChanged(); - void priorityChanged(); - -private: - virtual void keyPressed(QKeyEvent *event, bool post); - virtual void keyReleased(QKeyEvent *event, bool post); - void setFocusNavigation(QSGItem *currentItem, const char *dir); -}; - -class QSGLayoutMirroringAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged) - Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged) - -public: - explicit QSGLayoutMirroringAttached(QObject *parent = 0); - - bool enabled() const; - void setEnabled(bool); - void resetEnabled(); - - bool childrenInherit() const; - void setChildrenInherit(bool); - - static QSGLayoutMirroringAttached *qmlAttachedProperties(QObject *); -Q_SIGNALS: - void enabledChanged(); - void childrenInheritChanged(); -private: - friend class QSGItemPrivate; - QSGItemPrivate *itemPrivate; -}; - -class QSGKeysAttachedPrivate : public QObjectPrivate -{ -public: - QSGKeysAttachedPrivate() - : QObjectPrivate(), inPress(false), inRelease(false) - , inIM(false), enabled(true), imeItem(0), item(0) - {} - - bool isConnected(const char *signalName); - - //loop detection - bool inPress:1; - bool inRelease:1; - bool inIM:1; - - bool enabled : 1; - - QSGItem *imeItem; - QList targets; - QSGItem *item; -}; - -class QSGKeysAttached : public QObject, public QSGItemKeyFilter -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGKeysAttached) - - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) - Q_PROPERTY(QDeclarativeListProperty forwardTo READ forwardTo) - Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) - - Q_ENUMS(Priority) - -public: - QSGKeysAttached(QObject *parent=0); - ~QSGKeysAttached(); - - bool enabled() const { Q_D(const QSGKeysAttached); return d->enabled; } - void setEnabled(bool enabled) { - Q_D(QSGKeysAttached); - if (enabled != d->enabled) { - d->enabled = enabled; - emit enabledChanged(); - } - } - - enum Priority { BeforeItem, AfterItem}; - Priority priority() const; - void setPriority(Priority); - - QDeclarativeListProperty forwardTo() { - Q_D(QSGKeysAttached); - return QDeclarativeListProperty(this, d->targets); - } - - virtual void componentComplete(); - - static QSGKeysAttached *qmlAttachedProperties(QObject *); - -Q_SIGNALS: - void enabledChanged(); - void priorityChanged(); - void pressed(QSGKeyEvent *event); - void released(QSGKeyEvent *event); - void digit0Pressed(QSGKeyEvent *event); - void digit1Pressed(QSGKeyEvent *event); - void digit2Pressed(QSGKeyEvent *event); - void digit3Pressed(QSGKeyEvent *event); - void digit4Pressed(QSGKeyEvent *event); - void digit5Pressed(QSGKeyEvent *event); - void digit6Pressed(QSGKeyEvent *event); - void digit7Pressed(QSGKeyEvent *event); - void digit8Pressed(QSGKeyEvent *event); - void digit9Pressed(QSGKeyEvent *event); - - void leftPressed(QSGKeyEvent *event); - void rightPressed(QSGKeyEvent *event); - void upPressed(QSGKeyEvent *event); - void downPressed(QSGKeyEvent *event); - void tabPressed(QSGKeyEvent *event); - void backtabPressed(QSGKeyEvent *event); - - void asteriskPressed(QSGKeyEvent *event); - void numberSignPressed(QSGKeyEvent *event); - void escapePressed(QSGKeyEvent *event); - void returnPressed(QSGKeyEvent *event); - void enterPressed(QSGKeyEvent *event); - void deletePressed(QSGKeyEvent *event); - void spacePressed(QSGKeyEvent *event); - void backPressed(QSGKeyEvent *event); - void cancelPressed(QSGKeyEvent *event); - void selectPressed(QSGKeyEvent *event); - void yesPressed(QSGKeyEvent *event); - void noPressed(QSGKeyEvent *event); - void context1Pressed(QSGKeyEvent *event); - void context2Pressed(QSGKeyEvent *event); - void context3Pressed(QSGKeyEvent *event); - void context4Pressed(QSGKeyEvent *event); - void callPressed(QSGKeyEvent *event); - void hangupPressed(QSGKeyEvent *event); - void flipPressed(QSGKeyEvent *event); - void menuPressed(QSGKeyEvent *event); - void volumeUpPressed(QSGKeyEvent *event); - void volumeDownPressed(QSGKeyEvent *event); - -private: - virtual void keyPressed(QKeyEvent *event, bool post); - virtual void keyReleased(QKeyEvent *event, bool post); - virtual void inputMethodEvent(QInputMethodEvent *, bool post); - virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; - - const QByteArray keyToSignal(int key) { - QByteArray keySignal; - if (key >= Qt::Key_0 && key <= Qt::Key_9) { - keySignal = "digit0Pressed"; - keySignal[5] = '0' + (key - Qt::Key_0); - } else { - int i = 0; - while (sigMap[i].key && sigMap[i].key != key) - ++i; - keySignal = sigMap[i].sig; - } - return keySignal; - } - - struct SigMap { - int key; - const char *sig; - }; - - static const SigMap sigMap[]; -}; - -QSGTransformNode *QSGItemPrivate::itemNode() -{ - if (!itemNodeInstance) { - itemNodeInstance = createTransformNode(); - itemNodeInstance->setFlag(QSGNode::OwnedByParent, false); -#ifdef QML_RUNTIME_TESTING - Q_Q(QSGItem); - itemNodeInstance->description = QString::fromLatin1("QSGItem(%1)").arg(QString::fromLatin1(q->metaObject()->className())); -#endif - } - return itemNodeInstance; -} - -QSGNode *QSGItemPrivate::childContainerNode() -{ - if (!groupNode) { - groupNode = new QSGNode(); - if (rootNode) - rootNode->appendChildNode(groupNode); - else if (clipNode) - clipNode->appendChildNode(groupNode); - else if (opacityNode) - opacityNode->appendChildNode(groupNode); - else - itemNode()->appendChildNode(groupNode); - groupNode->setFlag(QSGNode::ChildrenDoNotOverlap, childrenDoNotOverlap); -#ifdef QML_RUNTIME_TESTING - groupNode->description = QLatin1String("group"); -#endif - } - return groupNode; -} - -Q_DECLARE_OPERATORS_FOR_FLAGS(QSGItemPrivate::ChangeTypes); - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGKeysAttached) -QML_DECLARE_TYPEINFO(QSGKeysAttached, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QSGKeyNavigationAttached) -QML_DECLARE_TYPEINFO(QSGKeyNavigationAttached, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QSGLayoutMirroringAttached) -QML_DECLARE_TYPEINFO(QSGLayoutMirroringAttached, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QSGITEM_P_H diff --git a/src/declarative/items/qsgitemchangelistener_p.h b/src/declarative/items/qsgitemchangelistener_p.h deleted file mode 100644 index 3ea0c9cee7..0000000000 --- a/src/declarative/items/qsgitemchangelistener_p.h +++ /dev/null @@ -1,82 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGITEMCHANGELISTENER_P_H -#define QSGITEMCHANGELISTENER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -QT_BEGIN_NAMESPACE - -class QRectF; -class QSGItem; -class QSGAnchorsPrivate; -class QSGItemChangeListener -{ -public: - virtual void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &) {} - virtual void itemSiblingOrderChanged(QSGItem *) {} - virtual void itemVisibilityChanged(QSGItem *) {} - virtual void itemOpacityChanged(QSGItem *) {} - virtual void itemDestroyed(QSGItem *) {} - virtual void itemChildAdded(QSGItem *, QSGItem *) {} - virtual void itemChildRemoved(QSGItem *, QSGItem *) {} - virtual void itemParentChanged(QSGItem *, QSGItem *) {} - virtual void itemRotationChanged(QSGItem *) {} - - virtual QSGAnchorsPrivate *anchorPrivate() { return 0; } -}; - -QT_END_NAMESPACE - -#endif // QSGITEMCHANGELISTENER_P_H diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp deleted file mode 100644 index bb471dd05a..0000000000 --- a/src/declarative/items/qsgitemsmodule.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgitemsmodule_p.h" - -#include "qsgitem.h" -#include "qsgitem_p.h" -#include "qsgevents_p_p.h" -#include "qsgrectangle_p.h" -#include "qsgfocusscope_p.h" -#include "qsgtext_p.h" -#include "qsgtextinput_p.h" -#include "qsgtextedit_p.h" -#include "qsgimage_p.h" -#include "qsgborderimage_p.h" -#include "qsgscalegrid_p_p.h" -#include "qsgmousearea_p.h" -#include "qsgpincharea_p.h" -#include "qsgflickable_p.h" -#include "qsgflickable_p_p.h" -#include "qsglistview_p.h" -#include "qsgvisualitemmodel_p.h" -#include "qsgvisualdatamodel_p.h" -#include "qsggridview_p.h" -#include "qsgpathview_p.h" -#include -#include -#include "qsgpositioners_p.h" -#include "qsgrepeater_p.h" -#include "qsgloader_p.h" -#include "qsganimatedimage_p.h" -#include "qsgflipable_p.h" -#include "qsgtranslate_p.h" -#include "qsgstateoperations_p.h" -#include "qsganimation_p.h" -#include -#include -//#include -#include -#include -#include "qsgsprite_p.h" -#include "qsgspriteimage_p.h" -#include "qsgdrag_p.h" -#include "qsgdroparea_p.h" - -static QDeclarativePrivate::AutoParentResult qsgitem_autoParent(QObject *obj, QObject *parent) -{ - QSGItem *item = qobject_cast(obj); - if (!item) - return QDeclarativePrivate::IncompatibleObject; - - QSGItem *parentItem = qobject_cast(parent); - if (!parentItem) - return QDeclarativePrivate::IncompatibleParent; - - item->setParentItem(parentItem); - return QDeclarativePrivate::Parented; -} - -static void qt_sgitems_defineModule(const char *uri, int major, int minor) -{ - QDeclarativePrivate::RegisterAutoParent autoparent = { 0, &qsgitem_autoParent }; - QDeclarativePrivate::qmlregister(QDeclarativePrivate::AutoParentRegistration, &autoparent); - -#ifdef QT_NO_MOVIE - qmlRegisterTypeNotAvailable(uri,major,minor,"AnimatedImage", qApp->translate("QSGAnimatedImage","Qt was built without support for QMovie")); -#else - qmlRegisterType(uri,major,minor,"AnimatedImage"); -#endif - qmlRegisterType(uri,major,minor,"BorderImage"); - qmlRegisterType(uri,major,minor,"Column"); - qmlRegisterType(uri,major,minor,"Flickable"); - qmlRegisterType(uri,major,minor,"Flipable"); - qmlRegisterType(uri,major,minor,"Flow"); -// qmlRegisterType(uri,major,minor,"FocusPanel"); - qmlRegisterType(uri,major,minor,"FocusScope"); - qmlRegisterType(uri,major,minor,"Gradient"); - qmlRegisterType(uri,major,minor,"GradientStop"); - qmlRegisterType(uri,major,minor,"Grid"); - qmlRegisterType(uri,major,minor,"GridView"); - qmlRegisterType(uri,major,minor,"Image"); - qmlRegisterType(uri,major,minor,"Item"); - qmlRegisterType(uri,major,minor,"ListView"); - qmlRegisterType(uri,major,minor,"Loader"); - qmlRegisterType(uri,major,minor,"MouseArea"); - qmlRegisterType(uri,major,minor,"Path"); - qmlRegisterType(uri,major,minor,"PathAttribute"); - qmlRegisterType(uri,major,minor,"PathCubic"); - qmlRegisterType(uri,major,minor,"PathLine"); - qmlRegisterType(uri,major,minor,"PathPercent"); - qmlRegisterType(uri,major,minor,"PathQuad"); - qmlRegisterType("QtQuick",2,0,"PathCurve"); - qmlRegisterType("QtQuick",2,0,"PathArc"); - qmlRegisterType("QtQuick",2,0,"PathSvg"); - qmlRegisterType(uri,major,minor,"PathView"); - qmlRegisterUncreatableType(uri,major,minor,"Positioner", - QStringLiteral("Positioner is an abstract type that is only available as an attached property.")); -#ifndef QT_NO_VALIDATOR - qmlRegisterType(uri,major,minor,"IntValidator"); - qmlRegisterType(uri,major,minor,"DoubleValidator"); - qmlRegisterType(uri,major,minor,"RegExpValidator"); -#endif - qmlRegisterType(uri,major,minor,"Rectangle"); - qmlRegisterType(uri,major,minor,"Repeater"); - qmlRegisterType(uri,major,minor,"Row"); - qmlRegisterType(uri,major,minor,"Translate"); - qmlRegisterType(uri,major,minor,"Rotation"); - qmlRegisterType(uri,major,minor,"Scale"); - qmlRegisterType(uri,major,minor,"Text"); - qmlRegisterType(uri,major,minor,"TextEdit"); - qmlRegisterType(uri,major,minor,"TextInput"); - qmlRegisterType(uri,major,minor,"ViewSection"); - qmlRegisterType(uri,major,minor,"VisualDataModel"); - qmlRegisterType(uri,major,minor,"VisualDataGroup"); - qmlRegisterType(uri,major,minor,"VisualItemModel"); - - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterType(); -#ifndef QT_NO_VALIDATOR - qmlRegisterType(); -#endif - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterType(); - qRegisterMetaType("QSGAnchorLine"); - - qmlRegisterUncreatableType(uri,major,minor,"KeyNavigation",QSGKeyNavigationAttached::tr("KeyNavigation is only available via attached properties")); - qmlRegisterUncreatableType(uri,major,minor,"Keys",QSGKeysAttached::tr("Keys is only available via attached properties")); - qmlRegisterUncreatableType(uri,major,minor,"LayoutMirroring", QSGLayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties")); - - qmlRegisterType(uri,major,minor,"PinchArea"); - qmlRegisterType(uri,major,minor,"Pinch"); - qmlRegisterType(); - - qmlRegisterType("QtQuick", 2, 0, "ShaderEffectItem"); // TODO: Remove after grace period. - qmlRegisterType("QtQuick", 2, 0, "ShaderEffect"); - qmlRegisterType("QtQuick", 2, 0, "ShaderEffectSource"); - qmlRegisterUncreatableType("QtQuick", 2, 0, "ShaderEffectMesh", QSGShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh.")); - qmlRegisterType("QtQuick", 2, 0, "GridMesh"); - - qmlRegisterUncreatableType("QtQuick", 2, 0, "PaintedItem", QSGPaintedItem::tr("Cannot create instance of abstract class PaintedItem")); - - qmlRegisterType("QtQuick", 2, 0, "Canvas"); - - qmlRegisterType("QtQuick", 2, 0, "Sprite"); - qmlRegisterType("QtQuick", 2, 0, "SpriteImage"); - - qmlRegisterType(uri, major, minor,"ParentChange"); - qmlRegisterType(uri, major, minor,"AnchorChanges"); - qmlRegisterType(); - qmlRegisterType(uri, major, minor,"AnchorAnimation"); - qmlRegisterType(uri, major, minor,"ParentAnimation"); - qmlRegisterType("QtQuick",2,0,"PathAnimation"); - qmlRegisterType("QtQuick",2,0,"PathInterpolator"); - - qmlRegisterType("QtQuick", 2, 0, "DropArea"); - qmlRegisterType(); - qmlRegisterType(); - qmlRegisterUncreatableType("QtQuick", 2, 0, "Drag", QSGDragAttached::tr("Drag is only available via attached properties")); -} - -void QSGItemsModule::defineModule() -{ - static bool initialized = false; - if (initialized) - return; - initialized = true; - - // XXX todo - Remove before final integration... - QByteArray mode = qgetenv("QMLSCENE_IMPORT_NAME"); - QByteArray name = "QtQuick"; - int majorVersion = 2; - int minorVersion = 0; - if (mode == "quick1") { - majorVersion = 1; - } else if (mode == "qt") { - name = "Qt"; - majorVersion = 4; - minorVersion = 7; - } - - qt_sgitems_defineModule(name, majorVersion, minorVersion); -} - diff --git a/src/declarative/items/qsgitemsmodule_p.h b/src/declarative/items/qsgitemsmodule_p.h deleted file mode 100644 index b030669736..0000000000 --- a/src/declarative/items/qsgitemsmodule_p.h +++ /dev/null @@ -1,65 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGITEMSMODULE_P_H -#define QSGITEMSMODULE_P_H - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGItemsModule -{ -public: - static void defineModule(); -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QSGITEMSMODULE_P_H - diff --git a/src/declarative/items/qsgitemview.cpp b/src/declarative/items/qsgitemview.cpp deleted file mode 100644 index 5b95097dcc..0000000000 --- a/src/declarative/items/qsgitemview.cpp +++ /dev/null @@ -1,1676 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgitemview_p_p.h" - -QT_BEGIN_NAMESPACE - - -FxViewItem::FxViewItem(QSGItem *i, bool own) - : item(i), ownItem(own), index(-1) -{ -} - -FxViewItem::~FxViewItem() -{ - if (ownItem && item) { - item->setParentItem(0); - item->deleteLater(); - item = 0; - } -} - - -QSGItemViewChangeSet::QSGItemViewChangeSet() - : active(false) -{ - reset(); -} - -bool QSGItemViewChangeSet::hasPendingChanges() const -{ - return !pendingChanges.isEmpty(); -} - -void QSGItemViewChangeSet::applyChanges(const QDeclarativeChangeSet &changeSet) -{ - pendingChanges.apply(changeSet); - - int moveId = -1; - int moveOffset; - - foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) { - itemCount -= r.count; - if (moveId == -1 && newCurrentIndex >= r.index + r.count) { - newCurrentIndex -= r.count; - currentChanged = true; - } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) { - // current item has been removed. - if (r.isMove()) { - moveId = r.moveId; - moveOffset = newCurrentIndex - r.index; - } else { - currentRemoved = true; - newCurrentIndex = -1; - if (itemCount) - newCurrentIndex = qMin(r.index, itemCount - 1); - } - currentChanged = true; - } - } - foreach (const QDeclarativeChangeSet::Insert &i, changeSet.inserts()) { - if (moveId == -1) { - if (itemCount && newCurrentIndex >= i.index) { - newCurrentIndex += i.count; - currentChanged = true; - } else if (newCurrentIndex < 0) { - newCurrentIndex = 0; - currentChanged = true; - } else if (newCurrentIndex == 0 && !itemCount) { - // this is the first item, set the initial current index - currentChanged = true; - } - } else if (moveId == i.moveId) { - newCurrentIndex = i.index + moveOffset; - } - itemCount += i.count; - } -} - -void QSGItemViewChangeSet::prepare(int currentIndex, int count) -{ - if (active) - return; - reset(); - active = true; - itemCount = count; - newCurrentIndex = currentIndex; -} - -void QSGItemViewChangeSet::reset() -{ - itemCount = 0; - newCurrentIndex = -1; - pendingChanges.clear(); - removedItems.clear(); - active = false; - currentChanged = false; - currentRemoved = false; -} - - -QSGItemView::QSGItemView(QSGFlickablePrivate &dd, QSGItem *parent) - : QSGFlickable(dd, parent) -{ - Q_D(QSGItemView); - d->init(); -} - -QSGItemView::~QSGItemView() -{ - Q_D(QSGItemView); - d->clear(); - if (d->ownModel) - delete d->model; - delete d->header; - delete d->footer; -} - - -QSGItem *QSGItemView::currentItem() const -{ - Q_D(const QSGItemView); - if (!d->currentItem) - return 0; - const_cast(d)->applyPendingChanges(); - return d->currentItem->item; -} - -QVariant QSGItemView::model() const -{ - Q_D(const QSGItemView); - return d->modelVariant; -} - -void QSGItemView::setModel(const QVariant &model) -{ - Q_D(QSGItemView); - if (d->modelVariant == model) - return; - if (d->model) { - disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), - this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - } - - QSGVisualModel *oldModel = d->model; - - d->clear(); - d->setPosition(d->contentStartPosition()); - d->model = 0; - d->modelVariant = model; - - QObject *object = qvariant_cast(model); - QSGVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { - if (d->ownModel) { - delete oldModel; - d->ownModel = false; - } - d->model = vim; - } else { - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this), this); - d->ownModel = true; - if (isComponentComplete()) - static_cast(d->model.data())->componentComplete(); - } else { - d->model = oldModel; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(model); - } - - if (d->model) { - d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter; - if (isComponentComplete()) { - updateSections(); - d->refill(); - if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { - setCurrentIndex(0); - } else { - d->moveReason = QSGItemViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->resetHighlightPosition(); - d->updateTrackedItem(); - } - d->moveReason = QSGItemViewPrivate::Other; - } - d->updateViewport(); - } - connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), - this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - emit countChanged(); - } - emit modelChanged(); -} - -QDeclarativeComponent *QSGItemView::delegate() const -{ - Q_D(const QSGItemView); - if (d->model) { - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - return dataModel->delegate(); - } - - return 0; -} - -void QSGItemView::setDelegate(QDeclarativeComponent *delegate) -{ - Q_D(QSGItemView); - if (delegate == this->delegate()) - return; - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this)); - d->ownModel = true; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) { - int oldCount = dataModel->count(); - dataModel->setDelegate(delegate); - if (isComponentComplete()) { - for (int i = 0; i < d->visibleItems.count(); ++i) - d->releaseItem(d->visibleItems.at(i)); - d->visibleItems.clear(); - d->releaseItem(d->currentItem); - d->currentItem = 0; - updateSections(); - d->refill(); - d->moveReason = QSGItemViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->resetHighlightPosition(); - d->updateTrackedItem(); - } - d->moveReason = QSGItemViewPrivate::Other; - d->updateViewport(); - } - if (oldCount != dataModel->count()) - emit countChanged(); - } - emit delegateChanged(); -} - - -int QSGItemView::count() const -{ - Q_D(const QSGItemView); - if (!d->model) - return 0; - const_cast(d)->applyPendingChanges(); - return d->model->count(); -} - -int QSGItemView::currentIndex() const -{ - Q_D(const QSGItemView); - const_cast(d)->applyPendingChanges(); - return d->currentIndex; -} - -void QSGItemView::setCurrentIndex(int index) -{ - Q_D(QSGItemView); - if (d->requestedIndex >= 0) // currently creating item - return; - d->currentIndexCleared = (index == -1); - - d->applyPendingChanges(); - if (index == d->currentIndex) - return; - if (isComponentComplete() && d->isValid()) { - d->moveReason = QSGItemViewPrivate::SetIndex; - d->updateCurrent(index); - } else if (d->currentIndex != index) { - d->currentIndex = index; - emit currentIndexChanged(); - } -} - - -bool QSGItemView::isWrapEnabled() const -{ - Q_D(const QSGItemView); - return d->wrap; -} - -void QSGItemView::setWrapEnabled(bool wrap) -{ - Q_D(QSGItemView); - if (d->wrap == wrap) - return; - d->wrap = wrap; - emit keyNavigationWrapsChanged(); -} - -int QSGItemView::cacheBuffer() const -{ - Q_D(const QSGItemView); - return d->buffer; -} - -void QSGItemView::setCacheBuffer(int b) -{ - Q_D(QSGItemView); - if (d->buffer != b) { - d->buffer = b; - if (isComponentComplete()) { - d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter; - d->refill(); - } - emit cacheBufferChanged(); - } -} - - -Qt::LayoutDirection QSGItemView::layoutDirection() const -{ - Q_D(const QSGItemView); - return d->layoutDirection; -} - -void QSGItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection) -{ - Q_D(QSGItemView); - if (d->layoutDirection != layoutDirection) { - d->layoutDirection = layoutDirection; - d->regenerate(); - emit layoutDirectionChanged(); - emit effectiveLayoutDirectionChanged(); - } -} - -Qt::LayoutDirection QSGItemView::effectiveLayoutDirection() const -{ - Q_D(const QSGItemView); - if (d->effectiveLayoutMirror) - return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; - else - return d->layoutDirection; -} - - -QDeclarativeComponent *QSGItemView::header() const -{ - Q_D(const QSGItemView); - return d->headerComponent; -} - -QSGItem *QSGItemView::headerItem() const -{ - Q_D(const QSGItemView); - const_cast(d)->applyPendingChanges(); - return d->header ? d->header->item : 0; -} - -void QSGItemView::setHeader(QDeclarativeComponent *headerComponent) -{ - Q_D(QSGItemView); - if (d->headerComponent != headerComponent) { - d->applyPendingChanges(); - delete d->header; - d->header = 0; - d->headerComponent = headerComponent; - - d->markExtentsDirty(); - - if (isComponentComplete()) { - d->updateHeader(); - d->updateFooter(); - d->updateViewport(); - d->fixupPosition(); - } else { - emit headerItemChanged(); - } - emit headerChanged(); - } -} - -QDeclarativeComponent *QSGItemView::footer() const -{ - Q_D(const QSGItemView); - return d->footerComponent; -} - -QSGItem *QSGItemView::footerItem() const -{ - Q_D(const QSGItemView); - const_cast(d)->applyPendingChanges(); - return d->footer ? d->footer->item : 0; -} - -void QSGItemView::setFooter(QDeclarativeComponent *footerComponent) -{ - Q_D(QSGItemView); - if (d->footerComponent != footerComponent) { - d->applyPendingChanges(); - delete d->footer; - d->footer = 0; - d->footerComponent = footerComponent; - - if (isComponentComplete()) { - d->updateFooter(); - d->updateViewport(); - d->fixupPosition(); - } else { - emit footerItemChanged(); - } - emit footerChanged(); - } -} - -QDeclarativeComponent *QSGItemView::highlight() const -{ - Q_D(const QSGItemView); - const_cast(d)->applyPendingChanges(); - return d->highlightComponent; -} - -void QSGItemView::setHighlight(QDeclarativeComponent *highlightComponent) -{ - Q_D(QSGItemView); - if (highlightComponent != d->highlightComponent) { - d->applyPendingChanges(); - d->highlightComponent = highlightComponent; - d->createHighlight(); - if (d->currentItem) - d->updateHighlight(); - emit highlightChanged(); - } -} - -QSGItem *QSGItemView::highlightItem() const -{ - Q_D(const QSGItemView); - const_cast(d)->applyPendingChanges(); - return d->highlight ? d->highlight->item : 0; -} - -bool QSGItemView::highlightFollowsCurrentItem() const -{ - Q_D(const QSGItemView); - return d->autoHighlight; -} - -void QSGItemView::setHighlightFollowsCurrentItem(bool autoHighlight) -{ - Q_D(QSGItemView); - if (d->autoHighlight != autoHighlight) { - d->autoHighlight = autoHighlight; - if (autoHighlight) - d->updateHighlight(); - emit highlightFollowsCurrentItemChanged(); - } -} - -QSGItemView::HighlightRangeMode QSGItemView::highlightRangeMode() const -{ - Q_D(const QSGItemView); - return static_cast(d->highlightRange); -} - -void QSGItemView::setHighlightRangeMode(HighlightRangeMode mode) -{ - Q_D(QSGItemView); - if (d->highlightRange == mode) - return; - d->highlightRange = mode; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit highlightRangeModeChanged(); -} - -//###Possibly rename these properties, since they are very useful even without a highlight? -qreal QSGItemView::preferredHighlightBegin() const -{ - Q_D(const QSGItemView); - return d->highlightRangeStart; -} - -void QSGItemView::setPreferredHighlightBegin(qreal start) -{ - Q_D(QSGItemView); - d->highlightRangeStartValid = true; - if (d->highlightRangeStart == start) - return; - d->highlightRangeStart = start; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit preferredHighlightBeginChanged(); -} - -void QSGItemView::resetPreferredHighlightBegin() -{ - Q_D(QSGItemView); - d->highlightRangeStartValid = false; - if (d->highlightRangeStart == 0) - return; - d->highlightRangeStart = 0; - emit preferredHighlightBeginChanged(); -} - -qreal QSGItemView::preferredHighlightEnd() const -{ - Q_D(const QSGItemView); - return d->highlightRangeEnd; -} - -void QSGItemView::setPreferredHighlightEnd(qreal end) -{ - Q_D(QSGItemView); - d->highlightRangeEndValid = true; - if (d->highlightRangeEnd == end) - return; - d->highlightRangeEnd = end; - d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit preferredHighlightEndChanged(); -} - -void QSGItemView::resetPreferredHighlightEnd() -{ - Q_D(QSGItemView); - d->highlightRangeEndValid = false; - if (d->highlightRangeEnd == 0) - return; - d->highlightRangeEnd = 0; - emit preferredHighlightEndChanged(); -} - -int QSGItemView::highlightMoveDuration() const -{ - Q_D(const QSGItemView); - return d->highlightMoveDuration; -} - -void QSGItemView::setHighlightMoveDuration(int duration) -{ - Q_D(QSGItemView); - if (d->highlightMoveDuration != duration) { - d->highlightMoveDuration = duration; - emit highlightMoveDurationChanged(); - } -} - -void QSGItemViewPrivate::positionViewAtIndex(int index, int mode) -{ - Q_Q(QSGItemView); - if (!isValid()) - return; - if (mode < QSGItemView::Beginning || mode > QSGItemView::Contain) - return; - - applyPendingChanges(); - int idx = qMax(qMin(index, model->count()-1), 0); - - qreal pos = isContentFlowReversed() ? -position() - size() : position(); - FxViewItem *item = visibleItem(idx); - qreal maxExtent; - if (layoutOrientation() == Qt::Vertical) - maxExtent = -q->maxYExtent(); - else - maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent(); - if (!item) { - int itemPos = positionAt(idx); - changedVisibleIndex(idx); - // save the currently visible items in case any of them end up visible again - QList oldVisible = visibleItems; - visibleItems.clear(); - setPosition(qMin(qreal(itemPos), maxExtent)); - // now release the reference to all the old visible items. - for (int i = 0; i < oldVisible.count(); ++i) - releaseItem(oldVisible.at(i)); - item = visibleItem(idx); - } - if (item) { - const qreal itemPos = item->position(); - switch (mode) { - case QSGItemView::Beginning: - pos = itemPos; - if (index < 0 && header) - pos -= headerSize(); - break; - case QSGItemView::Center: - pos = itemPos - (size() - item->size())/2; - break; - case QSGItemView::End: - pos = itemPos - size() + item->size(); - if (index >= model->count() && footer) - pos += footerSize(); - break; - case QSGItemView::Visible: - if (itemPos > pos + size()) - pos = itemPos - size() + item->size(); - else if (item->endPosition() <= pos) - pos = itemPos; - break; - case QSGItemView::Contain: - if (item->endPosition() >= pos + size()) - pos = itemPos - size() + item->size(); - if (itemPos < pos) - pos = itemPos; - } - pos = qMin(pos, maxExtent); - qreal minExtent; - if (layoutOrientation() == Qt::Vertical) - minExtent = -q->minYExtent(); - else - minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent(); - pos = qMax(pos, minExtent); - moveReason = QSGItemViewPrivate::Other; - q->cancelFlick(); - setPosition(pos); - - if (highlight) { - if (autoHighlight) - resetHighlightPosition(); - updateHighlight(); - } - } - fixupPosition(); -} - -void QSGItemView::positionViewAtIndex(int index, int mode) -{ - Q_D(QSGItemView); - if (!d->isValid() || index < 0 || index >= d->model->count()) - return; - d->positionViewAtIndex(index, mode); -} - - -void QSGItemView::positionViewAtBeginning() -{ - Q_D(QSGItemView); - if (!d->isValid()) - return; - d->positionViewAtIndex(-1, Beginning); -} - -void QSGItemView::positionViewAtEnd() -{ - Q_D(QSGItemView); - if (!d->isValid()) - return; - d->positionViewAtIndex(d->model->count(), End); -} - -int QSGItemView::indexAt(qreal x, qreal y) const -{ - Q_D(const QSGItemView); - for (int i = 0; i < d->visibleItems.count(); ++i) { - const FxViewItem *item = d->visibleItems.at(i); - if (item->contains(x, y)) - return item->index; - } - - return -1; -} - -void QSGItemViewPrivate::applyPendingChanges() -{ - Q_Q(QSGItemView); - if (q->isComponentComplete() && currentChanges.hasPendingChanges()) - layout(); -} - -// for debugging only -void QSGItemViewPrivate::checkVisible() const -{ - int skip = 0; - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index == -1) { - ++skip; - } else if (item->index != visibleIndex + i - skip) { - qFatal("index %d %d %d", visibleIndex, i, item->index); - } - } -} - -void QSGItemViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_Q(QSGItemView); - QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); - if (!q->isComponentComplete()) - return; - - if (header && header->item == item) { - updateHeader(); - markExtentsDirty(); - if (!q->isMoving() && !q->isFlicking()) - fixupPosition(); - } else if (footer && footer->item == item) { - updateFooter(); - markExtentsDirty(); - if (!q->isMoving() && !q->isFlicking()) - fixupPosition(); - } - - if (currentItem && currentItem->item == item) - updateHighlight(); - if (trackedItem && trackedItem->item == item) - q->trackedPositionChanged(); -} - -void QSGItemView::destroyRemoved() -{ - Q_D(QSGItemView); - for (QList::Iterator it = d->visibleItems.begin(); - it != d->visibleItems.end();) { - FxViewItem *item = *it; - if (item->index == -1 && item->attached->delayRemove() == false) { - d->releaseItem(item); - it = d->visibleItems.erase(it); - } else { - ++it; - } - } - - // Correct the positioning of the items - d->updateSections(); - d->forceLayout = true; - d->layout(); -} - -void QSGItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) -{ - Q_D(QSGItemView); - if (reset) { - d->moveReason = QSGItemViewPrivate::SetIndex; - d->regenerate(); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->resetHighlightPosition(); - d->updateTrackedItem(); - } - d->moveReason = QSGItemViewPrivate::Other; - - emit countChanged(); - } else { - d->currentChanges.prepare(d->currentIndex, d->itemCount); - d->currentChanges.applyChanges(changeSet); - polish(); - } -} - -void QSGItemView::createdItem(int index, QSGItem *item) -{ - Q_D(QSGItemView); - if (d->requestedIndex != index) { - item->setParentItem(contentItem()); - d->unrequestedItems.insert(item, index); - d->repositionPackageItemAt(item, index); - } -} - -void QSGItemView::destroyingItem(QSGItem *item) -{ - Q_D(QSGItemView); - d->unrequestedItems.remove(item); -} - -void QSGItemView::animStopped() -{ - Q_D(QSGItemView); - d->bufferMode = QSGItemViewPrivate::NoBuffer; - if (d->haveHighlightRange && d->highlightRange == QSGItemView::StrictlyEnforceRange) - d->updateHighlight(); -} - - -void QSGItemView::trackedPositionChanged() -{ - Q_D(QSGItemView); - if (!d->trackedItem || !d->currentItem) - return; - if (d->moveReason == QSGItemViewPrivate::SetIndex) { - qreal trackedPos = d->trackedItem->position(); - qreal trackedSize = d->trackedItem->size(); - if (d->trackedItem != d->currentItem) { - trackedSize += d->currentItem->sectionSize(); - } - qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position(); - qreal pos = viewPos; - if (d->haveHighlightRange) { - if (trackedPos > pos + d->highlightRangeEnd - trackedSize) - pos = trackedPos - d->highlightRangeEnd + trackedSize; - if (trackedPos < pos + d->highlightRangeStart) - pos = trackedPos - d->highlightRangeStart; - if (d->highlightRange != StrictlyEnforceRange) { - if (pos > d->endPosition() - d->size()) - pos = d->endPosition() - d->size(); - if (pos < d->startPosition()) - pos = d->startPosition(); - } - } else { - qreal trackedEndPos = d->trackedItem->endPosition(); - qreal toItemPos = d->currentItem->position(); - qreal toItemEndPos = d->currentItem->endPosition(); - - if (d->header && d->showHeaderForIndex(d->currentIndex)) { - trackedPos -= d->headerSize(); - trackedEndPos -= d->headerSize(); - toItemPos -= d->headerSize(); - toItemEndPos -= d->headerSize(); - } else if (d->footer && d->showFooterForIndex(d->currentIndex)) { - trackedPos += d->footerSize(); - trackedEndPos += d->footerSize(); - toItemPos += d->footerSize(); - toItemEndPos += d->footerSize(); - } - - if (trackedPos < viewPos && toItemPos < viewPos) { - pos = qMax(trackedPos, toItemPos); - } else if (trackedEndPos >= viewPos + d->size() - && toItemEndPos >= viewPos + d->size()) { - if (trackedEndPos <= toItemEndPos) { - pos = trackedEndPos - d->size(); - if (trackedSize > d->size()) - pos = trackedPos; - } else { - pos = toItemEndPos - d->size(); - if (d->currentItem->size() > d->size()) - pos = d->currentItem->position(); - } - } - } - if (viewPos != pos) { - cancelFlick(); - d->calcVelocity = true; - d->setPosition(pos); - d->calcVelocity = false; - } - } -} - -void QSGItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_D(QSGItemView); - d->markExtentsDirty(); - QSGFlickable::geometryChanged(newGeometry, oldGeometry); -} - - -qreal QSGItemView::minYExtent() const -{ - Q_D(const QSGItemView); - if (d->layoutOrientation() == Qt::Horizontal) - return QSGFlickable::minYExtent(); - - if (d->vData.minExtentDirty) { - d->minExtent = d->vData.startMargin-d->startPosition(); - if (d->header) - d->minExtent += d->headerSize(); - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->minExtent += d->highlightRangeStart; - if (d->visibleItem(0)) - d->minExtent -= d->visibleItem(0)->sectionSize(); - d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd)); - } - d->vData.minExtentDirty = false; - } - - return d->minExtent; -} - -qreal QSGItemView::maxYExtent() const -{ - Q_D(const QSGItemView); - if (d->layoutOrientation() == Qt::Horizontal) - return height(); - - if (d->vData.maxExtentDirty) { - if (!d->model || !d->model->count()) { - d->maxExtent = d->header ? -d->headerSize() : 0; - d->maxExtent += height(); - } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart); - if (d->highlightRangeEnd != d->highlightRangeStart) - d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd)); - } else { - d->maxExtent = -(d->endPosition() - height()); - } - - if (d->footer) - d->maxExtent -= d->footerSize(); - d->maxExtent -= d->vData.endMargin; - qreal minY = minYExtent(); - if (d->maxExtent > minY) - d->maxExtent = minY; - d->vData.maxExtentDirty = false; - } - return d->maxExtent; -} - -qreal QSGItemView::minXExtent() const -{ - Q_D(const QSGItemView); - if (d->layoutOrientation() == Qt::Vertical) - return QSGFlickable::minXExtent(); - - if (d->hData.minExtentDirty) { - d->minExtent = -d->startPosition(); - qreal highlightStart; - qreal highlightEnd; - qreal endPositionFirstItem = 0; - if (d->isContentFlowReversed()) { - d->minExtent += d->hData.endMargin; - if (d->model && d->model->count()) - endPositionFirstItem = d->positionAt(d->model->count()-1); - else if (d->header) - d->minExtent += d->headerSize(); - highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size(); - highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size(); - if (d->footer) - d->minExtent += d->footerSize(); - qreal maxX = maxXExtent(); - if (d->minExtent < maxX) - d->minExtent = maxX; - } else { - d->minExtent += d->hData.startMargin; - endPositionFirstItem = d->endPositionAt(0); - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - if (d->header) - d->minExtent += d->headerSize(); - } - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->minExtent += highlightStart; - d->minExtent = d->isContentFlowReversed() - ? qMin(d->minExtent, endPositionFirstItem + highlightEnd) - : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd)); - } - d->hData.minExtentDirty = false; - } - - return d->minExtent; -} - -qreal QSGItemView::maxXExtent() const -{ - Q_D(const QSGItemView); - if (d->layoutOrientation() == Qt::Vertical) - return width(); - - if (d->hData.maxExtentDirty) { - qreal highlightStart; - qreal highlightEnd; - qreal lastItemPosition = 0; - d->maxExtent = 0; - if (d->isContentFlowReversed()) { - highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size(); - highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size(); - lastItemPosition = d->endPosition(); - } else { - highlightStart = d->highlightRangeStart; - highlightEnd = d->highlightRangeEnd; - if (d->model && d->model->count()) - lastItemPosition = d->positionAt(d->model->count()-1); - } - if (!d->model || !d->model->count()) { - if (!d->isContentFlowReversed()) - d->maxExtent = d->header ? -d->headerSize() : 0; - d->maxExtent += width(); - } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { - d->maxExtent = -(lastItemPosition - highlightStart); - if (highlightEnd != highlightStart) { - d->maxExtent = d->isContentFlowReversed() - ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd)) - : qMin(d->maxExtent, -(d->endPosition() - highlightEnd)); - } - } else { - d->maxExtent = -(d->endPosition() - width()); - } - if (d->isContentFlowReversed()) { - if (d->header) - d->maxExtent -= d->headerSize(); - d->maxExtent -= d->hData.startMargin; - } else { - if (d->footer) - d->maxExtent -= d->footerSize(); - d->maxExtent -= d->hData.endMargin; - qreal minX = minXExtent(); - if (d->maxExtent > minX) - d->maxExtent = minX; - } - d->hData.maxExtentDirty = false; - } - - return d->maxExtent; -} - -void QSGItemView::setContentX(qreal pos) -{ - Q_D(QSGItemView); - // Positioning the view manually should override any current movement state - d->moveReason = QSGItemViewPrivate::Other; - QSGFlickable::setContentX(pos); -} - -void QSGItemView::setContentY(qreal pos) -{ - Q_D(QSGItemView); - // Positioning the view manually should override any current movement state - d->moveReason = QSGItemViewPrivate::Other; - QSGFlickable::setContentY(pos); -} - -qreal QSGItemView::xOrigin() const -{ - Q_D(const QSGItemView); - if (d->isContentFlowReversed()) - return -maxXExtent() + d->size() - d->hData.startMargin; - else - return -minXExtent() + d->hData.startMargin; -} - -void QSGItemView::updatePolish() -{ - Q_D(QSGItemView); - QSGFlickable::updatePolish(); - d->layout(); -} - -void QSGItemView::componentComplete() -{ - Q_D(QSGItemView); - if (d->model && d->ownModel) - static_cast(d->model.data())->componentComplete(); - - QSGFlickable::componentComplete(); - - updateSections(); - d->updateHeader(); - d->updateFooter(); - d->updateViewport(); - d->setPosition(d->contentStartPosition()); - if (d->isValid()) { - d->refill(); - d->moveReason = QSGItemViewPrivate::SetIndex; - if (d->currentIndex < 0 && !d->currentIndexCleared) - d->updateCurrent(0); - else - d->updateCurrent(d->currentIndex); - if (d->highlight && d->currentItem) { - if (d->autoHighlight) - d->resetHighlightPosition(); - d->updateTrackedItem(); - } - d->moveReason = QSGItemViewPrivate::Other; - d->fixupPosition(); - } - if (d->model && d->model->count()) - emit countChanged(); -} - - - -QSGItemViewPrivate::QSGItemViewPrivate() - : itemCount(0) - , buffer(0), bufferMode(BufferBefore | BufferAfter) - , layoutDirection(Qt::LeftToRight) - , moveReason(Other) - , visibleIndex(0) - , currentIndex(-1), currentItem(0) - , trackedItem(0), requestedIndex(-1) - , highlightComponent(0), highlight(0) - , highlightRange(QSGItemView::NoHighlightRange) - , highlightRangeStart(0), highlightRangeEnd(0) - , highlightMoveDuration(150) - , headerComponent(0), header(0), footerComponent(0), footer(0) - , minExtent(0), maxExtent(0) - , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false) - , inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) - , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) -{ -} - -bool QSGItemViewPrivate::isValid() const -{ - return model && model->count() && model->isValid(); -} - -qreal QSGItemViewPrivate::position() const -{ - Q_Q(const QSGItemView); - return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX(); -} - -qreal QSGItemViewPrivate::size() const -{ - Q_Q(const QSGItemView); - return layoutOrientation() == Qt::Vertical ? q->height() : q->width(); -} - -qreal QSGItemViewPrivate::startPosition() const -{ - return isContentFlowReversed() ? -lastPosition() : originPosition(); -} - -qreal QSGItemViewPrivate::endPosition() const -{ - return isContentFlowReversed() ? -originPosition() : lastPosition(); -} - -qreal QSGItemViewPrivate::contentStartPosition() const -{ - Q_Q(const QSGItemView); - qreal pos = -headerSize(); - if (layoutOrientation() == Qt::Vertical) - pos -= vData.startMargin; - else if (isContentFlowReversed()) - pos -= hData.endMargin; - else - pos -= hData.startMargin; - - return pos; -} - -int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const -{ - if (visibleItems.count()) { - int i = visibleItems.count() - 1; - while (i > 0 && visibleItems.at(i)->index == -1) - --i; - if (visibleItems.at(i)->index != -1) - return visibleItems.at(i)->index; - } - return defaultValue; -} - -FxViewItem *QSGItemViewPrivate::visibleItem(int modelIndex) const { - if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { - for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index == modelIndex) - return item; - } - } - return 0; -} - -FxViewItem *QSGItemViewPrivate::firstVisibleItem() const { - const qreal pos = isContentFlowReversed() ? -position()-size() : position(); - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index != -1 && item->endPosition() > pos) - return item; - } - return visibleItems.count() ? visibleItems.first() : 0; -} - -// Map a model index to visibleItems list index. -// These may differ if removed items are still present in the visible list, -// e.g. doing a removal animation -int QSGItemViewPrivate::mapFromModel(int modelIndex) const -{ - if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) - return -1; - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index == modelIndex) - return i; - if (item->index > modelIndex) - return -1; - } - return -1; // Not in visibleList -} - -void QSGItemViewPrivate::init() -{ - Q_Q(QSGItemView); - QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true; - q->setFlag(QSGItem::ItemIsFocusScope); - addItemChangeListener(this, Geometry); - QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); - q->setFlickableDirection(QSGFlickable::VerticalFlick); -} - -void QSGItemViewPrivate::updateCurrent(int modelIndex) -{ - Q_Q(QSGItemView); - applyPendingChanges(); - - if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { - if (currentItem) { - currentItem->attached->setIsCurrentItem(false); - releaseItem(currentItem); - currentItem = 0; - currentIndex = modelIndex; - emit q->currentIndexChanged(); - updateHighlight(); - } else if (currentIndex != modelIndex) { - currentIndex = modelIndex; - emit q->currentIndexChanged(); - } - return; - } - - if (currentItem && currentIndex == modelIndex) { - updateHighlight(); - return; - } - - FxViewItem *oldCurrentItem = currentItem; - currentIndex = modelIndex; - currentItem = createItem(modelIndex); - if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) - oldCurrentItem->attached->setIsCurrentItem(false); - if (currentItem) { - currentItem->item->setFocus(true); - currentItem->attached->setIsCurrentItem(true); - initializeCurrentItem(); - } - - updateHighlight(); - emit q->currentIndexChanged(); - releaseItem(oldCurrentItem); -} - -void QSGItemViewPrivate::clear() -{ - currentChanges.reset(); - timeline.clear(); - - for (int i = 0; i < visibleItems.count(); ++i) - releaseItem(visibleItems.at(i)); - visibleItems.clear(); - visibleIndex = 0; - - releaseItem(currentItem); - currentItem = 0; - createHighlight(); - trackedItem = 0; - - markExtentsDirty(); - itemCount = 0; -} - - -void QSGItemViewPrivate::mirrorChange() -{ - Q_Q(QSGItemView); - regenerate(); - emit q->effectiveLayoutDirectionChanged(); -} - -void QSGItemViewPrivate::refill() -{ - if (isContentFlowReversed()) - refill(-position()-size(), -position()); - else - refill(position(), position()+size()); -} - -void QSGItemViewPrivate::refill(qreal from, qreal to, bool doBuffer) -{ - Q_Q(QSGItemView); - if (!isValid() || !q->isComponentComplete()) - return; - - currentChanges.reset(); - - int prevCount = itemCount; - itemCount = model->count(); - qreal bufferFrom = from - buffer; - qreal bufferTo = to + buffer; - qreal fillFrom = from; - qreal fillTo = to; - if (doBuffer && (bufferMode & BufferAfter)) - fillTo = bufferTo; - if (doBuffer && (bufferMode & BufferBefore)) - fillFrom = bufferFrom; - - // Item creation and release is staggered in order to avoid - // creating/releasing multiple items in one frame - // while flicking (as much as possible). - - bool changed = addVisibleItems(fillFrom, fillTo, doBuffer); - - if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create - if (removeNonVisibleItems(bufferFrom, bufferTo)) - changed = true; - deferredRelease = false; - } else { - deferredRelease = true; - } - - if (changed) { - markExtentsDirty(); - visibleItemsChanged(); - } else if (!doBuffer && buffer && bufferMode != NoBuffer) { - refill(from, to, true); - } - - lazyRelease = false; - if (prevCount != itemCount) - emit q->countChanged(); -} - -void QSGItemViewPrivate::regenerate() -{ - Q_Q(QSGItemView); - if (q->isComponentComplete()) { - currentChanges.reset(); - delete header; - header = 0; - delete footer; - footer = 0; - updateHeader(); - updateFooter(); - clear(); - updateViewport(); - setPosition(contentStartPosition()); - refill(); - updateCurrent(currentIndex); - } -} - -void QSGItemViewPrivate::updateViewport() -{ - Q_Q(QSGItemView); - if (isValid()) { - if (layoutOrientation() == Qt::Vertical) - q->setContentHeight(endPosition() - startPosition()); - else - q->setContentWidth(endPosition() - startPosition()); - } -} - -void QSGItemViewPrivate::layout() -{ - Q_Q(QSGItemView); - if (inApplyModelChanges) - return; - - if (!isValid() && !visibleItems.count()) { - clear(); - setPosition(contentStartPosition()); - return; - } - - if (!applyModelChanges() && !forceLayout) - return; - forceLayout = false; - - layoutVisibleItems(); - refill(); - - markExtentsDirty(); - - updateHighlight(); - - if (!q->isMoving() && !q->isFlicking()) { - fixupPosition(); - refill(); - } - - updateHeader(); - updateFooter(); - updateViewport(); - updateUnrequestedPositions(); -} - -bool QSGItemViewPrivate::applyModelChanges() -{ - Q_Q(QSGItemView); - if (!q->isComponentComplete() || !currentChanges.hasPendingChanges() || inApplyModelChanges) - return false; - inApplyModelChanges = true; - - updateUnrequestedIndexes(); - moveReason = QSGItemViewPrivate::Other; - - int prevCount = itemCount; - bool removedVisible = false; - bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty() - || !currentChanges.pendingChanges.inserts().isEmpty(); - - FxViewItem *firstVisible = firstVisibleItem(); - FxViewItem *origVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0; - int firstItemIndex = firstVisible ? firstVisible->index : -1; - qreal removedBeforeFirstVisibleBy = 0; - - const QVector &removals = currentChanges.pendingChanges.removes(); - for (int i=0; i::Iterator it = visibleItems.begin(); - while (it != visibleItems.end()) { - FxViewItem *item = *it; - if (item->index == -1 || item->index < removals[i].index) { - // already removed, or before removed items - if (item->index < removals[i].index && !removedVisible) - removedVisible = true; - ++it; - } else if (item->index >= removals[i].index + removals[i].count) { - // after removed items - item->index -= removals[i].count; - ++it; - } else { - // removed item - removedVisible = true; - if (!removals[i].isMove()) - item->attached->emitRemove(); - - if (item->attached->delayRemove() && !removals[i].isMove()) { - item->index = -1; - QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection); - ++it; - } else { - if (firstVisible && item->position() < firstVisible->position() && item != visibleItems.first()) - removedBeforeFirstVisibleBy += item->size(); - if (removals[i].isMove()) { - currentChanges.removedItems.insert(removals[i].moveKey(item->index), item); - } else { - if (item == firstVisible) - firstVisible = 0; - currentChanges.removedItems.insertMulti(QDeclarativeChangeSet::MoveKey(), item); - } - it = visibleItems.erase(it); - } - } - } - - } - if (!removals.isEmpty()) - updateVisibleIndex(); - - const QVector &insertions = currentChanges.pendingChanges.inserts(); - bool addedVisible = false; - InsertionsResult insertResult; - bool allInsertionsBeforeVisible = true; - - for (int i=0; i= visibleIndex) - allInsertionsBeforeVisible = false; - if (wasEmpty && !visibleItems.isEmpty()) - resetFirstItemPosition(); - itemCount += insertions[i].count; - } - for (int i=0; iattached->emitAdd(); - - // if the first visible item has moved, ensure another one takes its place - // so that we avoid shifting all content forwards - // (if an item is removed from before the first visible, the first visible should not move upwards) - if (firstVisible && firstItemIndex >= 0) { - bool found = false; - for (int i=0; iindex == firstItemIndex) { - // an item has moved backwards up to the first visible's position - resetItemPosition(insertResult.movedBackwards[i], firstVisible); - insertResult.movedBackwards.removeAt(i); - found = true; - break; - } - } - if (!found && !allInsertionsBeforeVisible) { - // first visible item has moved forward, another visible item takes its place - FxViewItem *item = visibleItem(firstItemIndex); - if (item) - resetItemPosition(item, firstVisible); - } - } - - // Ensure we don't cause an ugly list scroll - if (firstVisible && visibleItems.count() && visibleItems.first() != firstVisible) { - // ensure first item is placed at correct postion if moving backward - // since it will be used to position all subsequent items - if (insertResult.movedBackwards.count() && origVisibleItemsFirst) - resetItemPosition(visibleItems.first(), origVisibleItemsFirst); - qreal moveBackwardsBy = insertResult.sizeAddedBeforeVisible; - for (int i=0; isize(); - moveItemBy(visibleItems.first(), removedBeforeFirstVisibleBy, moveBackwardsBy); - } - - // Whatever removed/moved items remain are no longer visible items. - for (QHash::Iterator it = currentChanges.removedItems.begin(); - it != currentChanges.removedItems.end(); ++it) { - releaseItem(it.value()); - } - currentChanges.removedItems.clear(); - - if (currentChanges.currentChanged) { - if (currentChanges.currentRemoved && currentItem) { - currentItem->attached->setIsCurrentItem(false); - releaseItem(currentItem); - currentItem = 0; - } - if (!currentIndexCleared) - updateCurrent(currentChanges.newCurrentIndex); - } - currentChanges.reset(); - - updateSections(); - if (prevCount != itemCount) - emit q->countChanged(); - - bool visibleAffected = removedVisible || addedVisible || !currentChanges.pendingChanges.changes().isEmpty(); - if (!visibleAffected && viewportChanged) - updateViewport(); - - inApplyModelChanges = false; - return visibleAffected; -} - -FxViewItem *QSGItemViewPrivate::createItem(int modelIndex) -{ - Q_Q(QSGItemView); - - requestedIndex = modelIndex; - FxViewItem *viewItem = 0; - - if (QSGItem *item = model->item(modelIndex, false)) { - viewItem = newViewItem(modelIndex, item); - if (viewItem) { - viewItem->index = modelIndex; - if (model->completePending()) { - // complete - viewItem->item->setZ(1); - QDeclarative_setParent_noEvent(viewItem->item, q->contentItem()); - viewItem->item->setParentItem(q->contentItem()); - model->completeItem(); - } else { - QDeclarative_setParent_noEvent(viewItem->item, q->contentItem()); - viewItem->item->setParentItem(q->contentItem()); - } - // do other set up for the new item that should not happen - // until after bindings are evaluated - initializeViewItem(viewItem); - - unrequestedItems.remove(viewItem->item); - } - } - requestedIndex = -1; - return viewItem; -} - - -void QSGItemViewPrivate::releaseItem(FxViewItem *item) -{ - Q_Q(QSGItemView); - if (!item || !model) - return; - if (trackedItem == item) - trackedItem = 0; - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item); - itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry); - if (model->release(item->item) == 0) { - // item was not destroyed, and we no longer reference it. - unrequestedItems.insert(item->item, model->indexOf(item->item, q)); - } - delete item; -} - -QSGItem *QSGItemViewPrivate::createHighlightItem() -{ - return createComponentItem(highlightComponent, true, true); -} - -QSGItem *QSGItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault) -{ - Q_Q(QSGItemView); - - QSGItem *item = 0; - if (component) { - QDeclarativeContext *creationContext = component->creationContext(); - QDeclarativeContext *context = new QDeclarativeContext( - creationContext ? creationContext : qmlContext(q)); - QObject *nobj = component->create(context); - if (nobj) { - QDeclarative_setParent_noEvent(context, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete context; - } - } else if (createDefault) { - item = new QSGItem; - } - if (item) { - QDeclarative_setParent_noEvent(item, q->contentItem()); - item->setParentItem(q->contentItem()); - if (receiveItemGeometryChanges) { - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - } - } - return item; -} - -void QSGItemViewPrivate::updateTrackedItem() -{ - Q_Q(QSGItemView); - FxViewItem *item = currentItem; - if (highlight) - item = highlight; - trackedItem = item; - - if (trackedItem) - q->trackedPositionChanged(); -} - -void QSGItemViewPrivate::updateUnrequestedIndexes() -{ - Q_Q(QSGItemView); - for (QHash::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) - *it = model->indexOf(it.key(), q); -} - -void QSGItemViewPrivate::updateUnrequestedPositions() -{ - for (QHash::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) - repositionPackageItemAt(it.key(), it.value()); -} - -void QSGItemViewPrivate::updateVisibleIndex() -{ - visibleIndex = 0; - for (QList::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) { - if ((*it)->index != -1) { - visibleIndex = (*it)->index; - break; - } - } -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgitemview_p.h b/src/declarative/items/qsgitemview_p.h deleted file mode 100644 index 7b8efbbda0..0000000000 --- a/src/declarative/items/qsgitemview_p.h +++ /dev/null @@ -1,294 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGITEMVIEW_P_H -#define QSGITEMVIEW_P_H - -#include "qsgflickable_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QDeclarativeChangeSet; - -class QSGItemViewPrivate; - -class Q_AUTOTEST_EXPORT QSGItemView : public QSGFlickable -{ - Q_OBJECT - - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) - Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged) - - Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) - Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) - - Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) - Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) - - Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged) - Q_PROPERTY(QSGItem *headerItem READ headerItem NOTIFY headerItemChanged) - Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged) - Q_PROPERTY(QSGItem *footerItem READ footerItem NOTIFY footerItemChanged) - - Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) - Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) - Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem NOTIFY highlightFollowsCurrentItemChanged) - Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) - Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin) - Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) - Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) - - Q_ENUMS(HighlightRangeMode) - Q_ENUMS(PositionMode) - -public: - QSGItemView(QSGFlickablePrivate &dd, QSGItem *parent = 0); - ~QSGItemView(); - - QVariant model() const; - void setModel(const QVariant &); - - QDeclarativeComponent *delegate() const; - void setDelegate(QDeclarativeComponent *); - - int count() const; - - int currentIndex() const; - void setCurrentIndex(int idx); - - QSGItem *currentItem() const; - - bool isWrapEnabled() const; - void setWrapEnabled(bool); - - int cacheBuffer() const; - void setCacheBuffer(int); - - Qt::LayoutDirection layoutDirection() const; - void setLayoutDirection(Qt::LayoutDirection); - Qt::LayoutDirection effectiveLayoutDirection() const; - - QDeclarativeComponent *footer() const; - void setFooter(QDeclarativeComponent *); - QSGItem *footerItem() const; - - QDeclarativeComponent *header() const; - void setHeader(QDeclarativeComponent *); - QSGItem *headerItem() const; - - QDeclarativeComponent *highlight() const; - void setHighlight(QDeclarativeComponent *); - - QSGItem *highlightItem() const; - - bool highlightFollowsCurrentItem() const; - virtual void setHighlightFollowsCurrentItem(bool); - - enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; - HighlightRangeMode highlightRangeMode() const; - void setHighlightRangeMode(HighlightRangeMode mode); - - qreal preferredHighlightBegin() const; - void setPreferredHighlightBegin(qreal); - void resetPreferredHighlightBegin(); - - qreal preferredHighlightEnd() const; - void setPreferredHighlightEnd(qreal); - void resetPreferredHighlightEnd(); - - int highlightMoveDuration() const; - virtual void setHighlightMoveDuration(int); - - enum PositionMode { Beginning, Center, End, Visible, Contain }; - - Q_INVOKABLE void positionViewAtIndex(int index, int mode); - Q_INVOKABLE int indexAt(qreal x, qreal y) const; - Q_INVOKABLE void positionViewAtBeginning(); - Q_INVOKABLE void positionViewAtEnd(); - - virtual void setContentX(qreal pos); - virtual void setContentY(qreal pos); - virtual qreal xOrigin() const; - -signals: - void modelChanged(); - void delegateChanged(); - void countChanged(); - void currentIndexChanged(); - - void keyNavigationWrapsChanged(); - void cacheBufferChanged(); - - void layoutDirectionChanged(); - void effectiveLayoutDirectionChanged(); - - void headerChanged(); - void footerChanged(); - void headerItemChanged(); - void footerItemChanged(); - - void highlightChanged(); - void highlightItemChanged(); - void highlightFollowsCurrentItemChanged(); - void highlightRangeModeChanged(); - void preferredHighlightBeginChanged(); - void preferredHighlightEndChanged(); - void highlightMoveDurationChanged(); - -protected: - virtual void updatePolish(); - virtual void componentComplete(); - virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - virtual qreal minYExtent() const; - virtual qreal maxYExtent() const; - virtual qreal minXExtent() const; - virtual qreal maxXExtent() const; - -protected slots: - virtual void updateSections() {} - void destroyRemoved(); - void createdItem(int index, QSGItem *item); - void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); - void destroyingItem(QSGItem *item); - void animStopped(); - void trackedPositionChanged(); - - - -private: - Q_DECLARE_PRIVATE(QSGItemView) -}; - - -class Q_AUTOTEST_EXPORT QSGItemViewAttached : public QObject -{ - Q_OBJECT - - Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) - Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged) - - Q_PROPERTY(QString section READ section NOTIFY sectionChanged) - Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged) - Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged) - -public: - QSGItemViewAttached(QObject *parent) - : QObject(parent), m_isCurrent(false), m_delayRemove(false) {} - ~QSGItemViewAttached() {} - - bool isCurrentItem() const { return m_isCurrent; } - void setIsCurrentItem(bool c) { - if (m_isCurrent != c) { - m_isCurrent = c; - emit currentItemChanged(); - } - } - - bool delayRemove() const { return m_delayRemove; } - void setDelayRemove(bool delay) { - if (m_delayRemove != delay) { - m_delayRemove = delay; - emit delayRemoveChanged(); - } - } - - QString section() const { return m_section; } - void setSection(const QString §) { - if (m_section != sect) { - m_section = sect; - emit sectionChanged(); - } - } - - QString prevSection() const { return m_prevSection; } - void setPrevSection(const QString §) { - if (m_prevSection != sect) { - m_prevSection = sect; - emit prevSectionChanged(); - } - } - - QString nextSection() const { return m_nextSection; } - void setNextSection(const QString §) { - if (m_nextSection != sect) { - m_nextSection = sect; - emit nextSectionChanged(); - } - } - - void emitAdd() { emit add(); } - void emitRemove() { emit remove(); } - -signals: - void currentItemChanged(); - void delayRemoveChanged(); - - void add(); - void remove(); - - void sectionChanged(); - void prevSectionChanged(); - void nextSectionChanged(); - -public: - bool m_isCurrent : 1; - bool m_delayRemove : 1; - - // current only used by list view - mutable QString m_section; - QString m_prevSection; - QString m_nextSection; -}; - - -QT_END_NAMESPACE -QT_END_HEADER - -#endif // QSGITEMVIEW_P_H - diff --git a/src/declarative/items/qsgitemview_p_p.h b/src/declarative/items/qsgitemview_p_p.h deleted file mode 100644 index 5eaa84c6b2..0000000000 --- a/src/declarative/items/qsgitemview_p_p.h +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGITEMVIEW_P_P_H -#define QSGITEMVIEW_P_P_H - -#include "qsgitemview_p.h" -#include "qsgflickable_p_p.h" -#include "qsgvisualdatamodel_p.h" -#include - - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class FxViewItem -{ -public: - FxViewItem(QSGItem *, bool own); - ~FxViewItem(); - - // these are positions and sizes along the current direction of scrolling/flicking - virtual qreal position() const = 0; - virtual qreal endPosition() const = 0; - virtual qreal size() const = 0; - virtual qreal sectionSize() const = 0; - - virtual bool contains(qreal x, qreal y) const = 0; - - QSGItem *item; - bool ownItem; - int index; - QSGItemViewAttached *attached; -}; - -class QSGItemViewChangeSet -{ -public: - QSGItemViewChangeSet(); - - bool hasPendingChanges() const; - void prepare(int currentIndex, int count); - void reset(); - - void applyChanges(const QDeclarativeChangeSet &changeSet); - - int itemCount; - int newCurrentIndex; - QDeclarativeChangeSet pendingChanges; - QHash removedItems; - - bool active : 1; - bool currentChanged : 1; - bool currentRemoved : 1; -}; - -class QSGItemViewPrivate : public QSGFlickablePrivate -{ - Q_DECLARE_PUBLIC(QSGItemView) -public: - QSGItemViewPrivate(); - - struct InsertionsResult { - QList addedItems; - QList movedBackwards; - qreal sizeAddedBeforeVisible; - - InsertionsResult() : sizeAddedBeforeVisible(0) {} - }; - - enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; - enum MovementReason { Other, SetIndex, Mouse }; - - bool isValid() const; - qreal position() const; - qreal size() const; - qreal startPosition() const; - qreal endPosition() const; - qreal contentStartPosition() const; - int findLastVisibleIndex(int defaultValue = -1) const; - FxViewItem *visibleItem(int modelIndex) const; - FxViewItem *firstVisibleItem() const; - int mapFromModel(int modelIndex) const; - - virtual void init(); - virtual void clear(); - virtual void updateViewport(); - - void regenerate(); - void layout(); - void refill(); - void refill(qreal from, qreal to, bool doBuffer = false); - void mirrorChange(); - - FxViewItem *createItem(int modelIndex); - virtual void releaseItem(FxViewItem *item); - - QSGItem *createHighlightItem(); - QSGItem *createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault = false); - - void updateCurrent(int modelIndex); - void updateTrackedItem(); - void updateUnrequestedIndexes(); - void updateUnrequestedPositions(); - void updateVisibleIndex(); - void positionViewAtIndex(int index, int mode); - void applyPendingChanges(); - bool applyModelChanges(); - - void checkVisible() const; - - void markExtentsDirty() { - if (layoutOrientation() == Qt::Vertical) - vData.markExtentsDirty(); - else - hData.markExtentsDirty(); - } - - QDeclarativeGuard model; - QVariant modelVariant; - int itemCount; - int buffer; - int bufferMode; - Qt::LayoutDirection layoutDirection; - - MovementReason moveReason; - - QList visibleItems; - int visibleIndex; - int currentIndex; - FxViewItem *currentItem; - FxViewItem *trackedItem; - QHash unrequestedItems; - int requestedIndex; - QSGItemViewChangeSet currentChanges; - - // XXX split into struct - QDeclarativeComponent *highlightComponent; - FxViewItem *highlight; - int highlightRange; // enum value - qreal highlightRangeStart; - qreal highlightRangeEnd; - int highlightMoveDuration; - - QDeclarativeComponent *headerComponent; - FxViewItem *header; - QDeclarativeComponent *footerComponent; - FxViewItem *footer; - - mutable qreal minExtent; - mutable qreal maxExtent; - - bool ownModel : 1; - bool wrap : 1; - bool lazyRelease : 1; - bool deferredRelease : 1; - bool inApplyModelChanges : 1; - bool inViewportMoved : 1; - bool forceLayout : 1; - bool currentIndexCleared : 1; - bool haveHighlightRange : 1; - bool autoHighlight : 1; - bool highlightRangeStartValid : 1; - bool highlightRangeEndValid : 1; - -protected: - virtual Qt::Orientation layoutOrientation() const = 0; - virtual bool isContentFlowReversed() const = 0; - - virtual qreal positionAt(int index) const = 0; - virtual qreal endPositionAt(int index) const = 0; - virtual qreal originPosition() const = 0; - virtual qreal lastPosition() const = 0; - - virtual qreal headerSize() const = 0; - virtual qreal footerSize() const = 0; - virtual bool showHeaderForIndex(int index) const = 0; - virtual bool showFooterForIndex(int index) const = 0; - virtual void updateHeader() = 0; - virtual void updateFooter() = 0; - - virtual void createHighlight() = 0; - virtual void updateHighlight() = 0; - virtual void resetHighlightPosition() = 0; - - virtual void setPosition(qreal pos) = 0; - virtual void fixupPosition() = 0; - - virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer) = 0; - virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) = 0; - virtual void visibleItemsChanged() = 0; - - virtual FxViewItem *newViewItem(int index, QSGItem *item) = 0; - virtual void repositionPackageItemAt(QSGItem *item, int index) = 0; - virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem) = 0; - virtual void resetFirstItemPosition() = 0; - virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards) = 0; - - virtual void layoutVisibleItems() = 0; - virtual void changedVisibleIndex(int newIndex) = 0; - virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *, InsertionsResult *) = 0; - - virtual void initializeViewItem(FxViewItem *) {} - virtual void initializeCurrentItem() {} - virtual void updateSections() {} - - virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); -}; - - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QSGITEMVIEW_P_P_H diff --git a/src/declarative/items/qsglistview.cpp b/src/declarative/items/qsglistview.cpp deleted file mode 100644 index 5ea9efe274..0000000000 --- a/src/declarative/items/qsglistview.cpp +++ /dev/null @@ -1,2500 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsglistview_p.h" -#include "qsgitemview_p_p.h" -#include "qsgvisualitemmodel_p.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include "qplatformdefs.h" - -QT_BEGIN_NAMESPACE - -#ifndef QML_FLICK_SNAPONETHRESHOLD -#define QML_FLICK_SNAPONETHRESHOLD 30 -#endif - -class FxListItemSG; - -class QSGListViewPrivate : public QSGItemViewPrivate -{ - Q_DECLARE_PUBLIC(QSGListView) -public: - static QSGListViewPrivate* get(QSGListView *item) { return item->d_func(); } - - virtual Qt::Orientation layoutOrientation() const; - virtual bool isContentFlowReversed() const; - bool isRightToLeft() const; - - virtual qreal positionAt(int index) const; - virtual qreal endPositionAt(int index) const; - virtual qreal originPosition() const; - virtual qreal lastPosition() const; - - FxViewItem *itemBefore(int modelIndex) const; - QString sectionAt(int modelIndex); - qreal snapPosAt(qreal pos); - FxViewItem *snapItemAt(qreal pos); - - virtual void init(); - virtual void clear(); - - virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer); - virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo); - virtual void visibleItemsChanged(); - - virtual FxViewItem *newViewItem(int index, QSGItem *item); - virtual void initializeViewItem(FxViewItem *item); - virtual void releaseItem(FxViewItem *item); - virtual void repositionPackageItemAt(QSGItem *item, int index); - virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem); - virtual void resetFirstItemPosition(); - virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards); - - virtual void createHighlight(); - virtual void updateHighlight(); - virtual void resetHighlightPosition(); - - virtual void setPosition(qreal pos); - virtual void layoutVisibleItems(); - bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *firstVisible, InsertionsResult *); - - virtual void updateSections(); - QSGItem *getSectionItem(const QString §ion); - void releaseSectionItem(QSGItem *item); - void updateInlineSection(FxListItemSG *); - void updateCurrentSection(); - void updateStickySections(); - - virtual qreal headerSize() const; - virtual qreal footerSize() const; - virtual bool showHeaderForIndex(int index) const; - virtual bool showFooterForIndex(int index) const; - virtual void updateHeader(); - virtual void updateFooter(); - - virtual void changedVisibleIndex(int newIndex); - virtual void initializeCurrentItem(); - - void updateAverage(); - - void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); - virtual void fixupPosition(); - virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent); - virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity); - - QSGListView::Orientation orient; - qreal visiblePos; - qreal averageSize; - qreal spacing; - QSGListView::SnapMode snapMode; - - QSmoothedAnimation *highlightPosAnimator; - QSmoothedAnimation *highlightSizeAnimator; - qreal highlightMoveSpeed; - qreal highlightResizeSpeed; - int highlightResizeDuration; - - QSGViewSection *sectionCriteria; - QString currentSection; - static const int sectionCacheSize = 5; - QSGItem *sectionCache[sectionCacheSize]; - QSGItem *currentSectionItem; - QString currentStickySection; - QSGItem *nextSectionItem; - QString nextStickySection; - QString lastVisibleSection; - QString nextSection; - - qreal overshootDist; - bool correctFlick : 1; - bool inFlickCorrection : 1; - - QSGListViewPrivate() - : orient(QSGListView::Vertical) - , visiblePos(0) - , averageSize(100.0), spacing(0.0) - , snapMode(QSGListView::NoSnap) - , highlightPosAnimator(0), highlightSizeAnimator(0) - , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1) - , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0) - , overshootDist(0.0), correctFlick(false), inFlickCorrection(false) - {} - - friend class QSGViewSection; -}; - -//---------------------------------------------------------------------------- - -QSGViewSection::QSGViewSection(QSGListView *parent) - : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels) - , m_view(parent ? QSGListViewPrivate::get(parent) : 0) -{ -} - -void QSGViewSection::setProperty(const QString &property) -{ - if (property != m_property) { - m_property = property; - emit propertyChanged(); - m_view->updateSections(); - } -} - -void QSGViewSection::setCriteria(QSGViewSection::SectionCriteria criteria) -{ - if (criteria != m_criteria) { - m_criteria = criteria; - emit criteriaChanged(); - m_view->updateSections(); - } -} - -void QSGViewSection::setDelegate(QDeclarativeComponent *delegate) -{ - if (delegate != m_delegate) { - m_delegate = delegate; - emit delegateChanged(); - m_view->updateSections(); - } -} - -QString QSGViewSection::sectionString(const QString &value) -{ - if (m_criteria == FirstCharacter) - return value.isEmpty() ? QString() : value.at(0); - else - return value; -} - -void QSGViewSection::setLabelPositioning(int l) -{ - if (m_labelPositioning != l) { - m_labelPositioning = l; - emit labelPositioningChanged(); - m_view->updateSections(); - } -} - -//---------------------------------------------------------------------------- - -class FxListItemSG : public FxViewItem -{ -public: - FxListItemSG(QSGItem *i, QSGListView *v, bool own) : FxViewItem(i, own), section(0), view(v) { - attached = static_cast(qmlAttachedPropertiesObject(item)); - if (attached) - static_cast(attached)->setView(view); - } - - ~FxListItemSG() {} - - qreal position() const { - if (section) { - if (view->orientation() == QSGListView::Vertical) - return section->y(); - else - return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x()); - } else { - return itemPosition(); - } - } - qreal itemPosition() const { - if (view->orientation() == QSGListView::Vertical) - return item->y(); - else - return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x()); - } - qreal size() const { - if (section) - return (view->orientation() == QSGListView::Vertical ? item->height()+section->height() : item->width()+section->width()); - else - return (view->orientation() == QSGListView::Vertical ? item->height() : item->width()); - } - qreal itemSize() const { - return (view->orientation() == QSGListView::Vertical ? item->height() : item->width()); - } - qreal sectionSize() const { - if (section) - return (view->orientation() == QSGListView::Vertical ? section->height() : section->width()); - return 0.0; - } - qreal endPosition() const { - if (view->orientation() == QSGListView::Vertical) { - return item->y() + item->height(); - } else { - return (view->effectiveLayoutDirection() == Qt::RightToLeft - ? -item->x() - : item->x() + item->width()); - } - } - void setPosition(qreal pos) { - if (view->orientation() == QSGListView::Vertical) { - if (section) { - section->setY(pos); - pos += section->height(); - } - item->setY(pos); - } else { - if (view->effectiveLayoutDirection() == Qt::RightToLeft) { - if (section) { - section->setX(-section->width()-pos); - pos += section->width(); - } - item->setX(-item->width()-pos); - } else { - if (section) { - section->setX(pos); - pos += section->width(); - } - item->setX(pos); - } - } - } - void setSize(qreal size) { - if (view->orientation() == QSGListView::Vertical) - item->setHeight(size); - else - item->setWidth(size); - } - bool contains(qreal x, qreal y) const { - return (x >= item->x() && x < item->x() + item->width() && - y >= item->y() && y < item->y() + item->height()); - } - - QSGItem *section; - QSGListView *view; -}; - -//---------------------------------------------------------------------------- - -bool QSGListViewPrivate::isContentFlowReversed() const -{ - return isRightToLeft(); -} - -Qt::Orientation QSGListViewPrivate::layoutOrientation() const -{ - return static_cast(orient); -} - -bool QSGListViewPrivate::isRightToLeft() const -{ - Q_Q(const QSGListView); - return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft; -} - -// Returns the item before modelIndex, if created. -// May return an item marked for removal. -FxViewItem *QSGListViewPrivate::itemBefore(int modelIndex) const -{ - if (modelIndex < visibleIndex) - return 0; - int idx = 1; - int lastIndex = -1; - while (idx < visibleItems.count()) { - FxViewItem *item = visibleItems.at(idx); - if (item->index != -1) - lastIndex = item->index; - if (item->index == modelIndex) - return visibleItems.at(idx-1); - ++idx; - } - if (lastIndex == modelIndex-1) - return visibleItems.last(); - return 0; -} - -void QSGListViewPrivate::setPosition(qreal pos) -{ - Q_Q(QSGListView); - if (orient == QSGListView::Vertical) { - q->QSGFlickable::setContentY(pos); - } else { - if (isRightToLeft()) - q->QSGFlickable::setContentX(-pos-size()); - else - q->QSGFlickable::setContentX(pos); - } -} - -qreal QSGListViewPrivate::originPosition() const -{ - qreal pos = 0; - if (!visibleItems.isEmpty()) { - pos = (*visibleItems.constBegin())->position(); - if (visibleIndex > 0) - pos -= visibleIndex * (averageSize + spacing); - } - return pos; -} - -qreal QSGListViewPrivate::lastPosition() const -{ - qreal pos = 0; - if (!visibleItems.isEmpty()) { - int invisibleCount = visibleItems.count() - visibleIndex; - for (int i = visibleItems.count()-1; i >= 0; --i) { - if (visibleItems.at(i)->index != -1) { - invisibleCount = model->count() - visibleItems.at(i)->index - 1; - break; - } - } - pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); - } else if (model && model->count()) { - pos = (model->count() * averageSize + (model->count()-1) * spacing); - } - return pos; -} - -qreal QSGListViewPrivate::positionAt(int modelIndex) const -{ - if (FxViewItem *item = visibleItem(modelIndex)) - return item->position(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - int count = visibleIndex - modelIndex; - qreal cs = 0; - if (modelIndex == currentIndex && currentItem) { - cs = currentItem->size() + spacing; - --count; - } - return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs; - } else { - int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1; - return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing); - } - } - return 0; -} - -qreal QSGListViewPrivate::endPositionAt(int modelIndex) const -{ - if (FxViewItem *item = visibleItem(modelIndex)) - return item->endPosition(); - if (!visibleItems.isEmpty()) { - if (modelIndex < visibleIndex) { - int count = visibleIndex - modelIndex; - return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing; - } else { - int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1; - return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing); - } - } - return 0; -} - -QString QSGListViewPrivate::sectionAt(int modelIndex) -{ - if (FxViewItem *item = visibleItem(modelIndex)) - return item->attached->section(); - - QString section; - if (sectionCriteria) { - QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); - section = sectionCriteria->sectionString(propValue); - } - - return section; -} - -qreal QSGListViewPrivate::snapPosAt(qreal pos) -{ - if (FxViewItem *snapItem = snapItemAt(pos)) - return snapItem->position(); - if (visibleItems.count()) { - qreal firstPos = (*visibleItems.constBegin())->position(); - qreal endPos = (*(--visibleItems.constEnd()))->position(); - if (pos < firstPos) { - return firstPos - qRound((firstPos - pos) / averageSize) * averageSize; - } else if (pos > endPos) - return endPos + qRound((pos - endPos) / averageSize) * averageSize; - } - return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition(); -} - -FxViewItem *QSGListViewPrivate::snapItemAt(qreal pos) -{ - FxViewItem *snapItem = 0; - qreal prevItemSize = 0; - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index == -1) - continue; - qreal itemTop = item->position(); - if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size()) - return item; - if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos) - snapItem = item; - prevItemSize = item->size(); - } - return snapItem; -} - -void QSGListViewPrivate::changedVisibleIndex(int newIndex) -{ - visiblePos = positionAt(newIndex); - visibleIndex = newIndex; -} - -void QSGListViewPrivate::init() -{ - QSGItemViewPrivate::init(); - ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize); -} - -void QSGListViewPrivate::clear() -{ - for (int i = 0; i < sectionCacheSize; ++i) { - delete sectionCache[i]; - sectionCache[i] = 0; - } - visiblePos = 0; - currentSectionItem = 0; - nextSectionItem = 0; - lastVisibleSection = QString(); - QSGItemViewPrivate::clear(); -} - -FxViewItem *QSGListViewPrivate::newViewItem(int modelIndex, QSGItem *item) -{ - Q_Q(QSGListView); - - FxListItemSG *listItem = new FxListItemSG(item, q, false); - listItem->index = modelIndex; - - // initialise attached properties - if (sectionCriteria) { - QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); - listItem->attached->m_section = sectionCriteria->sectionString(propValue); - if (modelIndex > 0) { - if (FxViewItem *item = itemBefore(modelIndex)) - listItem->attached->m_prevSection = item->attached->section(); - else - listItem->attached->m_prevSection = sectionAt(modelIndex-1); - } - if (modelIndex < model->count()-1) { - if (FxViewItem *item = visibleItem(modelIndex+1)) - listItem->attached->m_nextSection = static_cast(item->attached)->section(); - else - listItem->attached->m_nextSection = sectionAt(modelIndex+1); - } - } - - return listItem; -} - -void QSGListViewPrivate::initializeViewItem(FxViewItem *item) -{ - QSGItemViewPrivate::initializeViewItem(item); - - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - - if (sectionCriteria && sectionCriteria->delegate()) { - if (item->attached->m_prevSection != item->attached->m_section) - updateInlineSection(static_cast(item)); - } -} - -void QSGListViewPrivate::releaseItem(FxViewItem *item) -{ - if (item) { - FxListItemSG* listItem = static_cast(item); - if (listItem->section) { - int i = 0; - do { - if (!sectionCache[i]) { - sectionCache[i] = listItem->section; - sectionCache[i]->setVisible(false); - listItem->section = 0; - break; - } - ++i; - } while (i < sectionCacheSize); - delete listItem->section; - } - } - QSGItemViewPrivate::releaseItem(item); -} - -bool QSGListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer) -{ - qreal itemEnd = visiblePos; - if (visibleItems.count()) { - visiblePos = (*visibleItems.constBegin())->position(); - itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing; - } - - int modelIndex = findLastVisibleIndex(); - bool haveValidItems = modelIndex >= 0; - modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1; - - if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing - || fillTo < visiblePos - averageSize - spacing)) { - // We've jumped more than a page. Estimate which items are now - // visible and fill from there. - int count = (fillFrom - itemEnd) / (averageSize + spacing); - for (int i = 0; i < visibleItems.count(); ++i) - releaseItem(visibleItems.at(i)); - visibleItems.clear(); - modelIndex += count; - if (modelIndex >= model->count()) { - count -= modelIndex - model->count() + 1; - modelIndex = model->count() - 1; - } else if (modelIndex < 0) { - count -= modelIndex; - modelIndex = 0; - } - visibleIndex = modelIndex; - visiblePos = itemEnd + count * (averageSize + spacing); - itemEnd = visiblePos; - } - - bool changed = false; - FxListItemSG *item = 0; - qreal pos = itemEnd; - while (modelIndex < model->count() && pos <= fillTo) { -// qDebug() << "refill: append item" << modelIndex << "pos" << pos; - if (!(item = static_cast(createItem(modelIndex)))) - break; - item->setPosition(pos); - pos += item->size() + spacing; - visibleItems.append(item); - ++modelIndex; - changed = true; - if (doBuffer) // never buffer more than one item per frame - break; - } - while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos >= fillFrom) { -// qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos; - if (!(item = static_cast(createItem(visibleIndex-1)))) - break; - --visibleIndex; - visiblePos -= item->size() + spacing; - item->setPosition(visiblePos); - visibleItems.prepend(item); - changed = true; - if (doBuffer) // never buffer more than one item per frame - break; - } - - return changed; -} - -bool QSGListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) -{ - FxViewItem *item = 0; - bool changed = false; - - while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() <= bufferFrom) { - if (item->attached->delayRemove()) - break; - if (item->size() == 0) - break; -// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition(); - if (item->index != -1) - visibleIndex++; - visibleItems.removeFirst(); - releaseItem(item); - changed = true; - } - while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { - if (item->attached->delayRemove()) - break; -// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position(); - visibleItems.removeLast(); - releaseItem(item); - changed = true; - } - - return changed; -} - -void QSGListViewPrivate::visibleItemsChanged() -{ - if (visibleItems.count()) - visiblePos = (*visibleItems.constBegin())->position(); - updateAverage(); - if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) { - static_cast(currentItem)->setPosition(positionAt(currentIndex)); - updateHighlight(); - } - if (sectionCriteria) - updateCurrentSection(); - updateHeader(); - updateFooter(); - updateViewport(); - updateUnrequestedPositions(); -} - -void QSGListViewPrivate::layoutVisibleItems() -{ - if (!visibleItems.isEmpty()) { - bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item; - qreal sum = (*visibleItems.constBegin())->size(); - qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing; - for (int i=1; i < visibleItems.count(); ++i) { - FxListItemSG *item = static_cast(visibleItems.at(i)); - item->setPosition(pos); - pos += item->size() + spacing; - sum += item->size(); - fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item); - } - averageSize = qRound(sum / visibleItems.count()); - - // move current item if it is not a visible item. - if (currentIndex >= 0 && currentItem && !fixedCurrent) { - static_cast(currentItem)->setPosition(positionAt(currentIndex)); - } - } -} - -void QSGListViewPrivate::repositionPackageItemAt(QSGItem *item, int index) -{ - Q_Q(QSGListView); - qreal pos = position(); - if (orient == QSGListView::Vertical) { - if (item->y() + item->height() > pos && item->y() < pos + q->height()) - item->setY(positionAt(index)); - } else { - if (item->x() + item->width() > pos && item->x() < pos + q->width()) { - if (isRightToLeft()) - item->setX(-positionAt(index)-item->width()); - else - item->setX(positionAt(index)); - } - } -} - -void QSGListViewPrivate::resetItemPosition(FxViewItem *item, FxViewItem *toItem) -{ - if (item == toItem) - return; - static_cast(item)->setPosition(toItem->position()); -} - -void QSGListViewPrivate::resetFirstItemPosition() -{ - FxListItemSG *item = static_cast(visibleItems.first()); - item->setPosition(0); -} - -void QSGListViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards) -{ - qreal diff = forwards - backwards; - static_cast(item)->setPosition(item->position() + diff); -} - -void QSGListViewPrivate::createHighlight() -{ - Q_Q(QSGListView); - bool changed = false; - if (highlight) { - if (trackedItem == highlight) - trackedItem = 0; - delete highlight; - highlight = 0; - - delete highlightPosAnimator; - delete highlightSizeAnimator; - highlightPosAnimator = 0; - highlightSizeAnimator = 0; - - changed = true; - } - - if (currentItem) { - QSGItem *item = createHighlightItem(); - if (item) { - FxListItemSG *newHighlight = new FxListItemSG(item, q, true); - - if (autoHighlight) { - newHighlight->setSize(static_cast(currentItem)->itemSize()); - newHighlight->setPosition(static_cast(currentItem)->itemPosition()); - } - const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x"); - highlightPosAnimator = new QSmoothedAnimation(q); - highlightPosAnimator->target = QDeclarativeProperty(item, posProp); - highlightPosAnimator->velocity = highlightMoveSpeed; - highlightPosAnimator->userDuration = highlightMoveDuration; - - const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width"); - highlightSizeAnimator = new QSmoothedAnimation(q); - highlightSizeAnimator->velocity = highlightResizeSpeed; - highlightSizeAnimator->userDuration = highlightResizeDuration; - highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp); - - highlight = newHighlight; - changed = true; - } - } - if (changed) - emit q->highlightItemChanged(); -} - -void QSGListViewPrivate::updateHighlight() -{ - applyPendingChanges(); - - if ((!currentItem && highlight) || (currentItem && !highlight)) - createHighlight(); - bool strictHighlight = haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange; - if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) { - // auto-update highlight - FxListItemSG *listItem = static_cast(currentItem); - highlightPosAnimator->to = isRightToLeft() - ? -listItem->itemPosition()-listItem->itemSize() - : listItem->itemPosition(); - highlightSizeAnimator->to = listItem->itemSize(); - if (orient == QSGListView::Vertical) { - if (highlight->item->width() == 0) - highlight->item->setWidth(currentItem->item->width()); - } else { - if (highlight->item->height() == 0) - highlight->item->setHeight(currentItem->item->height()); - } - - highlightPosAnimator->restart(); - highlightSizeAnimator->restart(); - } - updateTrackedItem(); -} - -void QSGListViewPrivate::resetHighlightPosition() -{ - if (highlight && currentItem) - static_cast(highlight)->setPosition(static_cast(currentItem)->itemPosition()); -} - -QSGItem * QSGListViewPrivate::getSectionItem(const QString §ion) -{ - Q_Q(QSGListView); - QSGItem *sectionItem = 0; - int i = sectionCacheSize-1; - while (i >= 0 && !sectionCache[i]) - --i; - if (i >= 0) { - sectionItem = sectionCache[i]; - sectionCache[i] = 0; - sectionItem->setVisible(true); - QDeclarativeContext *context = QDeclarativeEngine::contextForObject(sectionItem)->parentContext(); - context->setContextProperty(QLatin1String("section"), section); - } else { - QDeclarativeContext *creationContext = sectionCriteria->delegate()->creationContext(); - QDeclarativeContext *context = new QDeclarativeContext( - creationContext ? creationContext : qmlContext(q)); - context->setContextProperty(QLatin1String("section"), section); - QObject *nobj = sectionCriteria->delegate()->beginCreate(context); - if (nobj) { - QDeclarative_setParent_noEvent(context, nobj); - sectionItem = qobject_cast(nobj); - if (!sectionItem) { - delete nobj; - } else { - sectionItem->setZ(2); - QDeclarative_setParent_noEvent(sectionItem, contentItem); - sectionItem->setParentItem(contentItem); - } - } else { - delete context; - } - sectionCriteria->delegate()->completeCreate(); - } - - return sectionItem; -} - -void QSGListViewPrivate::releaseSectionItem(QSGItem *item) -{ - int i = 0; - do { - if (!sectionCache[i]) { - sectionCache[i] = item; - sectionCache[i]->setVisible(false); - return; - } - ++i; - } while (i < sectionCacheSize); - delete item; -} - -void QSGListViewPrivate::updateInlineSection(FxListItemSG *listItem) -{ - if (!sectionCriteria || !sectionCriteria->delegate()) - return; - if (listItem->attached->m_prevSection != listItem->attached->m_section - && (sectionCriteria->labelPositioning() & QSGViewSection::InlineLabels - || (listItem->index == 0 && sectionCriteria->labelPositioning() & QSGViewSection::CurrentLabelAtStart))) { - if (!listItem->section) { - qreal pos = listItem->position(); - listItem->section = getSectionItem(listItem->attached->m_section); - listItem->setPosition(pos); - } else { - QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext(); - context->setContextProperty(QLatin1String("section"), listItem->attached->m_section); - } - } else if (listItem->section) { - qreal pos = listItem->position(); - releaseSectionItem(listItem->section); - listItem->section = 0; - listItem->setPosition(pos); - } -} - -void QSGListViewPrivate::updateStickySections() -{ - if (!sectionCriteria || visibleItems.isEmpty() - || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem)) - return; - - bool isRtl = isRightToLeft(); - qreal viewPos = isRightToLeft() ? -position()-size() : position(); - QSGItem *sectionItem = 0; - QSGItem *lastSectionItem = 0; - int index = 0; - while (index < visibleItems.count()) { - if (QSGItem *section = static_cast(visibleItems.at(index))->section) { - // Find the current section header and last visible section header - // and hide them if they will overlap a static section header. - qreal sectionPos = orient == QSGListView::Vertical ? section->y() : section->x(); - qreal sectionSize = orient == QSGListView::Vertical ? section->height() : section->width(); - bool visTop = true; - if (sectionCriteria->labelPositioning() & QSGViewSection::CurrentLabelAtStart) - visTop = isRtl ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos; - bool visBot = true; - if (sectionCriteria->labelPositioning() & QSGViewSection::NextLabelAtEnd) - visBot = isRtl ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size(); - section->setVisible(visBot && visTop); - if (visTop && !sectionItem) - sectionItem = section; - if (isRtl) { - if (-sectionPos <= viewPos + size()) - lastSectionItem = section; - } else { - if (sectionPos + sectionSize < viewPos + size()) - lastSectionItem = section; - } - } - ++index; - } - - // Current section header - if (sectionCriteria->labelPositioning() & QSGViewSection::CurrentLabelAtStart) { - if (!currentSectionItem) { - currentSectionItem = getSectionItem(currentSection); - } else if (currentStickySection != currentSection) { - QDeclarativeContext *context = QDeclarativeEngine::contextForObject(currentSectionItem)->parentContext(); - context->setContextProperty(QLatin1String("section"), currentSection); - } - currentStickySection = currentSection; - if (!currentSectionItem) - return; - - qreal sectionSize = orient == QSGListView::Vertical ? currentSectionItem->height() : currentSectionItem->width(); - bool atBeginning = orient == QSGListView::Vertical ? vData.atBeginning : (isRightToLeft() ? hData.atEnd : hData.atBeginning); - currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos)); - qreal pos = isRtl ? position() + size() - sectionSize : viewPos; - if (sectionItem) { - qreal sectionPos = orient == QSGListView::Vertical ? sectionItem->y() : sectionItem->x(); - pos = isRtl ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize); - } - if (header) - pos = isRtl ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos); - if (footer) - pos = isRtl ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos); - if (orient == QSGListView::Vertical) - currentSectionItem->setY(pos); - else - currentSectionItem->setX(pos); - } else if (currentSectionItem) { - releaseSectionItem(currentSectionItem); - currentSectionItem = 0; - } - - // Next section footer - if (sectionCriteria->labelPositioning() & QSGViewSection::NextLabelAtEnd) { - if (!nextSectionItem) { - nextSectionItem = getSectionItem(nextSection); - } else if (nextStickySection != nextSection) { - QDeclarativeContext *context = QDeclarativeEngine::contextForObject(nextSectionItem)->parentContext(); - context->setContextProperty(QLatin1String("section"), nextSection); - } - nextStickySection = nextSection; - if (!nextSectionItem) - return; - - qreal sectionSize = orient == QSGListView::Vertical ? nextSectionItem->height() : nextSectionItem->width(); - nextSectionItem->setVisible(!nextSection.isEmpty()); - qreal pos = isRtl ? position() : viewPos + size() - sectionSize; - if (lastSectionItem) { - qreal sectionPos = orient == QSGListView::Vertical ? lastSectionItem->y() : lastSectionItem->x(); - pos = isRtl ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize); - } - if (header) - pos = isRtl ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos); - if (orient == QSGListView::Vertical) - nextSectionItem->setY(pos); - else - nextSectionItem->setX(pos); - } else if (nextSectionItem) { - releaseSectionItem(nextSectionItem); - nextSectionItem = 0; - } -} - -void QSGListViewPrivate::updateSections() -{ - Q_Q(QSGListView); - if (!q->isComponentComplete()) - return; - - QSGItemViewPrivate::updateSections(); - - if (sectionCriteria && !visibleItems.isEmpty()) { - QString prevSection; - if (visibleIndex > 0) - prevSection = sectionAt(visibleIndex-1); - QSGListViewAttached *prevAtt = 0; - int idx = -1; - for (int i = 0; i < visibleItems.count(); ++i) { - QSGListViewAttached *attached = static_cast(visibleItems.at(i)->attached); - attached->setPrevSection(prevSection); - if (visibleItems.at(i)->index != -1) { - QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property()); - attached->setSection(sectionCriteria->sectionString(propValue)); - idx = visibleItems.at(i)->index; - } - updateInlineSection(static_cast(visibleItems.at(i))); - if (prevAtt) - prevAtt->setNextSection(attached->section()); - prevSection = attached->section(); - prevAtt = attached; - } - if (prevAtt) { - if (idx > 0 && idx < model->count()-1) - prevAtt->setNextSection(sectionAt(idx+1)); - else - prevAtt->setNextSection(QString()); - } - } - - lastVisibleSection = QString(); - updateCurrentSection(); - updateStickySections(); -} - -void QSGListViewPrivate::updateCurrentSection() -{ - Q_Q(QSGListView); - if (!sectionCriteria || visibleItems.isEmpty()) { - if (!currentSection.isEmpty()) { - currentSection.clear(); - emit q->currentSectionChanged(); - } - return; - } - bool inlineSections = sectionCriteria->labelPositioning() & QSGViewSection::InlineLabels; - qreal sectionThreshold = position(); - if (currentSectionItem && !inlineSections) - sectionThreshold += orient == QSGListView::Vertical ? currentSectionItem->height() : currentSectionItem->width(); - int index = 0; - int modelIndex = visibleIndex; - while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) { - if (visibleItems.at(index)->index != -1) - modelIndex = visibleItems.at(index)->index; - ++index; - } - - QString newSection = currentSection; - if (index < visibleItems.count()) - newSection = visibleItems.at(index)->attached->section(); - else - newSection = (*visibleItems.constBegin())->attached->section(); - if (newSection != currentSection) { - currentSection = newSection; - updateStickySections(); - emit q->currentSectionChanged(); - } - - if (sectionCriteria->labelPositioning() & QSGViewSection::NextLabelAtEnd) { - // Don't want to scan for next section on every movement, so remember - // the last section in the visible area and only scan for the next - // section when that changes. Clearing lastVisibleSection will also - // force searching. - QString lastSection = currentSection; - qreal endPos = isRightToLeft() ? -position() : position() + size(); - if (nextSectionItem && !inlineSections) - endPos -= orient == QSGListView::Vertical ? nextSectionItem->height() : nextSectionItem->width(); - while (index < visibleItems.count() && static_cast(visibleItems.at(index))->itemPosition() < endPos) { - if (visibleItems.at(index)->index != -1) - modelIndex = visibleItems.at(index)->index; - lastSection = visibleItems.at(index)->attached->section(); - ++index; - } - - if (lastVisibleSection != lastSection) { - nextSection = QString(); - lastVisibleSection = lastSection; - for (int i = modelIndex; i < itemCount; ++i) { - QString section = sectionAt(i); - if (section != lastSection) { - nextSection = section; - updateStickySections(); - break; - } - } - } - } -} - -void QSGListViewPrivate::initializeCurrentItem() -{ - QSGItemViewPrivate::initializeCurrentItem(); - - if (currentItem) { - FxListItemSG *listItem = static_cast(currentItem); - - if (currentIndex == visibleIndex - 1 && visibleItems.count()) { - // We can calculate exact postion in this case - listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing); - } else { - // Create current item now and position as best we can. - // Its position will be corrected when it becomes visible. - listItem->setPosition(positionAt(currentIndex)); - } - - // Avoid showing section delegate twice. We still need the section heading so that - // currentItem positioning works correctly. - // This is slightly sub-optimal, but section heading caching minimizes the impact. - if (listItem->section) - listItem->section->setVisible(false); - - if (visibleItems.isEmpty()) - averageSize = listItem->size(); - } -} - -void QSGListViewPrivate::updateAverage() -{ - if (!visibleItems.count()) - return; - qreal sum = 0.0; - for (int i = 0; i < visibleItems.count(); ++i) - sum += visibleItems.at(i)->size(); - averageSize = qRound(sum / visibleItems.count()); -} - -qreal QSGListViewPrivate::headerSize() const -{ - return header ? header->size() : 0.0; -} - -qreal QSGListViewPrivate::footerSize() const -{ - return footer ? footer->size() : 0.0; -} - -bool QSGListViewPrivate::showHeaderForIndex(int index) const -{ - return index == 0; -} - -bool QSGListViewPrivate::showFooterForIndex(int index) const -{ - return index == model->count()-1; -} - -void QSGListViewPrivate::updateFooter() -{ - Q_Q(QSGListView); - bool created = false; - if (!footer) { - QSGItem *item = createComponentItem(footerComponent, true); - if (!item) - return; - item->setZ(1); - footer = new FxListItemSG(item, q, true); - created = true; - } - - FxListItemSG *listItem = static_cast(footer); - if (visibleItems.count()) { - qreal endPos = lastPosition(); - if (findLastVisibleIndex() == model->count()-1) { - listItem->setPosition(endPos); - } else { - qreal visiblePos = position() + q->height(); - if (endPos <= visiblePos || listItem->position() < endPos) - listItem->setPosition(endPos); - } - } else { - listItem->setPosition(visiblePos); - } - - if (created) - emit q->footerItemChanged(); -} - -void QSGListViewPrivate::updateHeader() -{ - Q_Q(QSGListView); - bool created = false; - if (!header) { - QSGItem *item = createComponentItem(headerComponent, true); - if (!item) - return; - item->setZ(1); - header = new FxListItemSG(item, q, true); - created = true; - } - - FxListItemSG *listItem = static_cast(header); - if (listItem) { - if (visibleItems.count()) { - qreal startPos = originPosition(); - if (visibleIndex == 0) { - listItem->setPosition(startPos - headerSize()); - } else { - if (position() <= startPos || listItem->position() > startPos - headerSize()) - listItem->setPosition(startPos - headerSize()); - } - } else { - listItem->setPosition(-headerSize()); - } - } - - if (created) - emit q->headerItemChanged(); -} - -void QSGListViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_Q(QSGListView); - QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry); - if (!q->isComponentComplete()) - return; - if (item != contentItem && (!highlight || item != highlight->item)) { - if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height()) - || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) { - forceLayout = true; - q->polish(); - } - } -} - -void QSGListViewPrivate::fixupPosition() -{ - if ((haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange) - || snapMode != QSGListView::NoSnap) - moveReason = Other; - if (orient == QSGListView::Vertical) - fixupY(); - else - fixupX(); -} - -void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) -{ - if ((orient == QSGListView::Horizontal && &data == &vData) - || (orient == QSGListView::Vertical && &data == &hData)) - return; - - correctFlick = false; - fixupMode = moveReason == Mouse ? fixupMode : Immediate; - bool strictHighlightRange = haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange; - - qreal viewPos = isRightToLeft() ? -position()-size() : position(); - - if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) { - qreal tempPosition = isRightToLeft() ? -position()-size() : position(); - if (snapMode == QSGListView::SnapOneItem && moveReason == Mouse) { - // if we've been dragged < averageSize/2 then bias towards the next item - qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); - qreal bias = 0; - if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2) - bias = averageSize/2; - else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2) - bias = -averageSize/2; - if (isRightToLeft()) - bias = -bias; - tempPosition -= bias; - } - FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); - if (!topItem && strictHighlightRange && currentItem) { - // StrictlyEnforceRange always keeps an item in range - updateHighlight(); - topItem = currentItem; - } - FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); - if (!bottomItem && strictHighlightRange && currentItem) { - // StrictlyEnforceRange always keeps an item in range - updateHighlight(); - bottomItem = currentItem; - } - qreal pos; - bool isInBounds = -position() > maxExtent && -position() <= minExtent; - if (topItem && (isInBounds || strictHighlightRange)) { - if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) { - pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart; - } else { - if (isRightToLeft()) - pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent); - else - pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); - } - } else if (bottomItem && isInBounds) { - if (isRightToLeft()) - pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent); - else - pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent); - } else { - QSGItemViewPrivate::fixup(data, minExtent, maxExtent); - return; - } - - qreal dist = qAbs(data.move + pos); - if (dist > 0) { - timeline.reset(data.move); - if (fixupMode != Immediate) { - timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - data.fixingUp = true; - } else { - timeline.set(data.move, -pos); - } - vTime = timeline.time(); - } - } else if (currentItem && strictHighlightRange && moveReason != QSGListViewPrivate::SetIndex) { - updateHighlight(); - qreal pos = static_cast(currentItem)->itemPosition(); - if (viewPos < pos + static_cast(currentItem)->itemSize() - highlightRangeEnd) - viewPos = pos + static_cast(currentItem)->itemSize() - highlightRangeEnd; - if (viewPos > pos - highlightRangeStart) - viewPos = pos - highlightRangeStart; - if (isRightToLeft()) - viewPos = -viewPos-size(); - - timeline.reset(data.move); - if (viewPos != position()) { - if (fixupMode != Immediate) { - timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - data.fixingUp = true; - } else { - timeline.set(data.move, -viewPos); - } - } - vTime = timeline.time(); - } else { - QSGItemViewPrivate::fixup(data, minExtent, maxExtent); - } - data.inOvershoot = false; - fixupMode = Normal; -} - -void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, - QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) -{ - Q_Q(QSGListView); - - data.fixingUp = false; - moveReason = Mouse; - if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) { - correctFlick = true; - QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity); - return; - } - qreal maxDistance = 0; - qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value(); - - // -ve velocity means list is moving up/left - if (velocity > 0) { - if (data.move.value() < minExtent) { - if (snapMode == QSGListView::SnapOneItem && !hData.flicking && !vData.flicking) { - // if we've been dragged < averageSize/2 then bias towards the next item - qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); - qreal bias = dist < averageSize/2 ? averageSize/2 : 0; - if (isRightToLeft()) - bias = -bias; - data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart; - maxDistance = qAbs(data.flickTarget - data.move.value()); - velocity = maxVelocity; - } else { - maxDistance = qAbs(minExtent - data.move.value()); - } - } - if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange) - data.flickTarget = minExtent; - } else { - if (data.move.value() > maxExtent) { - if (snapMode == QSGListView::SnapOneItem && !hData.flicking && !vData.flicking) { - // if we've been dragged < averageSize/2 then bias towards the next item - qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset); - qreal bias = -dist < averageSize/2 ? averageSize/2 : 0; - if (isRightToLeft()) - bias = -bias; - data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart; - maxDistance = qAbs(data.flickTarget - data.move.value()); - velocity = -maxVelocity; - } else { - maxDistance = qAbs(maxExtent - data.move.value()); - } - } - if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange) - data.flickTarget = maxExtent; - } - bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds; - if (maxDistance > 0 || overShoot) { - // These modes require the list to stop exactly on an item boundary. - // The initial flick will estimate the boundary to stop on. - // Since list items can have variable sizes, the boundary will be - // reevaluated and adjusted as we approach the boundary. - qreal v = velocity; - if (maxVelocity != -1 && maxVelocity < qAbs(v)) { - if (v < 0) - v = -maxVelocity; - else - v = maxVelocity; - } - if (!hData.flicking && !vData.flicking) { - // the initial flick - estimate boundary - qreal accel = deceleration; - qreal v2 = v * v; - overshootDist = 0.0; - // + averageSize/4 to encourage moving at least one item in the flick direction - qreal dist = v2 / (accel * 2.0) + averageSize/4; - if (maxDistance > 0) - dist = qMin(dist, maxDistance); - if (v > 0) - dist = -dist; - if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) { - if (snapMode != QSGListView::SnapOneItem) { - qreal distTemp = isRightToLeft() ? -dist : dist; - data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart; - } - data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; - if (overShoot) { - if (data.flickTarget >= minExtent) { - overshootDist = overShootDistance(vSize); - data.flickTarget += overshootDist; - } else if (data.flickTarget <= maxExtent) { - overshootDist = overShootDistance(vSize); - data.flickTarget -= overshootDist; - } - } - qreal adjDist = -data.flickTarget + data.move.value(); - if (qAbs(adjDist) > qAbs(dist)) { - // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration - qreal adjv2 = accel * 2.0f * qAbs(adjDist); - if (adjv2 > v2) { - v2 = adjv2; - v = qSqrt(v2); - if (dist > 0) - v = -v; - } - } - dist = adjDist; - accel = v2 / (2.0f * qAbs(dist)); - } else if (overShoot) { - data.flickTarget = data.move.value() - dist; - if (data.flickTarget >= minExtent) { - overshootDist = overShootDistance(vSize); - data.flickTarget += overshootDist; - } else if (data.flickTarget <= maxExtent) { - overshootDist = overShootDistance(vSize); - data.flickTarget -= overshootDist; - } - } - timeline.reset(data.move); - timeline.accel(data.move, v, accel, maxDistance + overshootDist); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); - if (!hData.flicking && q->xflick()) { - hData.flicking = true; - emit q->flickingChanged(); - emit q->flickingHorizontallyChanged(); - emit q->flickStarted(); - } - if (!vData.flicking && q->yflick()) { - vData.flicking = true; - emit q->flickingChanged(); - emit q->flickingVerticallyChanged(); - emit q->flickStarted(); - } - correctFlick = true; - } else { - // reevaluate the target boundary. - qreal newtarget = data.flickTarget; - if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) { - qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget; - newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart; - newtarget = isRightToLeft() ? -newtarget+size() : newtarget; - } - if (velocity < 0 && newtarget <= maxExtent) - newtarget = maxExtent - overshootDist; - else if (velocity > 0 && newtarget >= minExtent) - newtarget = minExtent + overshootDist; - if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do - if (qAbs(velocity) < MinimumFlickVelocity) - correctFlick = false; - return; - } - data.flickTarget = newtarget; - qreal dist = -newtarget + data.move.value(); - if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) { - correctFlick = false; - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); - return; - } - timeline.reset(data.move); - timeline.accelDistance(data.move, v, -dist); - timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); - } - } else { - correctFlick = false; - timeline.reset(data.move); - fixup(data, minExtent, maxExtent); - } -} - -//---------------------------------------------------------------------------- - -/*! - \qmlclass ListView QSGListView - \inqmlmodule QtQuick 2 - \ingroup qml-view-elements - \inherits Flickable - \brief The ListView item provides a list view of items provided by a model. - - A ListView displays data from models created from built-in QML elements like ListModel - and XmlListModel, or custom model classes defined in C++ that inherit from - QAbstractListModel. - - A ListView has a \l model, which defines the data to be displayed, and - a \l delegate, which defines how the data should be displayed. Items in a - ListView are laid out horizontally or vertically. List views are inherently - flickable because ListView inherits from \l Flickable. - - \section1 Example Usage - - The following example shows the definition of a simple list model defined - in a file called \c ContactModel.qml: - - \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0 - - Another component can display this model data in a ListView, like this: - - \snippet doc/src/snippets/declarative/listview/listview.qml import - \codeline - \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple - - \image listview-simple.png - - Here, the ListView creates a \c ContactModel component for its model, and a \l Text element - for its delegate. The view will create a new \l Text component for each item in the model. Notice - the delegate is able to access the model's \c name and \c number data directly. - - An improved list view is shown below. The delegate is visually improved and is moved - into a separate \c contactDelegate component. - - \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced - \image listview-highlight.png - - The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, - and \c focus is set to \c true to enable keyboard navigation for the list view. - The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). - - Delegates are instantiated as needed and may be destroyed at any time. - State should \e never be stored in a delegate. - - ListView attaches a number of properties to the root item of the delegate, for example - \c {ListView.isCurrentItem}. In the following example, the root delegate item can access - this attached property directly as \c ListView.isCurrentItem, while the child - \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem. - - \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem - - \note Views do not enable \e clip automatically. If the view - is not clipped by another item or the screen, it will be necessary - to set \e {clip: true} in order to have the out of view items clipped - nicely. - - \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples} -*/ -QSGListView::QSGListView(QSGItem *parent) - : QSGItemView(*(new QSGListViewPrivate), parent) -{ -} - -QSGListView::~QSGListView() -{ -} - -/*! - \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem - This attached property is true if this delegate is the current item; otherwise false. - - It is attached to each instance of the delegate. - - This property may be used to adjust the appearance of the current item, for example: - - \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem -*/ - -/*! - \qmlattachedproperty ListView QtQuick2::ListView::view - This attached property holds the view that manages this delegate instance. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty string QtQuick2::ListView::previousSection - This attached property holds the section of the previous element. - - It is attached to each instance of the delegate. - - The section is evaluated using the \l {ListView::section.property}{section} properties. -*/ - -/*! - \qmlattachedproperty string QtQuick2::ListView::nextSection - This attached property holds the section of the next element. - - It is attached to each instance of the delegate. - - The section is evaluated using the \l {ListView::section.property}{section} properties. -*/ - -/*! - \qmlattachedproperty string QtQuick2::ListView::section - This attached property holds the section of this element. - - It is attached to each instance of the delegate. - - The section is evaluated using the \l {ListView::section.property}{section} properties. -*/ - -/*! - \qmlattachedproperty bool QtQuick2::ListView::delayRemove - This attached property holds whether the delegate may be destroyed. - - It is attached to each instance of the delegate. - - It is sometimes necessary to delay the destruction of an item - until an animation completes. - - The example delegate below ensures that the animation completes before - the item is removed from the list. - - \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove -*/ - -/*! - \qmlattachedsignal QtQuick2::ListView::onAdd() - This attached handler is called immediately after an item is added to the view. -*/ - -/*! - \qmlattachedsignal QtQuick2::ListView::onRemove() - This attached handler is called immediately before an item is removed from the view. -*/ - -/*! - \qmlproperty model QtQuick2::ListView::model - This property holds the model providing data for the list. - - The model provides the set of data that is used to create the items - in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel - or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is - used, it must be a subclass of \l QAbstractItemModel or a simple list. - - \sa {qmlmodels}{Data Models} -*/ - -/*! - \qmlproperty Component QtQuick2::ListView::delegate - - The delegate provides a template defining each item instantiated by the view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qmlmodels}{Data Model}. - - The number of elements in the delegate has a direct effect on the - flicking performance of the view. If at all possible, place functionality - that is not needed for the normal display of the delegate in a \l Loader which - can load additional elements when needed. - - The ListView will lay out the items based on the size of the root item - in the delegate. - - It is recommended that the delagate's size be a whole number to avoid sub-pixel - alignment of items. - - \note Delegates are instantiated as needed and may be destroyed at any time. - State should \e never be stored in a delegate. -*/ -/*! - \qmlproperty int QtQuick2::ListView::currentIndex - \qmlproperty Item QtQuick2::ListView::currentItem - - The \c currentIndex property holds the index of the current item, and - \c currentItem holds the current item. Setting the currentIndex to -1 - will clear the highlight and set currentItem to null. - - If highlightFollowsCurrentItem is \c true, setting either of these - properties will smoothly scroll the ListView so that the current - item becomes visible. - - Note that the position of the current item - may only be approximate until it becomes visible in the view. -*/ - -/*! - \qmlproperty Item QtQuick2::ListView::highlightItem - - This holds the highlight item created from the \l highlight component. - - The \c highlightItem is managed by the view unless - \l highlightFollowsCurrentItem is set to false. - - \sa highlight, highlightFollowsCurrentItem -*/ - -/*! - \qmlproperty int QtQuick2::ListView::count - This property holds the number of items in the view. -*/ - -/*! - \qmlproperty Component QtQuick2::ListView::highlight - This property holds the component to use as the highlight. - - An instance of the highlight component is created for each list. - The geometry of the resulting component instance is managed by the list - so as to stay with the current item, unless the highlightFollowsCurrentItem - property is false. - - \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples} -*/ - -/*! - \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem - This property holds whether the highlight is managed by the view. - - If this property is true (the default value), the highlight is moved smoothly - to follow the current item. Otherwise, the - highlight is not moved by the view, and any movement must be implemented - by the highlight. - - Here is a highlight with its motion defined by a \l {SpringAnimation} item: - - \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem - - Note that the highlight animation also affects the way that the view - is scrolled. This is because the view moves to maintain the - highlight within the preferred highlight range (or visible viewport). - - \sa highlight, highlightMoveSpeed -*/ -//###Possibly rename these properties, since they are very useful even without a highlight? -/*! - \qmlproperty real QtQuick2::ListView::preferredHighlightBegin - \qmlproperty real QtQuick2::ListView::preferredHighlightEnd - \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode - - These properties define the preferred range of the highlight (for the current item) - within the view. The \c preferredHighlightBegin value must be less than the - \c preferredHighlightEnd value. - - These properties affect the position of the current item when the list is scrolled. - For example, if the currently selected item should stay in the middle of the - list when the view is scrolled, set the \c preferredHighlightBegin and - \c preferredHighlightEnd values to the top and bottom coordinates of where the middle - item would be. If the \c currentItem is changed programmatically, the list will - automatically scroll so that the current item is in the middle of the view. - Furthermore, the behavior of the current item index will occur whether or not a - highlight exists. - - Valid values for \c highlightRangeMode are: - - \list - \o ListView.ApplyRange - the view attempts to maintain the highlight within the range. - However, the highlight can move outside of the range at the ends of the list or due - to mouse interaction. - \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range. - The current item changes if a keyboard or mouse action would cause the highlight to move - outside of the range. - \o ListView.NoHighlightRange - this is the default value. - \endlist -*/ -void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight) -{ - Q_D(QSGListView); - if (d->autoHighlight != autoHighlight) { - if (!autoHighlight) { - if (d->highlightPosAnimator) - d->highlightPosAnimator->stop(); - if (d->highlightSizeAnimator) - d->highlightSizeAnimator->stop(); - } - QSGItemView::setHighlightFollowsCurrentItem(autoHighlight); - } -} - -/*! - \qmlproperty real QtQuick2::ListView::spacing - - This property holds the spacing between items. - - The default value is 0. -*/ -qreal QSGListView::spacing() const -{ - Q_D(const QSGListView); - return d->spacing; -} - -void QSGListView::setSpacing(qreal spacing) -{ - Q_D(QSGListView); - if (spacing != d->spacing) { - d->spacing = spacing; - d->forceLayout = true; - d->layout(); - emit spacingChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick2::ListView::orientation - This property holds the orientation of the list. - - Possible values: - - \list - \o ListView.Horizontal - Items are laid out horizontally - \o ListView.Vertical (default) - Items are laid out vertically - \endlist - - \table - \row - \o Horizontal orientation: - \image ListViewHorizontal.png - - \row - \o Vertical orientation: - \image listview-highlight.png - \endtable -*/ -QSGListView::Orientation QSGListView::orientation() const -{ - Q_D(const QSGListView); - return d->orient; -} - -void QSGListView::setOrientation(QSGListView::Orientation orientation) -{ - Q_D(QSGListView); - if (d->orient != orientation) { - d->orient = orientation; - if (d->orient == Vertical) { - setContentWidth(-1); - setFlickableDirection(VerticalFlick); - setContentX(0); - } else { - setContentHeight(-1); - setFlickableDirection(HorizontalFlick); - setContentY(0); - } - d->regenerate(); - emit orientationChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick2::ListView::layoutDirection - This property holds the layout direction of the horizontal list. - - Possible values: - - \list - \o Qt.LeftToRight (default) - Items will be laid out from left to right. - \o Qt.RightToLeft - Items will be laid out from right to let. - \endlist - - \sa ListView::effectiveLayoutDirection -*/ - - -/*! - \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection - This property holds the effective layout direction of the horizontal list. - - When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, - the visual layout direction of the horizontal list will be mirrored. However, the - property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged. - - \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring} -*/ - -/*! - \qmlproperty bool QtQuick2::ListView::keyNavigationWraps - This property holds whether the list wraps key navigation. - - If this is true, key navigation that would move the current item selection - past the end of the list instead wraps around and moves the selection to - the start of the list, and vice-versa. - - By default, key navigation is not wrapped. -*/ - - -/*! - \qmlproperty int QtQuick2::ListView::cacheBuffer - This property determines whether delegates are retained outside the - visible area of the view. - - If this value is non-zero, the view keeps as many delegates - instantiated as it can fit within the buffer specified. For example, - if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is - set to 40, then up to 2 delegates above and 2 delegates below the visible - area may be retained. - - Note that cacheBuffer is not a pixel buffer - it only maintains additional - instantiated delegates. - - Setting this value can improve the smoothness of scrolling behavior at the expense - of additional memory usage. It is not a substitute for creating efficient - delegates; the fewer elements in a delegate, the faster a view can be - scrolled. -*/ - - -/*! - \qmlproperty string QtQuick2::ListView::section.property - \qmlproperty enumeration QtQuick2::ListView::section.criteria - \qmlproperty Component QtQuick2::ListView::section.delegate - \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning - - These properties determine the expression to be evaluated and appearance - of the section labels. - - \c section.property holds the name of the property that is the basis - of each section. - - \c section.criteria holds the criteria for forming each section based on - \c section.property. This value can be one of: - - \list - \o ViewSection.FullString (default) - sections are created based on the - \c section.property value. - \o ViewSection.FirstCharacter - sections are created based on the first - character of the \c section.property value (for example, 'A', 'B', 'C' - sections, etc. for an address book) - \endlist - - \c section.delegate holds the delegate component for each section. - - \c section.labelPositioning determines whether the current and/or - next section labels stick to the start/end of the view, and whether - the labels are shown inline. This value can be a combination of: - - \list - \o ViewSection.InlineLabels - section labels are shown inline between - the item delegates separating sections (default). - \o ViewSection.CurrentLabelAtStart - the current section label sticks to the - start of the view as it is moved. - \o ViewSection.NextLabelAtEnd - the next section label (beyond all visible - sections) sticks to the end of the view as it is moved. \note Enabling - \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next - section, which has performance implications, especially for slower models. - \endlist - - Each item in the list has attached properties named \c ListView.section, - \c ListView.previousSection and \c ListView.nextSection. - - For example, here is a ListView that displays a list of animals, separated - into sections. Each item in the ListView is placed in a different section - depending on the "size" property of the model item. The \c sectionHeading - delegate component provides the light blue bar that marks the beginning of - each section. - - - \snippet examples/declarative/modelviews/listview/sections.qml 0 - - \image qml-listview-sections-example.png - - \note Adding sections to a ListView does not automatically re-order the - list items by the section criteria. - If the model is not ordered by section, then it is possible that - the sections created will not be unique; each boundary between - differing sections will result in a section header being created - even if that section exists elsewhere. - - \sa {declarative/modelviews/listview}{ListView examples} -*/ -QSGViewSection *QSGListView::sectionCriteria() -{ - Q_D(QSGListView); - if (!d->sectionCriteria) { - d->sectionCriteria = new QSGViewSection(this); - connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections())); - } - return d->sectionCriteria; -} - -/*! - \qmlproperty string QtQuick2::ListView::currentSection - This property holds the section that is currently at the beginning of the view. -*/ -QString QSGListView::currentSection() const -{ - Q_D(const QSGListView); - return d->currentSection; -} - -/*! - \qmlproperty real QtQuick2::ListView::highlightMoveSpeed - \qmlproperty int QtQuick2::ListView::highlightMoveDuration - \qmlproperty real QtQuick2::ListView::highlightResizeSpeed - \qmlproperty int QtQuick2::ListView::highlightResizeDuration - - These properties hold the move and resize animation speed of the highlight delegate. - - \l highlightFollowsCurrentItem must be true for these properties - to have effect. - - The default value for the speed properties is 400 pixels/second. - The default value for the duration properties is -1, i.e. the - highlight will take as much time as necessary to move at the set speed. - - These properties have the same characteristics as a SmoothedAnimation. - - \sa highlightFollowsCurrentItem -*/ -qreal QSGListView::highlightMoveSpeed() const -{ - Q_D(const QSGListView); - return d->highlightMoveSpeed; -} - -void QSGListView::setHighlightMoveSpeed(qreal speed) -{ - Q_D(QSGListView); - if (d->highlightMoveSpeed != speed) { - d->highlightMoveSpeed = speed; - if (d->highlightPosAnimator) - d->highlightPosAnimator->velocity = d->highlightMoveSpeed; - emit highlightMoveSpeedChanged(); - } -} - -void QSGListView::setHighlightMoveDuration(int duration) -{ - Q_D(QSGListView); - if (d->highlightMoveDuration != duration) { - if (d->highlightPosAnimator) - d->highlightPosAnimator->userDuration = duration; - QSGItemView::setHighlightMoveDuration(duration); - } -} - -qreal QSGListView::highlightResizeSpeed() const -{ - Q_D(const QSGListView); - return d->highlightResizeSpeed; -} - -void QSGListView::setHighlightResizeSpeed(qreal speed) -{ - Q_D(QSGListView); - if (d->highlightResizeSpeed != speed) { - d->highlightResizeSpeed = speed; - if (d->highlightSizeAnimator) - d->highlightSizeAnimator->velocity = d->highlightResizeSpeed; - emit highlightResizeSpeedChanged(); - } -} - -int QSGListView::highlightResizeDuration() const -{ - Q_D(const QSGListView); - return d->highlightResizeDuration; -} - -void QSGListView::setHighlightResizeDuration(int duration) -{ - Q_D(QSGListView); - if (d->highlightResizeDuration != duration) { - d->highlightResizeDuration = duration; - if (d->highlightSizeAnimator) - d->highlightSizeAnimator->userDuration = d->highlightResizeDuration; - emit highlightResizeDurationChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick2::ListView::snapMode - - This property determines how the view scrolling will settle following a drag or flick. - The possible values are: - - \list - \o ListView.NoSnap (default) - the view stops anywhere within the visible area. - \o ListView.SnapToItem - the view settles with an item aligned with the start of - the view. - \o ListView.SnapOneItem - the view settles no more than one item away from the first - visible item at the time the mouse button is released. This mode is particularly - useful for moving one page at a time. - \endlist - - \c snapMode does not affect the \l currentIndex. To update the - \l currentIndex as the list is moved, set \l highlightRangeMode - to \c ListView.StrictlyEnforceRange. - - \sa highlightRangeMode -*/ -QSGListView::SnapMode QSGListView::snapMode() const -{ - Q_D(const QSGListView); - return d->snapMode; -} - -void QSGListView::setSnapMode(SnapMode mode) -{ - Q_D(QSGListView); - if (d->snapMode != mode) { - d->snapMode = mode; - emit snapModeChanged(); - } -} - - -/*! - \qmlproperty Component QtQuick2::ListView::footer - This property holds the component to use as the footer. - - An instance of the footer component is created for each view. The - footer is positioned at the end of the view, after any items. - - \sa header -*/ - - -/*! - \qmlproperty Component QtQuick2::ListView::header - This property holds the component to use as the header. - - An instance of the header component is created for each view. The - header is positioned at the beginning of the view, before any items. - - \sa footer -*/ - -void QSGListView::viewportMoved() -{ - Q_D(QSGListView); - QSGItemView::viewportMoved(); - if (!d->itemCount) - return; - // Recursion can occur due to refill changing the content size. - if (d->inViewportMoved) - return; - d->inViewportMoved = true; - d->lazyRelease = true; - d->refill(); - if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving) - d->moveReason = QSGListViewPrivate::Mouse; - if (d->moveReason != QSGListViewPrivate::SetIndex) { - if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { - // reposition highlight - qreal pos = d->highlight->position(); - qreal viewPos = d->isRightToLeft() ? -d->position()-d->size() : d->position(); - if (pos > viewPos + d->highlightRangeEnd - d->highlight->size()) - pos = viewPos + d->highlightRangeEnd - d->highlight->size(); - if (pos < viewPos + d->highlightRangeStart) - pos = viewPos + d->highlightRangeStart; - if (pos != d->highlight->position()) { - d->highlightPosAnimator->stop(); - static_cast(d->highlight)->setPosition(pos); - } else { - d->updateHighlight(); - } - - // update current index - if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) { - if (snapItem->index >= 0 && snapItem->index != d->currentIndex) - d->updateCurrent(snapItem->index); - } - } - } - - if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) { - d->inFlickCorrection = true; - // Near an end and it seems that the extent has changed? - // Recalculate the flick so that we don't end up in an odd position. - if (yflick() && !d->vData.inOvershoot) { - if (d->vData.velocity > 0) { - const qreal minY = minYExtent(); - if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2) - && minY != d->vData.flickTarget) - d->flickY(-d->vData.smoothVelocity.value()); - d->bufferMode = QSGListViewPrivate::BufferBefore; - } else if (d->vData.velocity < 0) { - const qreal maxY = maxYExtent(); - if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2) - && maxY != d->vData.flickTarget) - d->flickY(-d->vData.smoothVelocity.value()); - d->bufferMode = QSGListViewPrivate::BufferAfter; - } - } - - if (xflick() && !d->hData.inOvershoot) { - if (d->hData.velocity > 0) { - const qreal minX = minXExtent(); - if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2) - && minX != d->hData.flickTarget) - d->flickX(-d->hData.smoothVelocity.value()); - d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferAfter : QSGListViewPrivate::BufferBefore; - } else if (d->hData.velocity < 0) { - const qreal maxX = maxXExtent(); - if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2) - && maxX != d->hData.flickTarget) - d->flickX(-d->hData.smoothVelocity.value()); - d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferBefore : QSGListViewPrivate::BufferAfter; - } - } - d->inFlickCorrection = false; - } - if (d->sectionCriteria) { - d->updateCurrentSection(); - d->updateStickySections(); - } - d->inViewportMoved = false; -} - -void QSGListView::keyPressEvent(QKeyEvent *event) -{ - Q_D(QSGListView); - if (d->model && d->model->count() && d->interactive) { - if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left) - || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right) - || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Up)) { - if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) { - decrementCurrentIndex(); - event->accept(); - return; - } else if (d->wrap) { - event->accept(); - return; - } - } else if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right) - || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left) - || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Down)) { - if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) { - incrementCurrentIndex(); - event->accept(); - return; - } else if (d->wrap) { - event->accept(); - return; - } - } - } - event->ignore(); - QSGItemView::keyPressEvent(event); -} - -void QSGListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_D(QSGListView); - if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) { - // maintain position relative to the right edge - int dx = newGeometry.width() - oldGeometry.width(); - setContentX(contentX() - dx); - } - QSGItemView::geometryChanged(newGeometry, oldGeometry); -} - - -/*! - \qmlmethod QtQuick2::ListView::incrementCurrentIndex() - - Increments the current index. The current index will wrap - if keyNavigationWraps is true and it is currently at the end. - This method has no effect if the \l count is zero. - - \bold Note: methods should only be called after the Component has completed. -*/ -void QSGListView::incrementCurrentIndex() -{ - Q_D(QSGListView); - int count = d->model ? d->model->count() : 0; - if (count && (currentIndex() < count - 1 || d->wrap)) { - d->moveReason = QSGListViewPrivate::SetIndex; - int index = currentIndex()+1; - setCurrentIndex((index >= 0 && index < count) ? index : 0); - } -} - -/*! - \qmlmethod QtQuick2::ListView::decrementCurrentIndex() - - Decrements the current index. The current index will wrap - if keyNavigationWraps is true and it is currently at the beginning. - This method has no effect if the \l count is zero. - - \bold Note: methods should only be called after the Component has completed. -*/ -void QSGListView::decrementCurrentIndex() -{ - Q_D(QSGListView); - int count = d->model ? d->model->count() : 0; - if (count && (currentIndex() > 0 || d->wrap)) { - d->moveReason = QSGListViewPrivate::SetIndex; - int index = currentIndex()-1; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); - } -} - -void QSGListView::updateSections() -{ - Q_D(QSGListView); - if (isComponentComplete() && d->model) { - QList roles; - if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty()) - roles << d->sectionCriteria->property().toUtf8(); - d->model->setWatchedRoles(roles); - d->updateSections(); - if (d->itemCount) { - d->forceLayout = true; - d->layout(); - } - } -} - -bool QSGListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult) -{ - Q_Q(QSGListView); - - int modelIndex = change.index; - int count = change.count; - - - qreal tempPos = isRightToLeft() ? -position()-size() : position(); - int index = visibleItems.count() ? mapFromModel(modelIndex) : 0; - - if (index < 0) { - int i = visibleItems.count() - 1; - while (i > 0 && visibleItems.at(i)->index == -1) - --i; - if (i == 0 && visibleItems.first()->index == -1) { - // there are no visible items except items marked for removal - index = visibleItems.count(); - } else if (visibleItems.at(i)->index + 1 == modelIndex - && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) { - // Special case of appending an item to the model. - index = visibleItems.count(); - } else { - if (modelIndex < visibleIndex) { - // Insert before visible items - visibleIndex += count; - for (int i = 0; i < visibleItems.count(); ++i) { - FxViewItem *item = visibleItems.at(i); - if (item->index != -1 && item->index >= modelIndex) - item->index += count; - } - } - return true; - } - } - - // index can be the next item past the end of the visible items list (i.e. appended) - int pos = 0; - if (visibleItems.count()) { - pos = index < visibleItems.count() ? visibleItems.at(index)->position() - : visibleItems.last()->endPosition()+spacing; - } - - int prevAddedCount = insertResult->addedItems.count(); - if (firstVisible && pos < firstVisible->position()) { - // Insert items before the visible item. - int insertionIdx = index; - int i = 0; - int from = tempPos - buffer; - - for (i = count-1; i >= 0; --i) { - if (pos > from) { - insertResult->sizeAddedBeforeVisible += averageSize; - pos -= averageSize; - } else { - FxViewItem *item = 0; - if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { - if (item->index > modelIndex + i) - insertResult->movedBackwards.append(item); - item->index = modelIndex + i; - } - if (!item) - item = createItem(modelIndex + i); - - visibleItems.insert(insertionIdx, item); - if (!change.isMove()) { - insertResult->addedItems.append(item); - insertResult->sizeAddedBeforeVisible += item->size(); - } - pos -= item->size() + spacing; - } - index++; - } - } else { - int i = 0; - int to = buffer+tempPos+size(); - for (i = 0; i < count && pos <= to; ++i) { - FxViewItem *item = 0; - if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) { - if (item->index > modelIndex + i) - insertResult->movedBackwards.append(item); - item->index = modelIndex + i; - } - if (!item) - item = createItem(modelIndex + i); - - visibleItems.insert(index, item); - if (!change.isMove()) - insertResult->addedItems.append(item); - pos += item->size() + spacing; - ++index; - } - } - - for (; index < visibleItems.count(); ++index) { - FxViewItem *item = visibleItems.at(index); - if (item->index != -1) - item->index += count; - } - - updateVisibleIndex(); - - return insertResult->addedItems.count() > prevAddedCount; -} - - -/*! - \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode) - - Positions the view such that the \a index is at the position specified by - \a mode: - - \list - \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view. - \o ListView.Center - position item in the center of the view. - \o ListView.End - position item at bottom (or right for horizontal orientation) of the view. - \o ListView.Visible - if any part of the item is visible then take no action, otherwise - bring the item into view. - \o ListView.Contain - ensure the entire item is visible. If the item is larger than - the view the item is positioned at the top (or left for horizontal orientation) of the view. - \endlist - - If positioning the view at \a index would cause empty space to be displayed at - the beginning or end of the view, the view will be positioned at the boundary. - - It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view - at a particular index. This is unreliable since removing items from the start - of the list does not cause all other items to be repositioned, and because - the actual start of the view can vary based on the size of the delegates. - The correct way to bring an item into view is with \c positionViewAtIndex. - - \bold Note: methods should only be called after the Component has completed. To position - the view at startup, this method should be called by Component.onCompleted. For - example, to position the view at the end: - - \code - Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning) - \endcode -*/ - -/*! - \qmlmethod QtQuick2::ListView::positionViewAtBeginning() - \qmlmethod QtQuick2::ListView::positionViewAtEnd() - - Positions the view at the beginning or end, taking into account any header or footer. - - It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view - at a particular index. This is unreliable since removing items from the start - of the list does not cause all other items to be repositioned, and because - the actual start of the view can vary based on the size of the delegates. - - \bold Note: methods should only be called after the Component has completed. To position - the view at startup, this method should be called by Component.onCompleted. For - example, to position the view at the end on startup: - - \code - Component.onCompleted: positionViewAtEnd() - \endcode -*/ - -/*! - \qmlmethod int QtQuick2::ListView::indexAt(int x, int y) - - Returns the index of the visible item containing the point \a x, \a y in content - coordinates. If there is no item at the point specified, or the item is - not visible -1 is returned. - - If the item is outside the visible area, -1 is returned, regardless of - whether an item will exist at that point when scrolled into view. - - \bold Note: methods should only be called after the Component has completed. -*/ - -QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj) -{ - return new QSGListViewAttached(obj); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsglistview_p.h b/src/declarative/items/qsglistview_p.h deleted file mode 100644 index a3c31dfaf4..0000000000 --- a/src/declarative/items/qsglistview_p.h +++ /dev/null @@ -1,215 +0,0 @@ -// Commit: 95814418f9d6adeba365c795462e8afb00138211 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGLISTVIEW_P_H -#define QSGLISTVIEW_P_H - -#include "qsgitemview_p.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGListView; -class QSGListViewPrivate; -class Q_AUTOTEST_EXPORT QSGViewSection : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) - Q_PROPERTY(SectionCriteria criteria READ criteria WRITE setCriteria NOTIFY criteriaChanged) - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(int labelPositioning READ labelPositioning WRITE setLabelPositioning NOTIFY labelPositioningChanged) - Q_ENUMS(SectionCriteria) - Q_ENUMS(LabelPositioning) -public: - QSGViewSection(QSGListView *parent=0); - - QString property() const { return m_property; } - void setProperty(const QString &); - - enum SectionCriteria { FullString, FirstCharacter }; - SectionCriteria criteria() const { return m_criteria; } - void setCriteria(SectionCriteria); - - QDeclarativeComponent *delegate() const { return m_delegate; } - void setDelegate(QDeclarativeComponent *delegate); - - QString sectionString(const QString &value); - - enum LabelPositioning { InlineLabels = 0x01, CurrentLabelAtStart = 0x02, NextLabelAtEnd = 0x04 }; - int labelPositioning() { return m_labelPositioning; } - void setLabelPositioning(int pos); - -Q_SIGNALS: - void propertyChanged(); - void criteriaChanged(); - void delegateChanged(); - void labelPositioningChanged(); - -private: - QString m_property; - SectionCriteria m_criteria; - QDeclarativeComponent *m_delegate; - int m_labelPositioning; - QSGListViewPrivate *m_view; -}; - - -class QSGVisualModel; -class QSGListViewAttached; -class Q_AUTOTEST_EXPORT QSGListView : public QSGItemView -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGListView) - - // XXX deprecate these two properties (only duration should be necessary) - Q_PROPERTY(qreal highlightMoveSpeed READ highlightMoveSpeed WRITE setHighlightMoveSpeed NOTIFY highlightMoveSpeedChanged) - Q_PROPERTY(qreal highlightResizeSpeed READ highlightResizeSpeed WRITE setHighlightResizeSpeed NOTIFY highlightResizeSpeedChanged) - - Q_PROPERTY(int highlightResizeDuration READ highlightResizeDuration WRITE setHighlightResizeDuration NOTIFY highlightResizeDurationChanged) - - Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) - Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) - - Q_PROPERTY(QSGViewSection *section READ sectionCriteria CONSTANT) - Q_PROPERTY(QString currentSection READ currentSection NOTIFY currentSectionChanged) - - Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) - - Q_ENUMS(Orientation) - Q_ENUMS(SnapMode) - Q_CLASSINFO("DefaultProperty", "data") - -public: - QSGListView(QSGItem *parent=0); - ~QSGListView(); - - qreal spacing() const; - void setSpacing(qreal spacing); - - enum Orientation { Horizontal = Qt::Horizontal, Vertical = Qt::Vertical }; - Orientation orientation() const; - void setOrientation(Orientation); - - QSGViewSection *sectionCriteria(); - QString currentSection() const; - - virtual void setHighlightFollowsCurrentItem(bool); - - qreal highlightMoveSpeed() const; - void setHighlightMoveSpeed(qreal); - - qreal highlightResizeSpeed() const; - void setHighlightResizeSpeed(qreal); - - int highlightResizeDuration() const; - void setHighlightResizeDuration(int); - - virtual void setHighlightMoveDuration(int); - - enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; - SnapMode snapMode() const; - void setSnapMode(SnapMode mode); - - static QSGListViewAttached *qmlAttachedProperties(QObject *); - -public Q_SLOTS: - void incrementCurrentIndex(); - void decrementCurrentIndex(); - -Q_SIGNALS: - void spacingChanged(); - void orientationChanged(); - void currentSectionChanged(); - void highlightMoveSpeedChanged(); - void highlightResizeSpeedChanged(); - void highlightResizeDurationChanged(); - void snapModeChanged(); - -protected: - virtual void viewportMoved(); - virtual void keyPressEvent(QKeyEvent *); - virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry); - -protected Q_SLOTS: - void updateSections(); -}; - -class QSGListViewAttached : public QSGItemViewAttached -{ - Q_OBJECT - -public: - QSGListViewAttached(QObject *parent) - : QSGItemViewAttached(parent), m_view(0) {} - ~QSGListViewAttached() {} - - Q_PROPERTY(QSGListView *view READ view NOTIFY viewChanged) - QSGListView *view() { return m_view; } - void setView(QSGListView *view) { - if (view != m_view) { - m_view = view; - emit viewChanged(); - } - } - -Q_SIGNALS: - void viewChanged(); - -public: - QDeclarativeGuard m_view; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPEINFO(QSGListView, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QSGListView) -QML_DECLARE_TYPE(QSGViewSection) - -QT_END_HEADER - -#endif // QSGLISTVIEW_P_H diff --git a/src/declarative/items/qsgloader.cpp b/src/declarative/items/qsgloader.cpp deleted file mode 100644 index e655e179cd..0000000000 --- a/src/declarative/items/qsgloader.cpp +++ /dev/null @@ -1,853 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgloader_p_p.h" - -#include - -#include -#include - -#include - -#include - -QT_BEGIN_NAMESPACE - -QSGLoaderPrivate::QSGLoaderPrivate() - : item(0), component(0), itemContext(0), incubator(0), updatingSize(false), - itemWidthValid(false), itemHeightValid(false), - active(true), loadingFromSource(false), asynchronous(false) -{ -} - -QSGLoaderPrivate::~QSGLoaderPrivate() -{ - delete incubator; - disposeInitialPropertyValues(); -} - -void QSGLoaderPrivate::itemGeometryChanged(QSGItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) -{ - if (resizeItem == item) { - if (!updatingSize && newGeometry.width() != oldGeometry.width()) - itemWidthValid = true; - if (!updatingSize && newGeometry.height() != oldGeometry.height()) - itemHeightValid = true; - _q_updateSize(false); - } - QSGItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); -} - -void QSGLoaderPrivate::clear() -{ - disposeInitialPropertyValues(); - - if (incubator) - incubator->clear(); - - if (loadingFromSource && component) { - component->deleteLater(); - component = 0; - } - source = QUrl(); - - if (item) { - QSGItemPrivate *p = QSGItemPrivate::get(item); - p->removeItemChangeListener(this, QSGItemPrivate::Geometry); - - // We can't delete immediately because our item may have triggered - // the Loader to load a different item. - item->setParentItem(0); - item->setVisible(false); - item->deleteLater(); - item = 0; - } -} - -void QSGLoaderPrivate::initResize() -{ - QSGItemPrivate *p = QSGItemPrivate::get(item); - p->addItemChangeListener(this, QSGItemPrivate::Geometry); - // We may override the item's size, so we need to remember - // whether the item provided its own valid size. - itemWidthValid = p->widthValid; - itemHeightValid = p->heightValid; - _q_updateSize(); -} - -/*! - \qmlclass Loader QSGLoader - \inqmlmodule QtQuick 2 - \ingroup qml-utility-elements - \inherits Item - - \brief The Loader item allows dynamically loading an Item-based - subtree from a URL or Component. - - Loader is used to dynamically load visual QML components. It can load a - QML file (using the \l source property) or a \l Component object (using - the \l sourceComponent property). It is useful for delaying the creation - of a component until it is required: for example, when a component should - be created on demand, or when a component should not be created - unnecessarily for performance reasons. - - Here is a Loader that loads "Page1.qml" as a component when the - \l MouseArea is clicked: - - \snippet doc/src/snippets/declarative/loader/simple.qml 0 - - The loaded item can be accessed using the \l item property. - - If the \l source or \l sourceComponent changes, any previously instantiated - items are destroyed. Setting \l source to an empty string or setting - \l sourceComponent to \c undefined destroys the currently loaded item, - freeing resources and leaving the Loader empty. - - \section2 Loader sizing behavior - - Loader is like any other visual item and must be positioned and sized - accordingly to become visible. - - \list - \o If an explicit size is not specified for the Loader, the Loader - is automatically resized to the size of the loaded item once the - component is loaded. - \o If the size of the Loader is specified explicitly by setting - the width, height or by anchoring, the loaded item will be resized - to the size of the Loader. - \endlist - - In both scenarios the size of the item and the Loader are identical. - This ensures that anchoring to the Loader is equivalent to anchoring - to the loaded item. - - \table - \row - \o sizeloader.qml - \o sizeitem.qml - \row - \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0 - \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0 - \row - \o The red rectangle will be sized to the size of the root item. - \o The red rectangle will be 50x50, centered in the root item. - \endtable - - - \section2 Receiving signals from loaded items - - Any signals emitted from the loaded item can be received using the - \l Connections element. For example, the following \c application.qml - loads \c MyItem.qml, and is able to receive the \c message signal from - the loaded item through a \l Connections object: - - \table - \row - \o application.qml - \o MyItem.qml - \row - \o \snippet doc/src/snippets/declarative/loader/connections.qml 0 - \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0 - \endtable - - Alternatively, since \c MyItem.qml is loaded within the scope of the - Loader, it could also directly call any function defined in the Loader or - its parent \l Item. - - - \section2 Focus and key events - - Loader is a focus scope. Its \l {Item::}{focus} property must be set to - \c true for any of its children to get the \e {active focus}. (See - \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} - for more details.) Any key events received in the loaded item should likely - also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader. - - For example, the following \c application.qml loads \c KeyReader.qml when - the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is - set to \c true for the Loader as well as the \l Item in the dynamically - loaded object: - - \table - \row - \o application.qml - \o KeyReader.qml - \row - \o \snippet doc/src/snippets/declarative/loader/focus.qml 0 - \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0 - \endtable - - Once \c KeyReader.qml is loaded, it accepts key events and sets - \c event.accepted to \c true so that the event is not propagated to the - parent \l Rectangle. - - \sa {dynamic-object-creation}{Dynamic Object Creation} -*/ - -QSGLoader::QSGLoader(QSGItem *parent) - : QSGImplicitSizeItem(*(new QSGLoaderPrivate), parent) -{ - setFlag(ItemIsFocusScope); -} - -QSGLoader::~QSGLoader() -{ - Q_D(QSGLoader); - if (d->item) { - QSGItemPrivate *p = QSGItemPrivate::get(d->item); - p->removeItemChangeListener(d, QSGItemPrivate::Geometry); - } -} - -/*! - \qmlproperty bool QtQuick2::Loader::active - This property is \c true if the Loader is currently active. - The default value for the \l active property is \c true. - - If the Loader is inactive, changing the \l source or \l sourceComponent - will not cause the item to be instantiated until the Loader is made active. - - Setting the value to inactive will cause any \l item loaded by the loader - to be released, but will not affect the \l source or \l sourceComponent. - - The \l status of an inactive loader is always \c Null. - - \sa source, sourceComponent - */ -bool QSGLoader::active() const -{ - Q_D(const QSGLoader); - return d->active; -} - -void QSGLoader::setActive(bool newVal) -{ - Q_D(QSGLoader); - if (d->active != newVal) { - d->active = newVal; - if (newVal == true) { - if (d->loadingFromSource) { - loadFromSource(); - } else { - loadFromSourceComponent(); - } - } else { - if (d->item) { - QSGItemPrivate *p = QSGItemPrivate::get(d->item); - p->removeItemChangeListener(d, QSGItemPrivate::Geometry); - - // We can't delete immediately because our item may have triggered - // the Loader to load a different item. - d->item->setParentItem(0); - d->item->setVisible(false); - d->item->deleteLater(); - d->item = 0; - emit itemChanged(); - } - emit statusChanged(); - } - emit activeChanged(); - } -} - - -/*! - \qmlproperty url QtQuick2::Loader::source - This property holds the URL of the QML component to instantiate. - - Note the QML component must be an \l{Item}-based component. The loader - cannot load non-visual components. - - To unload the currently loaded item, set this property to an empty string, - or set \l sourceComponent to \c undefined. Setting \c source to a - new URL will also cause the item created by the previous URL to be unloaded. - - \sa sourceComponent, status, progress -*/ -QUrl QSGLoader::source() const -{ - Q_D(const QSGLoader); - return d->source; -} - -void QSGLoader::setSource(const QUrl &url) -{ - setSource(url, true); // clear previous values -} - -void QSGLoader::setSource(const QUrl &url, bool needsClear) -{ - Q_D(QSGLoader); - if (d->source == url) - return; - - if (needsClear) - d->clear(); - - d->source = url; - d->loadingFromSource = true; - - if (d->active) - loadFromSource(); - else - emit sourceChanged(); -} - -void QSGLoader::loadFromSource() -{ - Q_D(QSGLoader); - if (d->source.isEmpty()) { - emit sourceChanged(); - emit statusChanged(); - emit progressChanged(); - emit itemChanged(); - return; - } - - if (isComponentComplete()) { - d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this); - d->load(); - } -} - -/*! - \qmlproperty Component QtQuick2::Loader::sourceComponent - This property holds the \l{Component} to instantiate. - - \qml - Item { - Component { - id: redSquare - Rectangle { color: "red"; width: 10; height: 10 } - } - - Loader { sourceComponent: redSquare } - Loader { sourceComponent: redSquare; x: 10 } - } - \endqml - - To unload the currently loaded item, set this property to an empty string - or \c undefined. - - \sa source, progress -*/ - -QDeclarativeComponent *QSGLoader::sourceComponent() const -{ - Q_D(const QSGLoader); - return d->component; -} - -void QSGLoader::setSourceComponent(QDeclarativeComponent *comp) -{ - Q_D(QSGLoader); - if (comp == d->component) - return; - - d->clear(); - - d->component = comp; - d->loadingFromSource = false; - - if (d->active) - loadFromSourceComponent(); - else - emit sourceComponentChanged(); -} - -void QSGLoader::resetSourceComponent() -{ - setSourceComponent(0); -} - -void QSGLoader::loadFromSourceComponent() -{ - Q_D(QSGLoader); - if (!d->component) { - emit sourceComponentChanged(); - emit statusChanged(); - emit progressChanged(); - emit itemChanged(); - return; - } - - if (isComponentComplete()) - d->load(); -} - -/*! - \qmlmethod object QtQuick2::Loader::setSource(url source, object properties) - - Creates an object instance of the given \a source component that will have - the given \a properties. The \a properties argument is optional. The instance - will be accessible via the \l item property once loading and instantiation - is complete. - - If the \l active property is \c false at the time when this function is called, - the given \a source component will not be loaded but the \a source and initial - \a properties will be cached. When the loader is made \l active, an instance of - the \a source component will be created with the initial \a properties set. - - Setting the initial property values of an instance of a component in this manner - will \e not trigger any associated \l{Behavior}s. - - Note that the cached \a properties will be cleared if the \l source or \l sourceComponent - is changed after calling this function but prior to setting the loader \l active. - - Example: - \table - \row - \o - \qml - // ExampleComponent.qml - import QtQuick 2.0 - Rectangle { - id: rect - color: "red" - width: 10 - height: 10 - - Behavior on color { - NumberAnimation { - target: rect - property: "width" - to: (rect.width + 20) - duration: 0 - } - } - } - \endqml - \o - \qml - // example.qml - import QtQuick 2.0 - Item { - Loader { - id: squareLoader - onLoaded: console.log(squareLoader.item.width); // prints [10], not [30] - } - - Component.onCompleted: { - squareLoader.setSource("ExampleComponent.qml", { "color": "blue" }); - // will trigger the onLoaded code when complete. - } - } - \endqml - \endtable - - \sa source, active -*/ -void QSGLoader::setSource(QDeclarativeV8Function *args) -{ - Q_ASSERT(args); - Q_D(QSGLoader); - - bool ipvError = false; - args->returnValue(v8::Undefined()); - v8::Handle ipv = d->extractInitialPropertyValues(args, this, &ipvError); - if (ipvError) - return; - - d->clear(); - QUrl sourceUrl = d->resolveSourceUrl(args); - if (!ipv.IsEmpty()) { - d->disposeInitialPropertyValues(); - d->initialPropertyValues = qPersistentNew(ipv); - d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal()); - } - - setSource(sourceUrl, false); // already cleared and set ipv above. -} - -void QSGLoaderPrivate::disposeInitialPropertyValues() -{ - if (!initialPropertyValues.IsEmpty()) - qPersistentDispose(initialPropertyValues); - if (!qmlGlobalForIpv.IsEmpty()) - qPersistentDispose(qmlGlobalForIpv); -} - -void QSGLoaderPrivate::load() -{ - Q_Q(QSGLoader); - - if (!q->isComponentComplete() || !component) - return; - - if (!component->isLoading()) { - _q_sourceLoaded(); - } else { - QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), - q, SLOT(_q_sourceLoaded())); - QObject::connect(component, SIGNAL(progressChanged(qreal)), - q, SIGNAL(progressChanged())); - emit q->statusChanged(); - emit q->progressChanged(); - if (loadingFromSource) - emit q->sourceChanged(); - else - emit q->sourceComponentChanged(); - emit q->itemChanged(); - } -} - -void QSGLoaderIncubator::setInitialState(QObject *o) -{ - loader->setInitialState(o); -} - -void QSGLoaderPrivate::setInitialState(QObject *obj) -{ - Q_Q(QSGLoader); - - QSGItem *item = qobject_cast(obj); - if (item) { - QDeclarative_setParent_noEvent(itemContext, obj); - QDeclarative_setParent_noEvent(item, q); - item->setParentItem(q); - } - - if (initialPropertyValues.IsEmpty()) - return; - - QDeclarativeComponentPrivate *d = QDeclarativeComponentPrivate::get(component); - Q_ASSERT(d && d->engine); - d->initializeObjectWithInitialProperties(qmlGlobalForIpv, initialPropertyValues, obj); -} - -void QSGLoaderIncubator::statusChanged(Status status) -{ - loader->incubatorStateChanged(status); -} - -void QSGLoaderPrivate::incubatorStateChanged(QDeclarativeIncubator::Status status) -{ - Q_Q(QSGLoader); - if (status == QDeclarativeIncubator::Loading || status == QDeclarativeIncubator::Null) - return; - - if (status == QDeclarativeIncubator::Ready) { - QObject *obj = incubator->object(); - item = qobject_cast(obj); - if (item) { - initResize(); - } else { - qmlInfo(q) << QSGLoader::tr("Loader does not support loading non-visual elements."); - delete itemContext; - itemContext = 0; - delete obj; - } - } else if (status == QDeclarativeIncubator::Error) { - if (!incubator->errors().isEmpty()) - QDeclarativeEnginePrivate::warning(qmlEngine(q), incubator->errors()); - delete itemContext; - itemContext = 0; - delete incubator->object(); - source = QUrl(); - } - if (loadingFromSource) - emit q->sourceChanged(); - else - emit q->sourceComponentChanged(); - emit q->statusChanged(); - emit q->progressChanged(); - emit q->itemChanged(); - emit q->loaded(); - disposeInitialPropertyValues(); // cleanup -} - -void QSGLoaderPrivate::_q_sourceLoaded() -{ - Q_Q(QSGLoader); - if (!component || !component->errors().isEmpty()) { - if (component) - QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors()); - if (loadingFromSource) - emit q->sourceChanged(); - else - emit q->sourceComponentChanged(); - emit q->statusChanged(); - emit q->progressChanged(); - disposeInitialPropertyValues(); // cleanup - return; - } - - QDeclarativeContext *creationContext = component->creationContext(); - if (!creationContext) creationContext = qmlContext(q); - itemContext = new QDeclarativeContext(creationContext); - itemContext->setContextObject(q); - - if (incubator) { - bool async = incubator->incubationMode() == QDeclarativeIncubator::Asynchronous; - if (asynchronous != async) { - delete incubator; - incubator = 0; - } - } - if (!incubator) - incubator = new QSGLoaderIncubator(this, asynchronous ? QDeclarativeIncubator::Asynchronous : QDeclarativeIncubator::AsynchronousIfNested); - - component->create(*incubator, itemContext); - - if (incubator->status() == QDeclarativeIncubator::Loading) - emit q->statusChanged(); -} - -/*! - \qmlproperty enumeration QtQuick2::Loader::status - - This property holds the status of QML loading. It can be one of: - \list - \o Loader.Null - the loader is inactive or no QML source has been set - \o Loader.Ready - the QML source has been loaded - \o Loader.Loading - the QML source is currently being loaded - \o Loader.Error - an error occurred while loading the QML source - \endlist - - Use this status to provide an update or respond to the status change in some way. - For example, you could: - - \list - \o Trigger a state change: - \qml - State { name: 'loaded'; when: loader.status == Loader.Ready } - \endqml - - \o Implement an \c onStatusChanged signal handler: - \qml - Loader { - id: loader - onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded') - } - \endqml - - \o Bind to the status value: - \qml - Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' } - \endqml - \endlist - - Note that if the source is a local file, the status will initially be Ready (or Error). While - there will be no onStatusChanged signal in that case, the onLoaded will still be invoked. - - \sa progress -*/ - -QSGLoader::Status QSGLoader::status() const -{ - Q_D(const QSGLoader); - - if (!d->active) - return Null; - - if (d->component) { - switch (d->component->status()) { - case QDeclarativeComponent::Loading: - return Loading; - case QDeclarativeComponent::Error: - return Error; - case QDeclarativeComponent::Null: - return Null; - default: - break; - } - } - - if (d->incubator) { - switch (d->incubator->status()) { - case QDeclarativeIncubator::Loading: - return Loading; - case QDeclarativeIncubator::Error: - return Error; - default: - break; - } - } - - if (d->item) - return Ready; - - return d->source.isEmpty() ? Null : Error; -} - -void QSGLoader::componentComplete() -{ - Q_D(QSGLoader); - QSGItem::componentComplete(); - if (active()) { - if (d->loadingFromSource) { - d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this); - } - d->load(); - } -} - -/*! - \qmlsignal QtQuick2::Loader::onLoaded() - - This handler is called when the \l status becomes \c Loader.Ready, or on successful - initial load. -*/ - - -/*! -\qmlproperty real QtQuick2::Loader::progress - -This property holds the progress of loading QML data from the network, from -0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so -this value will rapidly change from 0 to 1. - -\sa status -*/ -qreal QSGLoader::progress() const -{ - Q_D(const QSGLoader); - - if (d->item) - return 1.0; - - if (d->component) - return d->component->progress(); - - return 0.0; -} - -/*! -\qmlproperty bool QtQuick2::Loader::asynchronous - -This property holds whether the component will be instantiated asynchronously. - -Note that this property affects object instantiation only; it is unrelated to -loading a component asynchronously via a network. -*/ -bool QSGLoader::asynchronous() const -{ - Q_D(const QSGLoader); - return d->asynchronous; -} - -void QSGLoader::setAsynchronous(bool a) -{ - Q_D(QSGLoader); - if (d->asynchronous == a) - return; - - d->asynchronous = a; - emit asynchronousChanged(); -} - -void QSGLoaderPrivate::_q_updateSize(bool loaderGeometryChanged) -{ - Q_Q(QSGLoader); - if (!item || updatingSize) - return; - - updatingSize = true; - - if (!itemWidthValid) - q->setImplicitWidth(item->implicitWidth()); - else - q->setImplicitWidth(item->width()); - if (loaderGeometryChanged && q->widthValid()) - item->setWidth(q->width()); - - if (!itemHeightValid) - q->setImplicitHeight(item->implicitHeight()); - else - q->setImplicitHeight(item->height()); - if (loaderGeometryChanged && q->heightValid()) - item->setHeight(q->height()); - - updatingSize = false; -} - -/*! - \qmlproperty Item QtQuick2::Loader::item - This property holds the top-level item that is currently loaded. -*/ -QSGItem *QSGLoader::item() const -{ - Q_D(const QSGLoader); - return d->item; -} - -void QSGLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_D(QSGLoader); - if (newGeometry != oldGeometry) { - d->_q_updateSize(); - } - QSGItem::geometryChanged(newGeometry, oldGeometry); -} - -QUrl QSGLoaderPrivate::resolveSourceUrl(QDeclarativeV8Function *args) -{ - QV8Engine *v8engine = args->engine(); - QString arg = v8engine->toString((*args)[0]->ToString()); - if (arg.isEmpty()) - return QUrl(); - - QDeclarativeContextData *context = args->context(); - Q_ASSERT(context); - return context->resolvedUrl(QUrl(arg)); -} - -v8::Handle QSGLoaderPrivate::extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error) -{ - v8::Local valuemap; - if (args->Length() >= 2) { - v8::Local v = (*args)[1]; - if (!v->IsObject() || v->IsArray()) { - *error = true; - qmlInfo(loader) << loader->tr("setSource: value is not an object"); - } else { - *error = false; - valuemap = v8::Local::Cast(v); - } - } - - return valuemap; -} - -#include - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgloader_p.h b/src/declarative/items/qsgloader_p.h deleted file mode 100644 index 3a41e79915..0000000000 --- a/src/declarative/items/qsgloader_p.h +++ /dev/null @@ -1,123 +0,0 @@ -// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGLOADER_P_H -#define QSGLOADER_P_H - -#include "qsgimplicitsizeitem_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGLoaderPrivate; -class Q_AUTOTEST_EXPORT QSGLoader : public QSGImplicitSizeItem -{ - Q_OBJECT - Q_ENUMS(Status) - - Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - Q_PROPERTY(QDeclarativeComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged) - Q_PROPERTY(QSGItem *item READ item NOTIFY itemChanged) - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) - Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) - -public: - QSGLoader(QSGItem *parent = 0); - virtual ~QSGLoader(); - - bool active() const; - void setActive(bool newVal); - - Q_INVOKABLE void setSource(QDeclarativeV8Function *); - - QUrl source() const; - void setSource(const QUrl &); - - QDeclarativeComponent *sourceComponent() const; - void setSourceComponent(QDeclarativeComponent *); - void resetSourceComponent(); - - enum Status { Null, Ready, Loading, Error }; - Status status() const; - qreal progress() const; - - bool asynchronous() const; - void setAsynchronous(bool a); - - QSGItem *item() const; - -Q_SIGNALS: - void itemChanged(); - void activeChanged(); - void sourceChanged(); - void sourceComponentChanged(); - void statusChanged(); - void progressChanged(); - void loaded(); - void asynchronousChanged(); - -protected: - void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - void componentComplete(); - -private: - void setSource(const QUrl &sourceUrl, bool needsClear); - void loadFromSource(); - void loadFromSourceComponent(); - Q_DISABLE_COPY(QSGLoader) - Q_DECLARE_PRIVATE(QSGLoader) - Q_PRIVATE_SLOT(d_func(), void _q_sourceLoaded()) - Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGLoader) - -QT_END_HEADER - -#endif // QSGLOADER_P_H diff --git a/src/declarative/items/qsgloader_p_p.h b/src/declarative/items/qsgloader_p_p.h deleted file mode 100644 index 36059ca51a..0000000000 --- a/src/declarative/items/qsgloader_p_p.h +++ /dev/null @@ -1,121 +0,0 @@ -// Commit: 5d2817cd668a705729df1727de49adf00713ac97 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGLOADER_P_P_H -#define QSGLOADER_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgloader_p.h" -#include "qsgimplicitsizeitem_p_p.h" -#include "qsgitemchangelistener_p.h" -#include - -#include - -QT_BEGIN_NAMESPACE - - -class QSGLoaderPrivate; -class QSGLoaderIncubator : public QDeclarativeIncubator -{ -public: - QSGLoaderIncubator(QSGLoaderPrivate *l, IncubationMode mode) : QDeclarativeIncubator(mode), loader(l) {} - -protected: - virtual void statusChanged(Status); - virtual void setInitialState(QObject *); - -private: - QSGLoaderPrivate *loader; -}; - -class QDeclarativeContext; -class QSGLoaderPrivate : public QSGImplicitSizeItemPrivate, public QSGItemChangeListener -{ - Q_DECLARE_PUBLIC(QSGLoader) - -public: - QSGLoaderPrivate(); - ~QSGLoaderPrivate(); - - void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); - void clear(); - void initResize(); - void load(); - - void incubatorStateChanged(QDeclarativeIncubator::Status status); - void setInitialState(QObject *o); - void disposeInitialPropertyValues(); - QUrl resolveSourceUrl(QDeclarativeV8Function *args); - v8::Handle extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error); - - QUrl source; - QSGItem *item; - QDeclarativeComponent *component; - QDeclarativeContext *itemContext; - QSGLoaderIncubator *incubator; - v8::Persistent initialPropertyValues; - v8::Persistent qmlGlobalForIpv; - bool updatingSize: 1; - bool itemWidthValid : 1; - bool itemHeightValid : 1; - bool active : 1; - bool loadingFromSource : 1; - bool asynchronous : 1; - - void _q_sourceLoaded(); - void _q_updateSize(bool loaderGeometryChanged = true); -}; - -QT_END_NAMESPACE - -#endif // QSGLOADER_P_P_H diff --git a/src/declarative/items/qsgmousearea.cpp b/src/declarative/items/qsgmousearea.cpp deleted file mode 100644 index a401b52e95..0000000000 --- a/src/declarative/items/qsgmousearea.cpp +++ /dev/null @@ -1,1131 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgmousearea_p.h" -#include "qsgmousearea_p_p.h" -#include "qsgcanvas.h" -#include "qsgevents_p_p.h" -#include "qsgdrag_p.h" - -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE -static const int PressAndHoldDelay = 800; - -QSGDrag::QSGDrag(QObject *parent) -: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), -_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false) -{ -} - -QSGDrag::~QSGDrag() -{ -} - -QSGItem *QSGDrag::target() const -{ - return _target; -} - -void QSGDrag::setTarget(QSGItem *t) -{ - if (_target == t) - return; - _target = t; - emit targetChanged(); -} - -void QSGDrag::resetTarget() -{ - if (_target == 0) - return; - _target = 0; - emit targetChanged(); -} - -QSGDrag::Axis QSGDrag::axis() const -{ - return _axis; -} - -void QSGDrag::setAxis(QSGDrag::Axis a) -{ - if (_axis == a) - return; - _axis = a; - emit axisChanged(); -} - -qreal QSGDrag::xmin() const -{ - return _xmin; -} - -void QSGDrag::setXmin(qreal m) -{ - if (_xmin == m) - return; - _xmin = m; - emit minimumXChanged(); -} - -qreal QSGDrag::xmax() const -{ - return _xmax; -} - -void QSGDrag::setXmax(qreal m) -{ - if (_xmax == m) - return; - _xmax = m; - emit maximumXChanged(); -} - -qreal QSGDrag::ymin() const -{ - return _ymin; -} - -void QSGDrag::setYmin(qreal m) -{ - if (_ymin == m) - return; - _ymin = m; - emit minimumYChanged(); -} - -qreal QSGDrag::ymax() const -{ - return _ymax; -} - -void QSGDrag::setYmax(qreal m) -{ - if (_ymax == m) - return; - _ymax = m; - emit maximumYChanged(); -} - -bool QSGDrag::active() const -{ - return _active; -} - -void QSGDrag::setActive(bool drag) -{ - if (_active == drag) - return; - _active = drag; - emit activeChanged(); -} - -bool QSGDrag::filterChildren() const -{ - return _filterChildren; -} - -void QSGDrag::setFilterChildren(bool filter) -{ - if (_filterChildren == filter) - return; - _filterChildren = filter; - emit filterChildrenChanged(); -} - -QSGDragAttached *QSGDrag::qmlAttachedProperties(QObject *obj) -{ - return new QSGDragAttached(obj); -} - -QSGMouseAreaPrivate::QSGMouseAreaPrivate() -: absorb(true), hovered(false), pressed(false), longPress(false), - moved(false), stealMouse(false), doubleClick(false), preventStealing(false), - drag(0) -{ -} - -QSGMouseAreaPrivate::~QSGMouseAreaPrivate() -{ - delete drag; -} - -void QSGMouseAreaPrivate::init() -{ - Q_Q(QSGMouseArea); - q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFiltersChildMouseEvents(true); -} - -void QSGMouseAreaPrivate::saveEvent(QMouseEvent *event) -{ - lastPos = event->localPos(); - lastScenePos = event->windowPos(); - lastButton = event->button(); - lastButtons = event->buttons(); - lastModifiers = event->modifiers(); -} - -bool QSGMouseAreaPrivate::isPressAndHoldConnected() -{ - Q_Q(QSGMouseArea); - static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QSGMouseEvent*)"); - return QObjectPrivate::get(q)->isSignalConnected(idx); -} - -bool QSGMouseAreaPrivate::isDoubleClickConnected() -{ - Q_Q(QSGMouseArea); - static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QSGMouseEvent*)"); - return QObjectPrivate::get(q)->isSignalConnected(idx); -} - -bool QSGMouseAreaPrivate::isClickConnected() -{ - Q_Q(QSGMouseArea); - static int idx = QObjectPrivate::get(q)->signalIndex("clicked(QSGMouseEvent*)"); - return QObjectPrivate::get(q)->isSignalConnected(idx); -} - -void QSGMouseAreaPrivate::propagate(QSGMouseEvent* event, PropagateType t) -{ - Q_Q(QSGMouseArea); - QPointF scenePos = q->mapToScene(QPointF(event->x(), event->y())); - propagateHelper(event, canvas->rootItem(), scenePos, t); -} - -bool QSGMouseAreaPrivate::propagateHelper(QSGMouseEvent *ev, QSGItem *item,const QPointF &sp, PropagateType sig) -{ - //Based off of QSGCanvas::deliverInitialMousePressEvent - //But specific to MouseArea, so doesn't belong in canvas - Q_Q(const QSGMouseArea); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(sp); - if (!QRectF(0, 0, item->width(), item->height()).contains(p)) - return false; - } - - QList children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QSGItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled()) - continue; - if (propagateHelper(ev, child, sp, sig)) - return true; - } - - QSGMouseArea* ma = qobject_cast(item); - if (ma && ma != q && itemPrivate->acceptedMouseButtons & ev->button()) { - switch (sig) { - case Click: - if (!ma->d_func()->isClickConnected()) - return false; - break; - case DoubleClick: - if (!ma->d_func()->isDoubleClickConnected()) - return false; - break; - case PressAndHold: - if (!ma->d_func()->isPressAndHoldConnected()) - return false; - break; - } - QPointF p = item->mapFromScene(sp); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - ev->setX(p.x()); - ev->setY(p.y()); - ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide - switch (sig) { - case Click: emit ma->clicked(ev); break; - case DoubleClick: emit ma->doubleClicked(ev); break; - case PressAndHold: emit ma->pressAndHold(ev); break; - } - if (ev->isAccepted()) - return true; - } - } - return false; - -} - -/*! - \qmlclass MouseArea QSGMouseArea - \inqmlmodule QtQuick 2 - \ingroup qml-basic-interaction-elements - \brief The MouseArea item enables simple mouse handling. - \inherits Item - - A MouseArea is an invisible item that is typically used in conjunction with - a visible item in order to provide mouse handling for that item. - By effectively acting as a proxy, the logic for mouse handling can be - contained within a MouseArea item. - - For basic key handling, see the \l{Keys}{Keys attached property}. - - The \l enabled property is used to enable and disable mouse handling for - the proxied item. When disabled, the mouse area becomes transparent to - mouse events. - - The \l pressed read-only property indicates whether or not the user is - holding down a mouse button over the mouse area. This property is often - used in bindings between properties in a user interface. The containsMouse - read-only property indicates the presence of the mouse cursor over the - mouse area but, by default, only when a mouse button is held down; see below - for further details. - - Information about the mouse position and button clicks are provided via - signals for which event handler properties are defined. The most commonly - used involved handling mouse presses and clicks: onClicked, onDoubleClicked, - onPressed, onReleased and onPressAndHold. - - By default, MouseArea items only report mouse clicks and not changes to the - position of the mouse cursor. Setting the hoverEnabled property ensures that - handlers defined for onPositionChanged, onEntered and onExited are used and - that the containsMouse property is updated even when no mouse buttons are - pressed. - - \section1 Example Usage - - \div {class="float-right"} - \inlineimage qml-mousearea-snippet.png - \enddiv - - The following example uses a MouseArea in a \l Rectangle that changes - the \l Rectangle color to red when clicked: - - \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import - \codeline - \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro - - \clearfloat - Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains - additional information about the mouse event, such as the position, button, - and any key modifiers. - - Here is an extension of the previous example that produces a different - color when the area is right clicked: - - \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended - - Behavioral Change in QtQuick 2.0 - - From QtQuick 2.0, the signals clicked, doubleClicked and pressAndHold have a different interaction - model with regards to the delivery of events to multiple overlapping MouseAreas. These signals will now propagate - to all MouseAreas in the area, in painting order, until accepted by one of them. A signal is accepted by - default if there is a signal handler for it, use mouse.accepted = false; to ignore. This propagation - can send the signal to MouseAreas other than the one which accepted the press event, although that MouseArea - will receive the signal first. - - Note that to get the same behavior as a QtQuick 1.0 MouseArea{} with regard to absorbing all mouse events, you will - now need to add empty signal handlers for these three signals. - - \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example} -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onEntered() - - This handler is called when the mouse enters the mouse area. - - By default the onEntered handler is only called while a button is - pressed. Setting hoverEnabled to true enables handling of - onEntered when no mouse button is pressed. - - \sa hoverEnabled -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onExited() - - This handler is called when the mouse exits the mouse area. - - By default the onExited handler is only called while a button is - pressed. Setting hoverEnabled to true enables handling of - onExited when no mouse button is pressed. - - The example below shows a fairly typical relationship between - two MouseAreas, with \c mouseArea2 on top of \c mouseArea1. Moving the - mouse into \c mouseArea2 from \c mouseArea1 will cause \c onExited - to be called for \c mouseArea1. - \qml - Rectangle { - width: 400; height: 400 - MouseArea { - id: mouseArea1 - anchors.fill: parent - hoverEnabled: true - } - MouseArea { - id: mouseArea2 - width: 100; height: 100 - anchors.centerIn: parent - hoverEnabled: true - } - } - \endqml - - If instead you give the two mouseAreas a parent-child relationship, - moving the mouse into \c mouseArea2 from \c mouseArea1 will \b not - cause \c onExited to be called for \c mouseArea1. Instead, they will - both be considered to be simultaneously hovered. - - \sa hoverEnabled -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onPositionChanged(MouseEvent mouse) - - This handler is called when the mouse position changes. - - The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y - position, and any buttons currently pressed. - - The \e accepted property of the MouseEvent parameter is ignored in this handler. - - By default the onPositionChanged handler is only called while a button is - pressed. Setting hoverEnabled to true enables handling of - onPositionChanged when no mouse button is pressed. -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onClicked(MouseEvent mouse) - - This handler is called when there is a click. A click is defined as a press followed by a release, - both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and - releasing is also considered a click). - - The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y - position of the release of the click, and whether the click was held. - - The \e accepted property of the MouseEvent parameter is ignored in this handler. -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onPressed(MouseEvent mouse) - - This handler is called when there is a press. - The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y - position and which button was pressed. - - The \e accepted property of the MouseEvent parameter determines whether this MouseArea - will handle the press and all future mouse events until release. The default is to accept - the event and not allow other MouseArea beneath this one to handle the event. If \e accepted - is set to false, no further events will be sent to this MouseArea until the button is next - pressed. -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onReleased(MouseEvent mouse) - - This handler is called when there is a release. - The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y - position of the release of the click, and whether the click was held. - - The \e accepted property of the MouseEvent parameter is ignored in this handler. - - \sa onCanceled -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onPressAndHold(MouseEvent mouse) - - This handler is called when there is a long press (currently 800ms). - The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y - position of the press, and which button is pressed. - - The \e accepted property of the MouseEvent parameter is ignored in this handler. -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onDoubleClicked(MouseEvent mouse) - - This handler is called when there is a double-click (a press followed by a release followed by a press). - The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y - position of the release of the click, and whether the click was held. - - If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false - in the handler, the onPressed/onReleased/onClicked handlers will be called for the second - click; otherwise they are suppressed. The accepted property defaults to true. -*/ - -/*! - \qmlsignal QtQuick2::MouseArea::onCanceled() - - This handler is called when mouse events have been canceled, either because an event was not accepted, or - because another element stole the mouse event handling. - - This signal is for advanced use: it is useful when there is more than one MouseArea - that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter - case, if you execute some logic on the pressed signal and then start dragging, the - \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset - the logic when the MouseArea has lost the mouse handling to the \l Flickable, - \c onCanceled should be used in addition to onReleased. -*/ -QSGMouseArea::QSGMouseArea(QSGItem *parent) - : QSGItem(*(new QSGMouseAreaPrivate), parent) -{ - Q_D(QSGMouseArea); - d->init(); -} - -QSGMouseArea::~QSGMouseArea() -{ -} - -/*! - \qmlproperty real QtQuick2::MouseArea::mouseX - \qmlproperty real QtQuick2::MouseArea::mouseY - These properties hold the coordinates of the mouse cursor. - - If the hoverEnabled property is false then these properties will only be valid - while a button is pressed, and will remain valid as long as the button is held - down even if the mouse is moved outside the area. - - By default, this property is false. - - If hoverEnabled is true then these properties will be valid when: - \list - \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true). - \i a button is pressed and held, even if it has since moved out of the area. - \endlist - - The coordinates are relative to the MouseArea. -*/ -qreal QSGMouseArea::mouseX() const -{ - Q_D(const QSGMouseArea); - return d->lastPos.x(); -} - -qreal QSGMouseArea::mouseY() const -{ - Q_D(const QSGMouseArea); - return d->lastPos.y(); -} - -/*! - \qmlproperty bool QtQuick2::MouseArea::enabled - This property holds whether the item accepts mouse events. - - By default, this property is true. -*/ -bool QSGMouseArea::isEnabled() const -{ - Q_D(const QSGMouseArea); - return d->absorb; -} - -void QSGMouseArea::setEnabled(bool a) -{ - Q_D(QSGMouseArea); - if (a != d->absorb) { - d->absorb = a; - emit enabledChanged(); - } -} - -/*! - \qmlproperty bool QtQuick2::MouseArea::preventStealing - This property holds whether the mouse events may be stolen from this - MouseArea. - - If a MouseArea is placed within an item that filters child mouse - events, such as Flickable, the mouse - events may be stolen from the MouseArea if a gesture is recognized - by the parent element, e.g. a flick gesture. If preventStealing is - set to true, no element will steal the mouse events. - - Note that setting preventStealing to true once an element has started - stealing events will have no effect until the next press event. - - By default this property is false. -*/ -bool QSGMouseArea::preventStealing() const -{ - Q_D(const QSGMouseArea); - return d->preventStealing; -} - -void QSGMouseArea::setPreventStealing(bool prevent) -{ - Q_D(QSGMouseArea); - if (prevent != d->preventStealing) { - d->preventStealing = prevent; - setKeepMouseGrab(d->preventStealing && d->absorb); - emit preventStealingChanged(); - } -} - -/*! - \qmlproperty MouseButtons QtQuick2::MouseArea::pressedButtons - This property holds the mouse buttons currently pressed. - - It contains a bitwise combination of: - \list - \o Qt.LeftButton - \o Qt.RightButton - \o Qt.MiddleButton - \endlist - - The code below displays "right" when the right mouse buttons is pressed: - - \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons - - \sa acceptedButtons -*/ -Qt::MouseButtons QSGMouseArea::pressedButtons() const -{ - Q_D(const QSGMouseArea); - return d->lastButtons; -} - -void QSGMouseArea::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGMouseArea); - d->moved = false; - d->stealMouse = d->preventStealing; - if (!d->absorb) - QSGItem::mousePressEvent(event); - else { - d->longPress = false; - d->saveEvent(event); - if (d->drag) { - d->dragX = drag()->axis() & QSGDrag::XAxis; - d->dragY = drag()->axis() & QSGDrag::YAxis; - } - if (d->drag) - d->drag->setActive(false); - setHovered(true); - d->startScene = event->windowPos(); - d->pressAndHoldTimer.start(PressAndHoldDelay, this); - setKeepMouseGrab(d->stealMouse); - event->setAccepted(setPressed(true)); - - } -} - -void QSGMouseArea::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QSGMouseArea); - if (!d->absorb) { - QSGItem::mouseMoveEvent(event); - return; - } - - d->saveEvent(event); - - // ### we should skip this if these signals aren't used - // ### can GV handle this for us? - bool contains = boundingRect().contains(d->lastPos); - if (d->hovered && !contains) - setHovered(false); - else if (!d->hovered && contains) - setHovered(true); - - if (d->drag && d->drag->target()) { - if (!d->moved) { - d->targetStartPos = d->drag->target()->parentItem() - ? d->drag->target()->parentItem()->mapToScene(d->drag->target()->pos()) - : d->drag->target()->pos(); - } - - QPointF startLocalPos; - QPointF curLocalPos; - if (drag()->target()->parentItem()) { - startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene); - curLocalPos = drag()->target()->parentItem()->mapFromScene(event->windowPos()); - } else { - startLocalPos = d->startScene; - curLocalPos = event->windowPos(); - } - - const int dragThreshold = qApp->styleHints()->startDragDistance(); - qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); - qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); - - if (keepMouseGrab() && d->stealMouse && !d->drag->active()) - d->drag->setActive(true); - - QPointF startPos = d->drag->target()->parentItem() - ? d->drag->target()->parentItem()->mapFromScene(d->targetStartPos) - : d->targetStartPos; - - QPointF dragPos = d->drag->target()->pos(); - - if (d->dragX && d->drag->active()) { - qreal x = (curLocalPos.x() - startLocalPos.x()) + startPos.x(); - if (x < drag()->xmin()) - x = drag()->xmin(); - else if (x > drag()->xmax()) - x = drag()->xmax(); - dragPos.setX(x); - } - if (d->dragY && d->drag->active()) { - qreal y = (curLocalPos.y() - startLocalPos.y()) + startPos.y(); - if (y < drag()->ymin()) - y = drag()->ymin(); - else if (y > drag()->ymax()) - y = drag()->ymax(); - dragPos.setY(y); - } - d->drag->target()->setPos(dragPos); - - if (!keepMouseGrab()) { - if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold) - || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold) - || (d->dragX && d->dragY && (dx > dragThreshold || dy > dragThreshold))) { - setKeepMouseGrab(true); - d->stealMouse = true; - } - } - - d->moved = true; - } - QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); - emit mouseXChanged(&me); - me.setPosition(d->lastPos); - emit mouseYChanged(&me); - me.setPosition(d->lastPos); - emit positionChanged(&me); -} - -void QSGMouseArea::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGMouseArea); - d->stealMouse = false; - if (!d->absorb) { - QSGItem::mouseReleaseEvent(event); - } else { - d->saveEvent(event); - setPressed(false); - if (d->drag) - d->drag->setActive(false); - // If we don't accept hover, we need to reset containsMouse. - if (!acceptHoverEvents()) - setHovered(false); - QSGCanvas *c = canvas(); - if (c && c->mouseGrabberItem() == this) - ungrabMouse(); - setKeepMouseGrab(false); - - } - d->doubleClick = false; -} - -void QSGMouseArea::mouseDoubleClickEvent(QMouseEvent *event) -{ - Q_D(QSGMouseArea); - if (d->absorb) { - d->saveEvent(event); - QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); - me.setAccepted(d->isDoubleClickConnected()); - emit this->doubleClicked(&me); - if (!me.isAccepted()) - d->propagate(&me, QSGMouseAreaPrivate::DoubleClick); - d->doubleClick = d->isDoubleClickConnected() || me.isAccepted(); - } - QSGItem::mouseDoubleClickEvent(event); -} - -void QSGMouseArea::hoverEnterEvent(QHoverEvent *event) -{ - Q_D(QSGMouseArea); - if (!d->absorb) { - QSGItem::hoverEnterEvent(event); - } else { - d->lastPos = event->posF(); - d->lastModifiers = event->modifiers(); - setHovered(true); - QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); - emit mouseXChanged(&me); - me.setPosition(d->lastPos); - emit mouseYChanged(&me); - me.setPosition(d->lastPos); - } -} - -void QSGMouseArea::hoverMoveEvent(QHoverEvent *event) -{ - Q_D(QSGMouseArea); - if (!d->absorb) { - QSGItem::hoverMoveEvent(event); - } else { - d->lastPos = event->posF(); - d->lastModifiers = event->modifiers(); - QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); - emit mouseXChanged(&me); - me.setPosition(d->lastPos); - emit mouseYChanged(&me); - me.setPosition(d->lastPos); - emit positionChanged(&me); - } -} - -void QSGMouseArea::hoverLeaveEvent(QHoverEvent *event) -{ - Q_D(QSGMouseArea); - if (!d->absorb) - QSGItem::hoverLeaveEvent(event); - else - setHovered(false); -} - -void QSGMouseArea::ungrabMouse() -{ - Q_D(QSGMouseArea); - if (d->pressed) { - // if our mouse grab has been removed (probably by Flickable), fix our - // state - d->pressed = false; - d->stealMouse = false; - setKeepMouseGrab(false); - emit canceled(); - emit pressedChanged(); - if (d->hovered) { - d->hovered = false; - emit hoveredChanged(); - } - } -} - -void QSGMouseArea::mouseUngrabEvent() -{ - ungrabMouse(); -} - -bool QSGMouseArea::sendMouseEvent(QMouseEvent *event) -{ - Q_D(QSGMouseArea); - QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); - - QSGCanvas *c = canvas(); - QSGItem *grabber = c ? c->mouseGrabberItem() : 0; - bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { - QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - mouseEvent.setAccepted(false); - - switch (event->type()) { - case QEvent::MouseMove: - mouseMoveEvent(&mouseEvent); - break; - case QEvent::MouseButtonPress: - mousePressEvent(&mouseEvent); - break; - case QEvent::MouseButtonRelease: - mouseReleaseEvent(&mouseEvent); - break; - default: - break; - } - grabber = c->mouseGrabberItem(); - if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) - grabMouse(); - - return stealThisEvent; - } - if (event->type() == QEvent::MouseButtonRelease) { - if (d->pressed) { - d->pressed = false; - d->stealMouse = false; - if (c && c->mouseGrabberItem() == this) - ungrabMouse(); - emit canceled(); - emit pressedChanged(); - if (d->hovered) { - d->hovered = false; - emit hoveredChanged(); - } - } - } - return false; -} - -bool QSGMouseArea::childMouseEventFilter(QSGItem *i, QEvent *e) -{ - Q_D(QSGMouseArea); - if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren()) - return QSGItem::childMouseEventFilter(i, e); - switch (e->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - return sendMouseEvent(static_cast(e)); - default: - break; - } - - return QSGItem::childMouseEventFilter(i, e); -} - -void QSGMouseArea::timerEvent(QTimerEvent *event) -{ - Q_D(QSGMouseArea); - if (event->timerId() == d->pressAndHoldTimer.timerId()) { - d->pressAndHoldTimer.stop(); - bool dragged = d->drag && d->drag->active(); - if (d->pressed && dragged == false && d->hovered == true) { - d->longPress = true; - QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); - me.setAccepted(d->isPressAndHoldConnected()); - emit pressAndHold(&me); - if (!me.isAccepted()) - d->propagate(&me, QSGMouseAreaPrivate::PressAndHold); - if (!me.isAccepted()) // no one handled the long press - allow click - d->longPress = false; - } - } -} - -void QSGMouseArea::windowDeactivateEvent() -{ - ungrabMouse(); - QSGItem::windowDeactivateEvent(); -} - -void QSGMouseArea::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) -{ - Q_D(QSGMouseArea); - QSGItem::geometryChanged(newGeometry, oldGeometry); - - if (d->lastScenePos.isNull) - d->lastScenePos = mapToScene(d->lastPos); - else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y()) - d->lastPos = mapFromScene(d->lastScenePos); -} - -void QSGMouseArea::itemChange(ItemChange change, const ItemChangeData &value) -{ - Q_D(QSGMouseArea); - switch (change) { - case ItemVisibleHasChanged: - if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) - setHovered(!d->hovered); - break; - default: - break; - } - - QSGItem::itemChange(change, value); -} - -/*! - \qmlproperty bool QtQuick2::MouseArea::hoverEnabled - This property holds whether hover events are handled. - - By default, mouse events are only handled in response to a button event, or when a button is - pressed. Hover enables handling of all mouse events even when no mouse button is - pressed. - - This property affects the containsMouse property and the onEntered, onExited and - onPositionChanged signals. -*/ -bool QSGMouseArea::hoverEnabled() const -{ - return acceptHoverEvents(); -} - -void QSGMouseArea::setHoverEnabled(bool h) -{ - if (h == acceptHoverEvents()) - return; - - setAcceptHoverEvents(h); - emit hoverEnabledChanged(); -} - - -/*! - \qmlproperty bool QtQuick2::MouseArea::containsMouse - This property holds whether the mouse is currently inside the mouse area. - - \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change. - In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed. -*/ -bool QSGMouseArea::hovered() const -{ - Q_D(const QSGMouseArea); - return d->hovered; -} - -/*! - \qmlproperty bool QtQuick2::MouseArea::pressed - This property holds whether the mouse area is currently pressed. -*/ -bool QSGMouseArea::pressed() const -{ - Q_D(const QSGMouseArea); - return d->pressed; -} - -void QSGMouseArea::setHovered(bool h) -{ - Q_D(QSGMouseArea); - if (d->hovered != h) { - d->hovered = h; - emit hoveredChanged(); - d->hovered ? emit entered() : emit exited(); - } -} -/*! - \qmlproperty QtQuick2::Qt::MouseButtons MouseArea::acceptedButtons - This property holds the mouse buttons that the mouse area reacts to. - - The available buttons are: - \list - \o Qt.LeftButton - \o Qt.RightButton - \o Qt.MiddleButton - \endlist - - To accept more than one button the flags can be combined with the - "|" (or) operator: - - \code - MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton } - \endcode - - The default value is \c Qt.LeftButton. -*/ -Qt::MouseButtons QSGMouseArea::acceptedButtons() const -{ - return acceptedMouseButtons(); -} - -void QSGMouseArea::setAcceptedButtons(Qt::MouseButtons buttons) -{ - if (buttons != acceptedMouseButtons()) { - setAcceptedMouseButtons(buttons); - emit acceptedButtonsChanged(); - } -} - -bool QSGMouseArea::setPressed(bool p) -{ - Q_D(QSGMouseArea); - bool dragged = d->drag && d->drag->active(); - bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true; - - if (d->pressed != p) { - d->pressed = p; - QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress); - if (d->pressed) { - if (!d->doubleClick) - emit pressed(&me); - me.setPosition(d->lastPos); - emit mouseXChanged(&me); - me.setPosition(d->lastPos); - emit mouseYChanged(&me); - emit pressedChanged(); - } else { - emit released(&me); - me.setPosition(d->lastPos); - emit pressedChanged(); - if (isclick && !d->longPress && !d->doubleClick){ - me.setAccepted(d->isClickConnected()); - emit clicked(&me); - if (!me.isAccepted()) - d->propagate(&me, QSGMouseAreaPrivate::Click); - } - } - - return me.isAccepted(); - } - return false; -} - -/*! - \qmlproperty Item QtQuick2::MouseArea::drag.target - \qmlproperty bool QtQuick2::MouseArea::drag.active - \qmlproperty enumeration QtQuick2::MouseArea::drag.axis - \qmlproperty real QtQuick2::MouseArea::drag.minimumX - \qmlproperty real QtQuick2::MouseArea::drag.maximumX - \qmlproperty real QtQuick2::MouseArea::drag.minimumY - \qmlproperty real QtQuick2::MouseArea::drag.maximumY - \qmlproperty bool QtQuick2::MouseArea::drag.filterChildren - - \c drag provides a convenient way to make an item draggable. - - \list - \i \c drag.target specifies the id of the item to drag. - \i \c drag.active specifies if the target item is currently being dragged. - \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis) - \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes. - \endlist - - The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity - of the rectangle is reduced when it is dragged to the right. - - \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag - - \note Items cannot be dragged if they are anchored for the requested - \c drag.axis. For example, if \c anchors.left or \c anchors.right was set - for \c rect in the above example, it cannot be dragged along the X-axis. - This can be avoided by settng the anchor value to \c undefined in - an \l onPressed handler. - - If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This - enables a parent MouseArea to handle drags, for example, while descendants handle clicks: - - \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter - -*/ - -QSGDrag *QSGMouseArea::drag() -{ - Q_D(QSGMouseArea); - if (!d->drag) - d->drag = new QSGDrag; - return d->drag; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgmousearea_p.h b/src/declarative/items/qsgmousearea_p.h deleted file mode 100644 index f0edf41777..0000000000 --- a/src/declarative/items/qsgmousearea_p.h +++ /dev/null @@ -1,226 +0,0 @@ -// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGMOUSEAREA_P_H -#define QSGMOUSEAREA_P_H - -#include "qsgitem.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGDragAttached; -class QSGMouseEvent; -class Q_AUTOTEST_EXPORT QSGDrag : public QObject -{ - Q_OBJECT - - Q_ENUMS(Axis) - Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget) - Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged) - Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) - Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged) - Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged) - Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) - Q_PROPERTY(bool active READ active NOTIFY activeChanged) - Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged) - //### consider drag and drop - -public: - QSGDrag(QObject *parent=0); - ~QSGDrag(); - - QSGItem *target() const; - void setTarget(QSGItem *target); - void resetTarget(); - - enum Axis { XAxis=0x01, YAxis=0x02, XandYAxis=0x03 }; - Axis axis() const; - void setAxis(Axis); - - qreal xmin() const; - void setXmin(qreal); - qreal xmax() const; - void setXmax(qreal); - qreal ymin() const; - void setYmin(qreal); - qreal ymax() const; - void setYmax(qreal); - - bool active() const; - void setActive(bool); - - bool filterChildren() const; - void setFilterChildren(bool); - - static QSGDragAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void targetChanged(); - void axisChanged(); - void minimumXChanged(); - void maximumXChanged(); - void minimumYChanged(); - void maximumYChanged(); - void activeChanged(); - void filterChildrenChanged(); - -private: - QSGItem *_target; - Axis _axis; - qreal _xmin; - qreal _xmax; - qreal _ymin; - qreal _ymax; - bool _active : 1; - bool _filterChildren: 1; - Q_DISABLE_COPY(QSGDrag) -}; - -class QSGMouseAreaPrivate; -// used in QtLocation -class Q_DECLARATIVE_EXPORT QSGMouseArea : public QSGItem -{ - Q_OBJECT - - Q_PROPERTY(qreal mouseX READ mouseX NOTIFY mouseXChanged) - Q_PROPERTY(qreal mouseY READ mouseY NOTIFY mouseYChanged) - Q_PROPERTY(bool containsMouse READ hovered NOTIFY hoveredChanged) - Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) - Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedChanged) - Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged) - Q_PROPERTY(bool hoverEnabled READ hoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged) - Q_PROPERTY(QSGDrag *drag READ drag CONSTANT) //### add flicking to QSGDrag or add a QDeclarativeFlick ??? - Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged) - -public: - QSGMouseArea(QSGItem *parent=0); - ~QSGMouseArea(); - - qreal mouseX() const; - qreal mouseY() const; - - bool isEnabled() const; - void setEnabled(bool); - - bool hovered() const; - bool pressed() const; - - Qt::MouseButtons pressedButtons() const; - - Qt::MouseButtons acceptedButtons() const; - void setAcceptedButtons(Qt::MouseButtons buttons); - - bool hoverEnabled() const; - void setHoverEnabled(bool h); - - QSGDrag *drag(); - - bool preventStealing() const; - void setPreventStealing(bool prevent); - -Q_SIGNALS: - void hoveredChanged(); - void pressedChanged(); - void enabledChanged(); - void acceptedButtonsChanged(); - void hoverEnabledChanged(); - void positionChanged(QSGMouseEvent *mouse); - void mouseXChanged(QSGMouseEvent *mouse); - void mouseYChanged(QSGMouseEvent *mouse); - void preventStealingChanged(); - - void pressed(QSGMouseEvent *mouse); - void pressAndHold(QSGMouseEvent *mouse); - void released(QSGMouseEvent *mouse); - void clicked(QSGMouseEvent *mouse); - void doubleClicked(QSGMouseEvent *mouse); - void entered(); - void exited(); - void canceled(); - -protected: - void setHovered(bool); - bool setPressed(bool); - bool sendMouseEvent(QMouseEvent *event); - - virtual void mousePressEvent(QMouseEvent *event); - virtual void mouseReleaseEvent(QMouseEvent *event); - virtual void mouseDoubleClickEvent(QMouseEvent *event); - virtual void mouseMoveEvent(QMouseEvent *event); - virtual void mouseUngrabEvent(); - virtual void hoverEnterEvent(QHoverEvent *event); - virtual void hoverMoveEvent(QHoverEvent *event); - virtual void hoverLeaveEvent(QHoverEvent *event); - virtual bool childMouseEventFilter(QSGItem *i, QEvent *e); - virtual void timerEvent(QTimerEvent *event); - virtual void windowDeactivateEvent(); - - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - virtual void itemChange(ItemChange change, const ItemChangeData& value); - -private: - void handlePress(); - void handleRelease(); - void ungrabMouse(); - -private: - Q_DISABLE_COPY(QSGMouseArea) - Q_DECLARE_PRIVATE(QSGMouseArea) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGDrag) -QML_DECLARE_TYPEINFO(QSGDrag, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QSGMouseArea) - -QT_END_HEADER - -#endif // QSGMOUSEAREA_P_H diff --git a/src/declarative/items/qsgmousearea_p_p.h b/src/declarative/items/qsgmousearea_p_p.h deleted file mode 100644 index 6cf663ac6d..0000000000 --- a/src/declarative/items/qsgmousearea_p_p.h +++ /dev/null @@ -1,111 +0,0 @@ -// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGMOUSEAREA_P_P_H -#define QSGMOUSEAREA_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgitem_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGMouseEvent; -class QSGMouseArea; -class QSGMouseAreaPrivate : public QSGItemPrivate -{ - Q_DECLARE_PUBLIC(QSGMouseArea) - -public: - QSGMouseAreaPrivate(); - ~QSGMouseAreaPrivate(); - void init(); - - void saveEvent(QMouseEvent *event); - enum PropagateType{ - Click, - DoubleClick, - PressAndHold - }; - void propagate(QSGMouseEvent* event, PropagateType); - bool propagateHelper(QSGMouseEvent*, QSGItem*,const QPointF &, PropagateType); - - bool isPressAndHoldConnected(); - bool isDoubleClickConnected(); - bool isClickConnected(); - - bool absorb : 1; - bool hovered : 1; - bool pressed : 1; - bool longPress : 1; - bool moved : 1; - bool dragX : 1; - bool dragY : 1; - bool stealMouse : 1; - bool doubleClick : 1; - bool preventStealing : 1; - QSGDrag *drag; - QPointF startScene; - QPointF targetStartPos; - QPointF lastPos; - QDeclarativeNullableValue lastScenePos; - Qt::MouseButton lastButton; - Qt::MouseButtons lastButtons; - Qt::KeyboardModifiers lastModifiers; - QBasicTimer pressAndHoldTimer; -}; - -QT_END_NAMESPACE - -#endif // QSGMOUSEAREA_P_P_H diff --git a/src/declarative/items/qsgninepatchnode.cpp b/src/declarative/items/qsgninepatchnode.cpp deleted file mode 100644 index 3658b3cb98..0000000000 --- a/src/declarative/items/qsgninepatchnode.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgninepatchnode_p.h" -#include -#include - -QSGNinePatchNode::QSGNinePatchNode() - : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) - , m_horizontalTileMode(QSGBorderImage::Stretch) - , m_verticalTileMode(QSGBorderImage::Stretch) - , m_dirtyGeometry(false) - , m_mirror(false) -{ - setOpaqueMaterial(&m_material); - setMaterial(&m_materialO); - setGeometry(&m_geometry); - m_geometry.setDrawingMode(GL_TRIANGLES); -#ifdef QML_RUNTIME_TESTING - description = QLatin1String("borderimage"); -#endif -} - -void QSGNinePatchNode::setInnerRect(const QRectF &rect) -{ - if (m_innerRect == rect) - return; - m_innerRect = rect; - m_dirtyGeometry = true; -} - -void QSGNinePatchNode::setRect(const QRectF &rect) -{ - if (m_targetRect == rect) - return; - m_targetRect = rect; - m_dirtyGeometry = true; -} - -void QSGNinePatchNode::setHorzontalTileMode(QSGBorderImage::TileMode mode) -{ - if (mode == QSGBorderImage::TileMode(m_horizontalTileMode)) - return; - m_horizontalTileMode = mode; - m_dirtyGeometry = true; -} - - -void QSGNinePatchNode::setVerticalTileMode(QSGBorderImage::TileMode mode) -{ - if (mode == QSGBorderImage::TileMode(m_verticalTileMode)) - return; - m_verticalTileMode = mode; - m_dirtyGeometry = true; -} - - -void QSGNinePatchNode::setFiltering(QSGTexture::Filtering filtering) -{ - if (m_material.filtering() == filtering) - return; - - m_material.setFiltering(filtering); - m_materialO.setFiltering(filtering); - markDirty(DirtyMaterial); -} - -QSGTexture::Filtering QSGNinePatchNode::filtering() const -{ - return m_material.filtering(); -} - -void QSGNinePatchNode::setTexture(QSGTexture *texture) -{ - if (texture == m_material.texture()) - return; - m_material.setTexture(texture); - m_materialO.setTexture(texture); - markDirty(DirtyMaterial); -} - -QSGTexture *QSGNinePatchNode::texture() const -{ - return m_material.texture(); -} - -void QSGNinePatchNode::setMirror(bool m) -{ - if (m_mirror == m) - return; - m_mirror = m; - m_dirtyGeometry = true; -} - - -void QSGNinePatchNode::update() -{ - if (!m_dirtyGeometry) - return; - - // For stretch this algorithm could be simplified to use less vertices - // as more vertices could be reused then, but I doubt its where our main - // problem will lie. This way, we at least share the algorithm between all - - Q_ASSERT(m_material.texture()); - - float tw = m_material.texture()->textureSize().width(); - float th = m_material.texture()->textureSize().height(); - - QRectF textureSubRect = m_material.texture()->textureSubRect(); - QSize textureSize = m_material.texture()->textureSize(); - - float rightBorder = tw - m_innerRect.right(); - float bottomBorder = th - m_innerRect.bottom(); - -// qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode; - - int xChunkCount = 0; // Number of chunks - float xChunkSize = 0; // Size of chunk in pixels - float xTexSize = m_innerRect.width(); // Size of the texture to stretch/tile - float xSize = m_targetRect.width() - m_innerRect.left() - rightBorder; // Size of area to fill with chunks - - if (m_horizontalTileMode == QSGBorderImage::Repeat) { - xChunkCount = qCeil(xSize / xTexSize); - xChunkSize = xTexSize; - } else if (m_horizontalTileMode == QSGBorderImage::Round) { - xChunkCount = qCeil(xSize / xTexSize); - qreal fullWidth = xChunkCount * xTexSize; - xChunkSize = xTexSize * xSize / fullWidth; - } else { - xChunkCount = 1; - xChunkSize = xSize; - } - - int yChunkCount = 0; - float yChunkSize = 0; // Relative to target rect. - float yTexSize = m_innerRect.height(); // Size of the texture to stretch/tile - float ySize = m_targetRect.height() - m_innerRect.top() - bottomBorder; - - if (m_verticalTileMode == QSGBorderImage::Repeat) { - yChunkCount = qCeil(ySize / yTexSize); - yChunkSize = yTexSize; - } else if (m_verticalTileMode == QSGBorderImage::Round) { - yChunkCount = qCeil(ySize / yTexSize); - qreal fullHeight = yChunkCount * yTexSize; - yChunkSize = yTexSize * ySize / fullHeight; - } else { - yChunkCount = 1; - yChunkSize = ySize; - } - - int xTotalChunkCount = xChunkCount + 2; - int yTotalChunkCount = yChunkCount + 2; - - int totalChunkCount = xTotalChunkCount * yTotalChunkCount; - int vertexCount = totalChunkCount * 4; - int indexCount = totalChunkCount * 6; - - if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount()) - m_geometry.allocate(vertexCount, indexCount); - - QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D(); - - - // Fill in the vertices.. The loop below is pretty much an exact replica - // of the one inside fillRow. - float yTexChunk1 = m_innerRect.top() / th; - float yTexChunk2 = m_innerRect.bottom() / th; - - fillRow(v, 0, 0, xChunkCount, xChunkSize, textureSubRect, textureSize); - fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize, textureSubRect, textureSize); - - for (int yc=0; ycx = m_targetRect.width() - v->x; - ++v; - } - } - -// v = m_geometry.vertexDataAsTexturedPoint2D(); -// for (int i=0; ix, v->y, v->tx, v->ty); -// ++v; -// } - - quint16 *i = m_geometry.indexDataAsUShort(); - int row = xTotalChunkCount * 2; - for (int r=0; rset(0, y, tsr.left(), ty); - v++->set(m_innerRect.x(), y, xTexChunk1, ty); - - for (int xc=0; xcset(xx, y, xTexChunk1, ty); - - // Special case the last one - if (xc == xChunkCount - 1) { - float t = m_horizontalTileMode == QSGBorderImage::Repeat - ? xTexChunk1 + (xTexChunk2 - xTexChunk1) * (m_targetRect.width() - rightBorder - xx) / xChunkSize - : xTexChunk2; - v->set(m_targetRect.width() - rightBorder, y, t, ty); - } else { - v->set(xx + xChunkSize, y, xTexChunk2, ty); - } - ++v; - } - - v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty); - v++->set(m_targetRect.width(), y, tsr.right(), ty); -} diff --git a/src/declarative/items/qsgninepatchnode_p.h b/src/declarative/items/qsgninepatchnode_p.h deleted file mode 100644 index fd0a0c5477..0000000000 --- a/src/declarative/items/qsgninepatchnode_p.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGNINEPATCHNODE_H -#define QSGNINEPATCHNODE_H - -#include "qsgnode.h" -#include "qsgtexturematerial.h" -#include "qsgborderimage_p.h" - -class TextureReference; - -class QSGNinePatchNode : public QSGGeometryNode -{ -public: - QSGNinePatchNode(); - - void setTexture(QSGTexture *texture); - QSGTexture *texture() const; - - void setRect(const QRectF &rect); - QRectF rect() const { return m_targetRect; } - - void setInnerRect(const QRectF &rect); - QRectF innerRect() const { return m_innerRect; } - - void setFiltering(QSGTexture::Filtering filtering); - QSGTexture::Filtering filtering() const; - - void setHorzontalTileMode(QSGBorderImage::TileMode mode); - QSGBorderImage::TileMode horizontalTileMode() const { - return (QSGBorderImage::TileMode) m_horizontalTileMode; - } - - void setVerticalTileMode(QSGBorderImage::TileMode mode); - QSGBorderImage::TileMode verticalTileMode() const { - return (QSGBorderImage::TileMode) m_verticalTileMode; - } - - void setMirror(bool m); - bool mirror() const { return m_mirror; } - - void update(); - -private: - void fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize, const QRectF &tsr, const QSize &ts); - QRectF m_targetRect; - QRectF m_innerRect; - QSGOpaqueTextureMaterial m_material; - QSGTextureMaterial m_materialO; - QSGGeometry m_geometry; - - uint m_horizontalTileMode : 2; - uint m_verticalTileMode : 2; - - uint m_dirtyGeometry : 1; - uint m_mirror : 1; -}; - -#endif // QSGNINEPATCHNODE_H diff --git a/src/declarative/items/qsgpainteditem.cpp b/src/declarative/items/qsgpainteditem.cpp deleted file mode 100644 index 16c3b3df95..0000000000 --- a/src/declarative/items/qsgpainteditem.cpp +++ /dev/null @@ -1,536 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgpainteditem.h" -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -/*! - \class QSGPaintedItem - \brief The QSGPaintedItem class provides a way to use the QPainter API in the - QML Scene Graph. - - \inmodule QtDeclarative - - The QSGPaintedItem makes it possible to use the QPainter API with the QML Scene Graph. - It sets up a textured rectangle in the Scene Graph and uses a QPainter to paint - onto the texture. The render target can be either a QImage or a QOpenGLFramebufferObject. - When the render target is a QImage, QPainter first renders into the image then - the content is uploaded to the texture. - When a QOpenGLFramebufferObject is used, QPainter paints directly onto the texture. - Call update() to trigger a repaint. - - To enable QPainter to do anti-aliased rendering, use setAntialiasing(). - - QSGPaintedItem is meant to make it easier to port old code that is using the - QPainter API to the QML Scene Graph API and it should be used only for that purpose. - - To write your own painted item, you first create a subclass of QSGPaintedItem, and then - start by implementing its only pure virtual public function: paint(), which implements - the actual painting. To get the size of the area painted by the item, use - contentsBoundingRect(). -*/ - -/*! - \enum QSGPaintedItem::RenderTarget - - This enum describes QSGPaintedItem's render targets. The render target is the - surface QPainter paints onto before the item is rendered on screen. - - \value Image The default; QPainter paints into a QImage using the raster paint engine. - The image's content needs to be uploaded to graphics memory afterward, this operation - can potentially be slow if the item is large. This render target allows high quality - anti-aliasing and fast item resizing. - - \value FramebufferObject QPainter paints into a QOpenGLFramebufferObject using the GL - paint engine. Painting can be faster as no texture upload is required, but anti-aliasing - quality is not as good as if using an image. This render target allows faster rendering - in some cases, but you should avoid using it if the item is resized often. - - \sa setRenderTarget() -*/ - -/*! - \enum QSGPaintedItem::PerformanceHint - - This enum describes flags that you can enable to improve rendering - performance in QSGPaintedItem. By default, none of these flags are set. - - \value FastFBOResizing If your item gets resized often and you are using the - QSGPaintedItem::FramebufferObject render target, set this flag to true to reduce the - item resizing time at the cost of using more graphics memory. Resizing a Framebuffer object - is a costly operation, by enabling this property the Framebuffer Object will use a texture - larger than the actual size of the item to avoid as much as possible resizing it. -*/ - -/*! - \internal -*/ -QSGPaintedItemPrivate::QSGPaintedItemPrivate() - : QSGItemPrivate() - , contentsScale(1.0) - , fillColor(Qt::transparent) - , renderTarget(QSGPaintedItem::Image) - , performanceHints(0) - , geometryDirty(false) - , contentsDirty(false) - , opaquePainting(false) - , antialiasing(false) - , mipmap(false) -{ -} - -/*! - Constructs a QSGPaintedItem with the given \a parent item. - */ -QSGPaintedItem::QSGPaintedItem(QSGItem *parent) - : QSGItem(*(new QSGPaintedItemPrivate), parent) -{ - setFlag(ItemHasContents); -} - -/*! - \internal -*/ -QSGPaintedItem::QSGPaintedItem(QSGPaintedItemPrivate &dd, QSGItem *parent) - : QSGItem(dd, parent) -{ - setFlag(ItemHasContents); -} - -/*! - Destroys the QSGPaintedItem. -*/ -QSGPaintedItem::~QSGPaintedItem() -{ -} - -/*! - Schedules a redraw of the area covered by \a rect in this item. You can call this function - whenever your item needs to be redrawn, such as if it changes appearance or size. - - This function does not cause an immediate paint; instead it schedules a paint request that - is processed by the QML Scene Graph when the next frame is rendered. The item will only be - redrawn if it is visible. - - Note that calling this function will trigger a repaint of the whole scene. - - \sa paint() -*/ -void QSGPaintedItem::update(const QRect &rect) -{ - Q_D(QSGPaintedItem); - d->contentsDirty = true; - - if (rect.isNull() && !d->dirtyRect.isNull()) - d->dirtyRect = contentsBoundingRect().toAlignedRect(); - else - d->dirtyRect |= (contentsBoundingRect() & rect).toAlignedRect(); - QSGItem::update(); -} - -/*! - Returns true if this item is opaque; otherwise, false is returned. - - By default, painted items are not opaque. - - \sa setOpaquePainting() -*/ -bool QSGPaintedItem::opaquePainting() const -{ - Q_D(const QSGPaintedItem); - return d->opaquePainting; -} - -/*! - If \a opaque is true, the item is opaque; otherwise, it is considered as translucent. - - Opaque items are not blended with the rest of the scene, you should set this to true - if the content of the item is opaque to speed up rendering. - - By default, painted items are not opaque. - - \sa opaquePainting() -*/ -void QSGPaintedItem::setOpaquePainting(bool opaque) -{ - Q_D(QSGPaintedItem); - - if (d->opaquePainting == opaque) - return; - - d->opaquePainting = opaque; - QSGItem::update(); -} - -/*! - Returns true if antialiased painting is enabled; otherwise, false is returned. - - By default, antialiasing is not enabled. - - \sa setAntialiasing() -*/ -bool QSGPaintedItem::antialiasing() const -{ - Q_D(const QSGPaintedItem); - return d->antialiasing; -} - -/*! - If \a enable is true, antialiased painting is enabled. - - By default, antialiasing is not enabled. - - \sa antialiasing() -*/ -void QSGPaintedItem::setAntialiasing(bool enable) -{ - Q_D(QSGPaintedItem); - - if (d->antialiasing == enable) - return; - - d->antialiasing = enable; - update(); -} - -/*! - Returns true if mipmaps are enabled; otherwise, false is returned. - - By default, mipmapping is not enabled. - - \sa setMipmap() -*/ -bool QSGPaintedItem::mipmap() const -{ - Q_D(const QSGPaintedItem); - return d->mipmap; -} - -/*! - If \a enable is true, mipmapping is enabled on the associated texture. - - Mipmapping increases rendering speed and reduces aliasing artifacts when the item is - scaled down. - - By default, mipmapping is not enabled. - - \sa mipmap() -*/ -void QSGPaintedItem::setMipmap(bool enable) -{ - Q_D(QSGPaintedItem); - - if (d->mipmap == enable) - return; - - d->mipmap = enable; - update(); -} - -/*! - Returns the performance hints. - - By default, no performance hint is enabled/ - - \sa setPerformanceHint(), setPerformanceHints() -*/ -QSGPaintedItem::PerformanceHints QSGPaintedItem::performanceHints() const -{ - Q_D(const QSGPaintedItem); - return d->performanceHints; -} - -/*! - Sets the given performance \a hint on the item if \a enabled is true; - otherwise clears the performance hint. - - By default, no performance hint is enabled/ - - \sa setPerformanceHints(), performanceHints() -*/ -void QSGPaintedItem::setPerformanceHint(QSGPaintedItem::PerformanceHint hint, bool enabled) -{ - Q_D(QSGPaintedItem); - PerformanceHints oldHints = d->performanceHints; - if (enabled) - d->performanceHints |= hint; - else - d->performanceHints &= ~hint; - if (oldHints != d->performanceHints) - update(); -} - -/*! - Sets the performance hints to \a hints - - By default, no performance hint is enabled/ - - \sa setPerformanceHint(), performanceHints() -*/ -void QSGPaintedItem::setPerformanceHints(QSGPaintedItem::PerformanceHints hints) -{ - Q_D(QSGPaintedItem); - if (d->performanceHints == hints) - return; - d->performanceHints = hints; - update(); -} - -/*! - This function returns the outer bounds of the item as a rectangle; all painting must be - restricted to inside an item's bounding rect. - - If the contents size has not been set it reflects the size of the item; otherwise - it reflects the contents size scaled by the contents scale. - - Use this function to know the area painted by the item. - - \sa QSGItem::width(), QSGItem::height(), contentsSize(), contentsScale() -*/ -QRectF QSGPaintedItem::contentsBoundingRect() const -{ - Q_D(const QSGPaintedItem); - - qreal w = d->width; - QSizeF sz = d->contentsSize * d->contentsScale; - if (w < sz.width()) - w = sz.width(); - qreal h = d->height; - if (h < sz.height()) - h = sz.height(); - - return QRectF(0, 0, w, h); -} - -/*! - \property QSGPaintedItem::contentsSize - \brief The size of the contents - - The contents size is the size of the item in regards to how it is painted - using the paint() function. This is distinct from the size of the - item in regards to height() and width(). -*/ -QSize QSGPaintedItem::contentsSize() const -{ - Q_D(const QSGPaintedItem); - return d->contentsSize; -} - -void QSGPaintedItem::setContentsSize(const QSize &size) -{ - Q_D(QSGPaintedItem); - - if (d->contentsSize == size) - return; - - d->contentsSize = size; - update(); -} - -/*! - This convenience function is equivalent to calling setContentsSize(QSize()). -*/ -void QSGPaintedItem::resetContentsSize() -{ - setContentsSize(QSize()); -} - -/*! - \property QSGPaintedItem::contentsScale - \brief The scale of the contents - - All painting happening in paint() is scaled by the contents scale. This is distinct - from the scale of the item in regards to scale(). - - The default value is 1. -*/ -qreal QSGPaintedItem::contentsScale() const -{ - Q_D(const QSGPaintedItem); - return d->contentsScale; -} - -void QSGPaintedItem::setContentsScale(qreal scale) -{ - Q_D(QSGPaintedItem); - - if (d->contentsScale == scale) - return; - - d->contentsScale = scale; - update(); -} - -/*! - \property QSGPaintedItem::fillColor - \brief The item's background fill color. - - By default, the fill color is set to Qt::transparent. -*/ -QColor QSGPaintedItem::fillColor() const -{ - Q_D(const QSGPaintedItem); - return d->fillColor; -} - -void QSGPaintedItem::setFillColor(const QColor &c) -{ - Q_D(QSGPaintedItem); - - if (d->fillColor == c) - return; - - d->fillColor = c; - update(); - - emit fillColorChanged(); -} - -/*! - \property QSGPaintedItem::renderTarget - \brief The item's render target. - - This property defines which render target the QPainter renders into, it can be either - QSGPaintedItem::Image or QSGPaintedItem::FramebufferObject. Both have certains benefits, - typically performance versus quality. Using a framebuffer object avoids a costly upload - of the image contents to the texture in graphics memory, while using an image enables - high quality anti-aliasing. - - \warning Resizing a framebuffer object is a costly operation, avoid using - the QSGPaintedItem::FramebufferObject render target if the item gets resized often. - - By default, the render target is QSGPaintedItem::Image. -*/ -QSGPaintedItem::RenderTarget QSGPaintedItem::renderTarget() const -{ - Q_D(const QSGPaintedItem); - return d->renderTarget; -} - -void QSGPaintedItem::setRenderTarget(RenderTarget target) -{ - Q_D(QSGPaintedItem); - - if (d->renderTarget == target) - return; - - d->renderTarget = target; - update(); - - emit renderTargetChanged(); -} - -/*! - \fn virtual void QSGPaintedItem::paint(QPainter *painter) = 0 - - This function, which is usually called by the QML Scene Graph, paints the - contents of an item in local coordinates. - - The function is called after the item has been filled with the fillColor. - - Reimplement this function in a QSGPaintedItem subclass to provide the - item's painting implementation, using \a painter. - - \note The QML Scene Graph uses two separate threads, the main thread does things such as - processing events or updating animations while a second thread does the actual OpenGL rendering. - As a consequence, paint() is not called from the main GUI thread but from the GL enabled - renderer thread. At the moment paint() is called, the GUI thread is blocked and this is - therefore thread-safe. -*/ - -/*! - This function is called after the item's geometry has changed. -*/ -void QSGPaintedItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_D(QSGPaintedItem); - d->geometryDirty = true; - QSGItem::geometryChanged(newGeometry, oldGeometry); -} - - -/*! - This function is called when the Scene Graph node associated to the item needs to - be updated. -*/ -QSGNode *QSGPaintedItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) -{ - Q_UNUSED(data); - Q_D(QSGPaintedItem); - - if (width() <= 0 || height() <= 0) { - delete oldNode; - return 0; - } - - QSGPainterNode *node = static_cast(oldNode); - if (!node) - node = new QSGPainterNode(this); - - QRectF br = contentsBoundingRect(); - - node->setPreferredRenderTarget(d->renderTarget); - node->setFastFBOResizing(d->performanceHints & FastFBOResizing); - node->setSize(QSize(qRound(br.width()), qRound(br.height()))); - node->setSmoothPainting(d->antialiasing); - node->setLinearFiltering(d->smooth); - node->setMipmapping(d->mipmap); - node->setOpaquePainting(d->opaquePainting); - node->setFillColor(d->fillColor); - node->setContentsScale(d->contentsScale); - node->setDirty(d->contentsDirty || d->geometryDirty, d->dirtyRect); - node->update(); - - d->contentsDirty = false; - d->geometryDirty = false; - d->dirtyRect = QRect(); - - return node; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgpainteditem.h b/src/declarative/items/qsgpainteditem.h deleted file mode 100644 index eaae3be7b5..0000000000 --- a/src/declarative/items/qsgpainteditem.h +++ /dev/null @@ -1,130 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGPAINTEDITEM_P_H -#define QSGPAINTEDITEM_P_H - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QSGPaintedItemPrivate; -class Q_DECLARATIVE_EXPORT QSGPaintedItem : public QSGItem -{ - Q_OBJECT - Q_ENUMS(RenderTarget) - - Q_PROPERTY(QSize contentsSize READ contentsSize WRITE setContentsSize NOTIFY contentsSizeChanged) - Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) - Q_PROPERTY(qreal contentsScale READ contentsScale WRITE setContentsScale NOTIFY contentsScaleChanged) - Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged) -public: - QSGPaintedItem(QSGItem *parent = 0); - virtual ~QSGPaintedItem(); - - enum RenderTarget { - Image, - FramebufferObject - }; - - enum PerformanceHint { - FastFBOResizing = 0x1 - }; - Q_DECLARE_FLAGS(PerformanceHints, PerformanceHint) - - void update(const QRect &rect = QRect()); - - bool opaquePainting() const; - void setOpaquePainting(bool opaque); - - bool antialiasing() const; - void setAntialiasing(bool enable); - - bool mipmap() const; - void setMipmap(bool enable); - - PerformanceHints performanceHints() const; - void setPerformanceHint(PerformanceHint hint, bool enabled = true); - void setPerformanceHints(PerformanceHints hints); - - QRectF contentsBoundingRect() const; - - QSize contentsSize() const; - void setContentsSize(const QSize &); - void resetContentsSize(); - - qreal contentsScale() const; - void setContentsScale(qreal); - - QColor fillColor() const; - void setFillColor(const QColor&); - - RenderTarget renderTarget() const; - void setRenderTarget(RenderTarget target); - - virtual void paint(QPainter *painter) = 0; - -Q_SIGNALS: - void fillColorChanged(); - void contentsSizeChanged(); - void contentsScaleChanged(); - void renderTargetChanged(); - -protected: - QSGPaintedItem(QSGPaintedItemPrivate &dd, QSGItem *parent = 0); - virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - -private: - Q_DISABLE_COPY(QSGPaintedItem) - Q_DECLARE_PRIVATE(QSGPaintedItem) -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(QSGPaintedItem::PerformanceHints) - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QSGPAINTEDITEM_P_H diff --git a/src/declarative/items/qsgpainteditem_p.h b/src/declarative/items/qsgpainteditem_p.h deleted file mode 100644 index f698e186bf..0000000000 --- a/src/declarative/items/qsgpainteditem_p.h +++ /dev/null @@ -1,73 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGPAINTEDITEM_P_P_H -#define QSGPAINTEDITEM_P_P_H - -#include "qsgitem_p.h" -#include - -QT_BEGIN_NAMESPACE - -class QSGPaintedItemPrivate : public QSGItemPrivate -{ -public: - QSGPaintedItemPrivate(); - - QSize contentsSize; - qreal contentsScale; - QColor fillColor; - QSGPaintedItem::RenderTarget renderTarget; - QSGPaintedItem::PerformanceHints performanceHints; - - QRect dirtyRect; - - bool geometryDirty : 1; - bool contentsDirty : 1; - bool opaquePainting: 1; - bool antialiasing: 1; - bool mipmap: 1; -}; - -QT_END_NAMESPACE - -#endif // QSGPAINTEDITEM_P_P_H diff --git a/src/declarative/items/qsgpathview.cpp b/src/declarative/items/qsgpathview.cpp deleted file mode 100644 index 5e2ad84e3b..0000000000 --- a/src/declarative/items/qsgpathview.cpp +++ /dev/null @@ -1,1738 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgpathview_p.h" -#include "qsgpathview_p_p.h" -#include "qsgcanvas.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -inline qreal qmlMod(qreal x, qreal y) -{ -#ifdef QT_USE_MATH_H_FLOATS - if (sizeof(qreal) == sizeof(float)) - return fmodf(float(x), float(y)); - else -#endif - return fmod(x, y); -} - -static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0; - -QSGPathViewAttached::QSGPathViewAttached(QObject *parent) -: QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false) -{ - if (qPathViewAttachedType) { - m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType); - m_metaobject->setCached(true); - } else { - m_metaobject = new QDeclarativeOpenMetaObject(this); - } -} - -QSGPathViewAttached::~QSGPathViewAttached() -{ -} - -QVariant QSGPathViewAttached::value(const QByteArray &name) const -{ - return m_metaobject->value(name); -} -void QSGPathViewAttached::setValue(const QByteArray &name, const QVariant &val) -{ - m_metaobject->setValue(name, val); -} - - -void QSGPathViewPrivate::init() -{ - Q_Q(QSGPathView); - offset = 0; - q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFlag(QSGItem::ItemIsFocusScope); - q->setFiltersChildMouseEvents(true); - FAST_CONNECT(&tl, SIGNAL(updated()), q, SLOT(ticked())) - lastPosTime.invalidate(); - FAST_CONNECT(&tl, SIGNAL(completed()), q, SLOT(movementEnding())) -} - -QSGItem *QSGPathViewPrivate::getItem(int modelIndex, bool onPath) -{ - Q_Q(QSGPathView); - requestedIndex = modelIndex; - QSGItem *item = model->item(modelIndex, false); - if (item) { - if (!attType) { - // pre-create one metatype to share with all attached objects - attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(q)); - foreach (const QString &attr, path->attributes()) - attType->createProperty(attr.toUtf8()); - } - qPathViewAttachedType = attType; - QSGPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); - qPathViewAttachedType = 0; - if (att) { - att->m_view = q; - att->setOnPath(onPath); - } - item->setParentItem(q); - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry); - } - requestedIndex = -1; - return item; -} - -void QSGPathViewPrivate::releaseItem(QSGItem *item) -{ - if (!item || !model) - return; - QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); - itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry); - if (model->release(item) == 0) { - // item was not destroyed, and we no longer reference it. - if (QSGPathViewAttached *att = attached(item)) - att->setOnPath(false); - } -} - -QSGPathViewAttached *QSGPathViewPrivate::attached(QSGItem *item) -{ - return static_cast(qmlAttachedPropertiesObject(item, false)); -} - -void QSGPathViewPrivate::clear() -{ - if (currentItem) { - releaseItem(currentItem); - currentItem = 0; - } - for (int i=0; i= 0 && index < modelCount) { - qreal start = 0.0; - if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange) - start = highlightRangeStart; - qreal globalPos = index + offset; - globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount; - if (pathItems != -1 && pathItems < modelCount) { - globalPos += start * mappedRange; - globalPos = qmlMod(globalPos, 1.0); - if (globalPos < mappedRange) - pos = globalPos / mappedRange; - } else { - pos = qmlMod(globalPos + start, 1.0); - } - } - - return pos; -} - -void QSGPathViewPrivate::createHighlight() -{ - Q_Q(QSGPathView); - if (!q->isComponentComplete()) - return; - - bool changed = false; - if (highlightItem) { - highlightItem->setParentItem(0); - highlightItem->deleteLater(); - highlightItem = 0; - changed = true; - } - - QSGItem *item = 0; - if (highlightComponent) { - QDeclarativeContext *creationContext = highlightComponent->creationContext(); - QDeclarativeContext *highlightContext = new QDeclarativeContext( - creationContext ? creationContext : qmlContext(q)); - QObject *nobj = highlightComponent->create(highlightContext); - if (nobj) { - QDeclarative_setParent_noEvent(highlightContext, nobj); - item = qobject_cast(nobj); - if (!item) - delete nobj; - } else { - delete highlightContext; - } - } else { - item = new QSGItem; - } - if (item) { - QDeclarative_setParent_noEvent(item, q); - item->setParentItem(q); - highlightItem = item; - changed = true; - } - if (changed) - emit q->highlightItemChanged(); -} - -void QSGPathViewPrivate::updateHighlight() -{ - Q_Q(QSGPathView); - if (!q->isComponentComplete() || !isValid()) - return; - if (highlightItem) { - if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) { - updateItem(highlightItem, highlightRangeStart); - } else { - qreal target = currentIndex; - - offsetAdj = 0.0; - tl.reset(moveHighlight); - moveHighlight.setValue(highlightPosition); - - const int duration = highlightMoveDuration; - - if (target - highlightPosition > modelCount/2) { - highlightUp = false; - qreal distance = modelCount - target + highlightPosition; - tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance)); - tl.set(moveHighlight, modelCount-0.01); - tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance)); - } else if (target - highlightPosition <= -modelCount/2) { - highlightUp = true; - qreal distance = modelCount - highlightPosition + target; - tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance)); - tl.set(moveHighlight, 0.0); - tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance)); - } else { - highlightUp = highlightPosition - target < 0; - tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration); - } - } - } -} - -void QSGPathViewPrivate::setHighlightPosition(qreal pos) -{ - if (pos != highlightPosition) { - qreal start = 0.0; - qreal end = 1.0; - if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange) { - start = highlightRangeStart; - end = highlightRangeEnd; - } - - qreal range = qreal(modelCount); - // calc normalized position of highlight relative to offset - qreal relativeHighlight = qmlMod(pos + offset, range) / range; - - if (!highlightUp && relativeHighlight > end * mappedRange) { - qreal diff = 1.0 - relativeHighlight; - setOffset(offset + diff * range); - } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) { - qreal diff = relativeHighlight - (end - start) * mappedRange; - setOffset(offset - diff * range - 0.00001); - } - - highlightPosition = pos; - qreal pathPos = positionOfIndex(pos); - updateItem(highlightItem, pathPos); - if (QSGPathViewAttached *att = attached(highlightItem)) - att->setOnPath(pathPos != -1.0); - } -} - -void QSGPathView::pathUpdated() -{ - Q_D(QSGPathView); - QList::iterator it = d->items.begin(); - while (it != d->items.end()) { - QSGItem *item = *it; - if (QSGPathViewAttached *att = d->attached(item)) - att->m_percent = -1; - ++it; - } - refill(); -} - -void QSGPathViewPrivate::updateItem(QSGItem *item, qreal percent) -{ - if (QSGPathViewAttached *att = attached(item)) { - if (qFuzzyCompare(att->m_percent, percent)) - return; - att->m_percent = percent; - foreach (const QString &attr, path->attributes()) - att->setValue(attr.toUtf8(), path->attributeAt(attr, percent)); - } - QPointF pf = path->pointAt(percent); - item->setX(qRound(pf.x() - item->width()/2)); - item->setY(qRound(pf.y() - item->height()/2)); -} - -void QSGPathViewPrivate::regenerate() -{ - Q_Q(QSGPathView); - if (!q->isComponentComplete()) - return; - - clear(); - - if (!isValid()) - return; - - firstIndex = -1; - updateMappedRange(); - q->refill(); -} - -/*! - \qmlclass PathView QSGPathView - \inqmlmodule QtQuick 2 - \ingroup qml-view-elements - \brief The PathView element lays out model-provided items on a path. - \inherits Item - - A PathView displays data from models created from built-in QML elements like ListModel - and XmlListModel, or custom model classes defined in C++ that inherit from - QAbstractListModel. - - The view has a \l model, which defines the data to be displayed, and - a \l delegate, which defines how the data should be displayed. - The \l delegate is instantiated for each item on the \l path. - The items may be flicked to move them along the path. - - For example, if there is a simple list model defined in a file \c ContactModel.qml like this: - - \snippet doc/src/snippets/declarative/pathview/ContactModel.qml 0 - - This data can be represented as a PathView, like this: - - \snippet doc/src/snippets/declarative/pathview/pathview.qml 0 - - \image pathview.gif - - (Note the above example uses PathAttribute to scale and modify the - opacity of the items as they rotate. This additional code can be seen in the - PathAttribute documentation.) - - PathView does not automatically handle keyboard navigation. This is because - the keys to use for navigation will depend upon the shape of the path. Navigation - can be added quite simply by setting \c focus to \c true and calling - \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate - using the left and right arrow keys: - - \qml - PathView { - // ... - focus: true - Keys.onLeftPressed: decrementCurrentIndex() - Keys.onRightPressed: incrementCurrentIndex() - } - \endqml - - The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). - - Delegates are instantiated as needed and may be destroyed at any time. - State should \e never be stored in a delegate. - - PathView attaches a number of properties to the root item of the delegate, for example - \c {PathView.isCurrentItem}. In the following example, the root delegate item can access - this attached property directly as \c PathView.isCurrentItem, while the child - \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem. - - \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 - - \bold Note that views do not enable \e clip automatically. If the view - is not clipped by another item or the screen, it will be necessary - to set \e {clip: true} in order to have the out of view items clipped - nicely. - - \sa Path, {declarative/modelviews/pathview}{PathView example} -*/ - -QSGPathView::QSGPathView(QSGItem *parent) - : QSGItem(*(new QSGPathViewPrivate), parent) -{ - Q_D(QSGPathView); - d->init(); -} - -QSGPathView::~QSGPathView() -{ - Q_D(QSGPathView); - d->clear(); - if (d->attType) - d->attType->release(); - if (d->ownModel) - delete d->model; -} - -/*! - \qmlattachedproperty PathView QtQuick2::PathView::view - This attached property holds the view that manages this delegate instance. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty bool QtQuick2::PathView::onPath - This attached property holds whether the item is currently on the path. - - If a pathItemCount has been set, it is possible that some items may - be instantiated, but not considered to be currently on the path. - Usually, these items would be set invisible, for example: - - \qml - Component { - Rectangle { - visible: PathView.onPath - // ... - } - } - \endqml - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty bool QtQuick2::PathView::isCurrentItem - This attached property is true if this delegate is the current item; otherwise false. - - It is attached to each instance of the delegate. - - This property may be used to adjust the appearance of the current item. - - \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 -*/ - -/*! - \qmlproperty model QtQuick2::PathView::model - This property holds the model providing data for the view. - - The model provides a set of data that is used to create the items for the view. - For large or dynamic datasets the model is usually provided by a C++ model object. - Models can also be created directly in QML, using the ListModel element. - - \sa {qmlmodels}{Data Models} -*/ -QVariant QSGPathView::model() const -{ - Q_D(const QSGPathView); - return d->modelVariant; -} - -void QSGPathView::setModel(const QVariant &model) -{ - Q_D(QSGPathView); - if (d->modelVariant == model) - return; - - if (d->model) { - disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), - this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - for (int i=0; iitems.count(); i++){ - QSGItem *p = d->items[i]; - d->model->release(p); - } - d->items.clear(); - } - - d->modelVariant = model; - QObject *object = qvariant_cast(model); - QSGVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { - if (d->ownModel) { - delete d->model; - d->ownModel = false; - } - d->model = vim; - } else { - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this)); - d->ownModel = true; - if (isComponentComplete()) - static_cast(d->model.data())->componentComplete(); - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(model); - } - d->modelCount = 0; - if (d->model) { - connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), - this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - d->modelCount = d->model->count(); - if (d->model->count()) - d->offset = qmlMod(d->offset, qreal(d->model->count())); - if (d->offset < 0) - d->offset = d->model->count() + d->offset; -} - d->regenerate(); - if (d->currentIndex < d->modelCount) - setOffset(qmlMod(d->modelCount - d->currentIndex, d->modelCount)); - else - d->fixOffset(); - emit countChanged(); - emit modelChanged(); -} - -/*! - \qmlproperty int QtQuick2::PathView::count - This property holds the number of items in the model. -*/ -int QSGPathView::count() const -{ - Q_D(const QSGPathView); - return d->model ? d->modelCount : 0; -} - -/*! - \qmlproperty Path QtQuick2::PathView::path - This property holds the path used to lay out the items. - For more information see the \l Path documentation. -*/ -QDeclarativePath *QSGPathView::path() const -{ - Q_D(const QSGPathView); - return d->path; -} - -void QSGPathView::setPath(QDeclarativePath *path) -{ - Q_D(QSGPathView); - if (d->path == path) - return; - if (d->path) - disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated())); - d->path = path; - connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated())); - if (d->isValid() && isComponentComplete()) { - d->clear(); - if (d->attType) { - d->attType->release(); - d->attType = 0; - } - d->regenerate(); - } - emit pathChanged(); -} - -/*! - \qmlproperty int QtQuick2::PathView::currentIndex - This property holds the index of the current item. -*/ -int QSGPathView::currentIndex() const -{ - Q_D(const QSGPathView); - return d->currentIndex; -} - -void QSGPathView::setCurrentIndex(int idx) -{ - Q_D(QSGPathView); - if (d->model && d->modelCount) - idx = qAbs(idx % d->modelCount); - if (d->model && idx != d->currentIndex) { - if (d->currentItem) { - if (QSGPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(false); - d->releaseItem(d->currentItem); - } - d->currentItem = 0; - d->moveReason = QSGPathViewPrivate::SetIndex; - d->currentIndex = idx; - if (d->modelCount) { - int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount; - if (itemIndex < d->items.count()) { - d->currentItem = d->model->item(d->currentIndex, true); - d->currentItem->setFocus(true); - if (QSGPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); - } else { - d->currentItem = d->getItem(d->currentIndex, false); - d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0); - if (QSGPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); - if (d->model->completePending()) - d->model->completeItem(); - } - if (d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange) - d->snapToCurrent(); - d->currentItemOffset = d->positionOfIndex(d->currentIndex); - d->updateHighlight(); - } - emit currentIndexChanged(); - } -} - -QSGItem *QSGPathView::currentItem() const -{ - Q_D(const QSGPathView); - return d->currentItem; -} - -/*! - \qmlmethod QtQuick2::PathView::incrementCurrentIndex() - - Increments the current index. - - \bold Note: methods should only be called after the Component has completed. -*/ -void QSGPathView::incrementCurrentIndex() -{ - Q_D(QSGPathView); - d->moveDirection = QSGPathViewPrivate::Positive; - setCurrentIndex(currentIndex()+1); -} - -/*! - \qmlmethod QtQuick2::PathView::decrementCurrentIndex() - - Decrements the current index. - - \bold Note: methods should only be called after the Component has completed. -*/ -void QSGPathView::decrementCurrentIndex() -{ - Q_D(QSGPathView); - if (d->model && d->modelCount) { - int idx = currentIndex()-1; - if (idx < 0) - idx = d->modelCount - 1; - d->moveDirection = QSGPathViewPrivate::Negative; - setCurrentIndex(idx); - } -} - -/*! - \qmlproperty real QtQuick2::PathView::offset - - The offset specifies how far along the path the items are from their initial positions. - This is a real number that ranges from 0.0 to the count of items in the model. -*/ -qreal QSGPathView::offset() const -{ - Q_D(const QSGPathView); - return d->offset; -} - -void QSGPathView::setOffset(qreal offset) -{ - Q_D(QSGPathView); - d->setOffset(offset); - d->updateCurrent(); -} - -void QSGPathViewPrivate::setOffset(qreal o) -{ - Q_Q(QSGPathView); - if (offset != o) { - if (isValid() && q->isComponentComplete()) { - offset = qmlMod(o, qreal(modelCount)); - if (offset < 0) - offset += qreal(modelCount); - q->refill(); - } else { - offset = o; - } - emit q->offsetChanged(); - } -} - -void QSGPathViewPrivate::setAdjustedOffset(qreal o) -{ - setOffset(o+offsetAdj); -} - -/*! - \qmlproperty Component QtQuick2::PathView::highlight - This property holds the component to use as the highlight. - - An instance of the highlight component will be created for each view. - The geometry of the resultant component instance will be managed by the view - so as to stay with the current item. - - The below example demonstrates how to make a simple highlight. Note the use - of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that - the highlight is hidden when flicked away from the path. - - \qml - Component { - Rectangle { - visible: PathView.onPath - // ... - } - } - \endqml - - \sa highlightItem, highlightRangeMode -*/ - -QDeclarativeComponent *QSGPathView::highlight() const -{ - Q_D(const QSGPathView); - return d->highlightComponent; -} - -void QSGPathView::setHighlight(QDeclarativeComponent *highlight) -{ - Q_D(QSGPathView); - if (highlight != d->highlightComponent) { - d->highlightComponent = highlight; - d->createHighlight(); - d->updateHighlight(); - emit highlightChanged(); - } -} - -/*! - \qmlproperty Item QtQuick2::PathView::highlightItem - - \c highlightItem holds the highlight item, which was created - from the \l highlight component. - - \sa highlight -*/ -QSGItem *QSGPathView::highlightItem() -{ - Q_D(const QSGPathView); - return d->highlightItem; -} -/*! - \qmlproperty real QtQuick2::PathView::preferredHighlightBegin - \qmlproperty real QtQuick2::PathView::preferredHighlightEnd - \qmlproperty enumeration QtQuick2::PathView::highlightRangeMode - - These properties set the preferred range of the highlight (current item) - within the view. The preferred values must be in the range 0.0-1.0. - - If highlightRangeMode is set to \e PathView.NoHighlightRange - - If highlightRangeMode is set to \e PathView.ApplyRange the view will - attempt to maintain the highlight within the range, however - the highlight can move outside of the range at the ends of the path - or due to a mouse interaction. - - If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never - move outside of the range. This means that the current item will change - if a keyboard or mouse action would cause the highlight to move - outside of the range. - - Note that this is the correct way to influence where the - current item ends up when the view moves. For example, if you want the - currently selected item to be in the middle of the path, then set the - highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange. - Then, when the path scrolls, - the currently selected item will be the item at that position. This also applies to - when the currently selected item changes - it will scroll to within the preferred - highlight range. Furthermore, the behaviour of the current item index will occur - whether or not a highlight exists. - - The default value is \e PathView.StrictlyEnforceRange. - - Note that a valid range requires preferredHighlightEnd to be greater - than or equal to preferredHighlightBegin. -*/ -qreal QSGPathView::preferredHighlightBegin() const -{ - Q_D(const QSGPathView); - return d->highlightRangeStart; -} - -void QSGPathView::setPreferredHighlightBegin(qreal start) -{ - Q_D(QSGPathView); - if (d->highlightRangeStart == start || start < 0 || start > 1.0) - return; - d->highlightRangeStart = start; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - refill(); - emit preferredHighlightBeginChanged(); -} - -qreal QSGPathView::preferredHighlightEnd() const -{ - Q_D(const QSGPathView); - return d->highlightRangeEnd; -} - -void QSGPathView::setPreferredHighlightEnd(qreal end) -{ - Q_D(QSGPathView); - if (d->highlightRangeEnd == end || end < 0 || end > 1.0) - return; - d->highlightRangeEnd = end; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - refill(); - emit preferredHighlightEndChanged(); -} - -QSGPathView::HighlightRangeMode QSGPathView::highlightRangeMode() const -{ - Q_D(const QSGPathView); - return d->highlightRangeMode; -} - -void QSGPathView::setHighlightRangeMode(HighlightRangeMode mode) -{ - Q_D(QSGPathView); - if (d->highlightRangeMode == mode) - return; - d->highlightRangeMode = mode; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; - emit highlightRangeModeChanged(); -} - -/*! - \qmlproperty int QtQuick2::PathView::highlightMoveDuration - This property holds the move animation duration of the highlight delegate. - - If the highlightRangeMode is StrictlyEnforceRange then this property - determines the speed that the items move along the path. - - The default value for the duration is 300ms. -*/ -int QSGPathView::highlightMoveDuration() const -{ - Q_D(const QSGPathView); - return d->highlightMoveDuration; -} - -void QSGPathView::setHighlightMoveDuration(int duration) -{ - Q_D(QSGPathView); - if (d->highlightMoveDuration == duration) - return; - d->highlightMoveDuration = duration; - emit highlightMoveDurationChanged(); -} - -/*! - \qmlproperty real QtQuick2::PathView::dragMargin - This property holds the maximum distance from the path that initiate mouse dragging. - - By default the path can only be dragged by clicking on an item. If - dragMargin is greater than zero, a drag can be initiated by clicking - within dragMargin pixels of the path. -*/ -qreal QSGPathView::dragMargin() const -{ - Q_D(const QSGPathView); - return d->dragMargin; -} - -void QSGPathView::setDragMargin(qreal dragMargin) -{ - Q_D(QSGPathView); - if (d->dragMargin == dragMargin) - return; - d->dragMargin = dragMargin; - emit dragMarginChanged(); -} - -/*! - \qmlproperty real QtQuick2::PathView::flickDeceleration - This property holds the rate at which a flick will decelerate. - - The default is 100. -*/ -qreal QSGPathView::flickDeceleration() const -{ - Q_D(const QSGPathView); - return d->deceleration; -} - -void QSGPathView::setFlickDeceleration(qreal dec) -{ - Q_D(QSGPathView); - if (d->deceleration == dec) - return; - d->deceleration = dec; - emit flickDecelerationChanged(); -} - -/*! - \qmlproperty bool QtQuick2::PathView::interactive - - A user cannot drag or flick a PathView that is not interactive. - - This property is useful for temporarily disabling flicking. This allows - special interaction with PathView's children. -*/ -bool QSGPathView::isInteractive() const -{ - Q_D(const QSGPathView); - return d->interactive; -} - -void QSGPathView::setInteractive(bool interactive) -{ - Q_D(QSGPathView); - if (interactive != d->interactive) { - d->interactive = interactive; - if (!interactive) - d->tl.clear(); - emit interactiveChanged(); - } -} - -/*! - \qmlproperty bool QtQuick2::PathView::moving - - This property holds whether the view is currently moving - due to the user either dragging or flicking the view. -*/ -bool QSGPathView::isMoving() const -{ - Q_D(const QSGPathView); - return d->moving; -} - -/*! - \qmlproperty bool QtQuick2::PathView::flicking - - This property holds whether the view is currently moving - due to the user flicking the view. -*/ -bool QSGPathView::isFlicking() const -{ - Q_D(const QSGPathView); - return d->flicking; -} - -/*! - \qmlsignal QtQuick2::PathView::onMovementStarted() - - This handler is called when the view begins moving due to user - interaction. -*/ - -/*! - \qmlsignal QtQuick2::PathView::onMovementEnded() - - This handler is called when the view stops moving due to user - interaction. If a flick was generated, this handler will - be triggered once the flick stops. If a flick was not - generated, the handler will be triggered when the - user stops dragging - i.e. a mouse or touch release. -*/ - -/*! - \qmlsignal QtQuick2::PathView::onFlickStarted() - - This handler is called when the view is flicked. A flick - starts from the point that the mouse or touch is released, - while still in motion. -*/ - -/*! - \qmlsignal QtQuick2::PathView::onFlickEnded() - - This handler is called when the view stops moving due to a flick. -*/ - -/*! - \qmlproperty Component QtQuick2::PathView::delegate - - The delegate provides a template defining each item instantiated by the view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qmlmodels}{Data Model}. - - The number of elements in the delegate has a direct effect on the - flicking performance of the view when pathItemCount is specified. If at all possible, place functionality - that is not needed for the normal display of the delegate in a \l Loader which - can load additional elements when needed. - - Note that the PathView will layout the items based on the size of the root - item in the delegate. - - Here is an example delegate: - \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 -*/ -QDeclarativeComponent *QSGPathView::delegate() const -{ - Q_D(const QSGPathView); - if (d->model) { - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - return dataModel->delegate(); - } - - return 0; -} - -void QSGPathView::setDelegate(QDeclarativeComponent *delegate) -{ - Q_D(QSGPathView); - if (delegate == this->delegate()) - return; - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this)); - d->ownModel = true; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) { - int oldCount = dataModel->count(); - dataModel->setDelegate(delegate); - d->modelCount = dataModel->count(); - d->regenerate(); - if (oldCount != dataModel->count()) - emit countChanged(); - emit delegateChanged(); - } -} - -/*! - \qmlproperty int QtQuick2::PathView::pathItemCount - This property holds the number of items visible on the path at any one time. -*/ -int QSGPathView::pathItemCount() const -{ - Q_D(const QSGPathView); - return d->pathItems; -} - -void QSGPathView::setPathItemCount(int i) -{ - Q_D(QSGPathView); - if (i == d->pathItems) - return; - if (i < 1) - i = 1; - d->pathItems = i; - d->updateMappedRange(); - if (d->isValid() && isComponentComplete()) { - d->regenerate(); - } - emit pathItemCountChanged(); -} - -QPointF QSGPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const -{ - //XXX maybe do recursively at increasing resolution. - qreal mindist = 1e10; // big number - QPointF nearPoint = path->pointAt(0); - qreal nearPc = 0; - for (qreal i=1; i < 1000; i++) { - QPointF pt = path->pointAt(i/1000.0); - QPointF diff = pt - point; - qreal dist = diff.x()*diff.x() + diff.y()*diff.y(); - if (dist < mindist) { - nearPoint = pt; - nearPc = i; - mindist = dist; - } - } - - if (nearPercent) - *nearPercent = nearPc / 1000.0; - - return nearPoint; -} - -void QSGPathView::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGPathView); - if (d->interactive) { - d->handleMousePressEvent(event); - event->accept(); - } else { - QSGItem::mousePressEvent(event); - } -} - -void QSGPathViewPrivate::handleMousePressEvent(QMouseEvent *event) -{ - Q_Q(QSGPathView); - if (!interactive || !items.count()) - return; - QPointF scenePoint = q->mapToScene(event->localPos()); - int idx = 0; - for (; idx < items.count(); ++idx) { - QRectF rect = items.at(idx)->boundingRect(); - rect = items.at(idx)->mapRectToScene(rect); - if (rect.contains(scenePoint)) - break; - } - if (idx == items.count() && dragMargin == 0.) // didn't click on an item - return; - - startPoint = pointNear(event->localPos(), &startPc); - if (idx == items.count()) { - qreal distance = qAbs(event->localPos().x() - startPoint.x()) + qAbs(event->localPos().y() - startPoint.y()); - if (distance > dragMargin) - return; - } - - if (tl.isActive() && flicking) - stealMouse = true; // If we've been flicked then steal the click. - else - stealMouse = false; - - lastElapsed = 0; - lastDist = 0; - QSGItemPrivate::start(lastPosTime); - tl.clear(); -} - -void QSGPathView::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QSGPathView); - if (d->interactive) { - d->handleMouseMoveEvent(event); - if (d->stealMouse) - setKeepMouseGrab(true); - event->accept(); - } else { - QSGItem::mouseMoveEvent(event); - } -} - -void QSGPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event) -{ - Q_Q(QSGPathView); - if (!interactive || !lastPosTime.isValid()) - return; - - qreal newPc; - QPointF pathPoint = pointNear(event->localPos(), &newPc); - if (!stealMouse) { - QPointF delta = pathPoint - startPoint; - if (qAbs(delta.x()) > qApp->styleHints()->startDragDistance() || qAbs(delta.y()) > qApp->styleHints()->startDragDistance()) { - stealMouse = true; - startPc = newPc; - } - } - - if (stealMouse) { - moveReason = QSGPathViewPrivate::Mouse; - qreal diff = (newPc - startPc)*modelCount*mappedRange; - if (diff) { - q->setOffset(offset + diff); - - if (diff > modelCount/2) - diff -= modelCount; - else if (diff < -modelCount/2) - diff += modelCount; - - lastElapsed = QSGItemPrivate::restart(lastPosTime); - lastDist = diff; - startPc = newPc; - } - if (!moving) { - moving = true; - emit q->movingChanged(); - emit q->movementStarted(); - } - } -} - -void QSGPathView::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGPathView); - if (d->interactive) { - d->handleMouseReleaseEvent(event); - event->accept(); - ungrabMouse(); - } else { - QSGItem::mouseReleaseEvent(event); - } -} - -void QSGPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *) -{ - Q_Q(QSGPathView); - stealMouse = false; - q->setKeepMouseGrab(false); - if (!interactive || !lastPosTime.isValid()) - return; - - qreal elapsed = qreal(lastElapsed + QSGItemPrivate::elapsed(lastPosTime)) / 1000.; - qreal velocity = elapsed > 0. ? lastDist / elapsed : 0; - if (model && modelCount && qAbs(velocity) > 1.) { - qreal count = pathItems == -1 ? modelCount : pathItems; - if (qAbs(velocity) > count * 2) // limit velocity - velocity = (velocity > 0 ? count : -count) * 2; - // Calculate the distance to be travelled - qreal v2 = velocity*velocity; - qreal accel = deceleration/10; - // + 0.25 to encourage moving at least one item in the flick direction - qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25)); - if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) { - // round to nearest item. - if (velocity > 0.) - dist = qRound(dist + offset) - offset; - else - dist = qRound(dist - offset) + offset; - // Calculate accel required to stop on item boundary - if (dist <= 0.) { - dist = 0.; - accel = 0.; - } else { - accel = v2 / (2.0f * qAbs(dist)); - } - } - offsetAdj = 0.0; - moveOffset.setValue(offset); - tl.accel(moveOffset, velocity, accel, dist); - tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this)); - if (!flicking) { - flicking = true; - emit q->flickingChanged(); - emit q->flickStarted(); - } - } else { - fixOffset(); - } - - lastPosTime.invalidate(); - if (!tl.isActive()) - q->movementEnding(); -} - -bool QSGPathView::sendMouseEvent(QMouseEvent *event) -{ - Q_D(QSGPathView); - QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); - QSGCanvas *c = canvas(); - QSGItem *grabber = c ? c->mouseGrabberItem() : 0; - bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { - QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - mouseEvent.setAccepted(false); - - switch (mouseEvent.type()) { - case QEvent::MouseMove: - d->handleMouseMoveEvent(&mouseEvent); - break; - case QEvent::MouseButtonPress: - d->handleMousePressEvent(&mouseEvent); - stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above - break; - case QEvent::MouseButtonRelease: - d->handleMouseReleaseEvent(&mouseEvent); - break; - default: - break; - } - grabber = c->mouseGrabberItem(); - if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) - grabMouse(); - - return d->stealMouse; - } else if (d->lastPosTime.isValid()) { - d->lastPosTime.invalidate(); - d->fixOffset(); - } - if (event->type() == QEvent::MouseButtonRelease) - d->stealMouse = false; - return false; -} - -bool QSGPathView::childMouseEventFilter(QSGItem *i, QEvent *e) -{ - Q_D(QSGPathView); - if (!isVisible() || !d->interactive) - return QSGItem::childMouseEventFilter(i, e); - - switch (e->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - return sendMouseEvent(static_cast(e)); - default: - break; - } - - return QSGItem::childMouseEventFilter(i, e); -} - -void QSGPathView::mouseUngrabEvent() -{ - Q_D(QSGPathView); - if (d->stealMouse) { - // if our mouse grab has been removed (probably by a Flickable), - // fix our state - d->stealMouse = false; - setKeepMouseGrab(false); - d->lastPosTime.invalidate(); - } -} - -void QSGPathView::updatePolish() -{ - QSGItem::updatePolish(); - refill(); -} - -void QSGPathView::componentComplete() -{ - Q_D(QSGPathView); - if (d->model && d->ownModel) - static_cast(d->model.data())->componentComplete(); - - QSGItem::componentComplete(); - - d->createHighlight(); - // It is possible that a refill has already happended to to Path - // bindings being handled in the componentComplete(). If so - // don't do it again. - if (d->items.count() == 0 && d->model) { - d->modelCount = d->model->count(); - d->regenerate(); - } - d->updateHighlight(); - - if (d->modelCount) - emit countChanged(); -} - -void QSGPathView::refill() -{ - Q_D(QSGPathView); - if (!d->isValid() || !isComponentComplete()) - return; - - d->layoutScheduled = false; - bool currentVisible = false; - - // first move existing items and remove items off path - int idx = d->firstIndex; - QList::iterator it = d->items.begin(); - while (it != d->items.end()) { - qreal pos = d->positionOfIndex(idx); - QSGItem *item = *it; - if (pos >= 0.0) { - d->updateItem(item, pos); - if (idx == d->currentIndex) { - currentVisible = true; - d->currentItemOffset = pos; - } - ++it; - } else { - // qDebug() << "release"; - d->updateItem(item, 1.0); - d->releaseItem(item); - if (it == d->items.begin()) { - if (++d->firstIndex >= d->modelCount) - d->firstIndex = 0; - } - it = d->items.erase(it); - } - ++idx; - if (idx >= d->modelCount) - idx = 0; - } - if (!d->items.count()) - d->firstIndex = -1; - - if (d->modelCount) { - // add items to beginning and end - int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount); - if (d->items.count() < count) { - int idx = qRound(d->modelCount - d->offset) % d->modelCount; - qreal startPos = 0.0; - if (d->haveHighlightRange && d->highlightRangeMode != QSGPathView::NoHighlightRange) - startPos = d->highlightRangeStart; - if (d->firstIndex >= 0) { - startPos = d->positionOfIndex(d->firstIndex); - idx = (d->firstIndex + d->items.count()) % d->modelCount; - } - qreal pos = d->positionOfIndex(idx); - while ((pos > startPos || !d->items.count()) && d->items.count() < count) { - // qDebug() << "append" << idx; - QSGItem *item = d->getItem(idx); - if (d->model->completePending()) - item->setZ(idx+1); - if (d->currentIndex == idx) { - currentVisible = true; - d->currentItemOffset = pos; - } - if (d->items.count() == 0) - d->firstIndex = idx; - d->items.append(item); - d->updateItem(item, pos); - if (d->model->completePending()) - d->model->completeItem(); - ++idx; - if (idx >= d->modelCount) - idx = 0; - pos = d->positionOfIndex(idx); - } - - idx = d->firstIndex - 1; - if (idx < 0) - idx = d->modelCount - 1; - pos = d->positionOfIndex(idx); - while ((pos >= 0.0 && pos < startPos) && d->items.count() < count) { - // qDebug() << "prepend" << idx; - QSGItem *item = d->getItem(idx); - if (d->model->completePending()) - item->setZ(idx+1); - if (d->currentIndex == idx) { - currentVisible = true; - d->currentItemOffset = pos; - } - d->items.prepend(item); - d->updateItem(item, pos); - if (d->model->completePending()) - d->model->completeItem(); - d->firstIndex = idx; - idx = d->firstIndex - 1; - if (idx < 0) - idx = d->modelCount - 1; - pos = d->positionOfIndex(idx); - } - } - } - - if (!currentVisible) { - d->currentItemOffset = 1.0; - if (d->currentItem) { - if (QSGPathViewAttached *att = d->attached(d->currentItem)) - att->setOnPath(false); - } else if (d->currentIndex >= 0 && d->currentIndex < d->modelCount) { - d->currentItem = d->getItem(d->currentIndex, false); - d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0); - if (QSGPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); - if (d->model->completePending()) - d->model->completeItem(); - } - } else if (!d->currentItem) { - d->currentItem = d->model->item(d->currentIndex, true); - d->currentItem->setFocus(true); - if (QSGPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); - } - - if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange) { - d->updateItem(d->highlightItem, d->highlightRangeStart); - if (QSGPathViewAttached *att = d->attached(d->highlightItem)) - att->setOnPath(true); - } else if (d->highlightItem && d->moveReason != QSGPathViewPrivate::SetIndex) { - d->updateItem(d->highlightItem, d->currentItemOffset); - if (QSGPathViewAttached *att = d->attached(d->highlightItem)) - att->setOnPath(currentVisible); - } - while (d->itemCache.count()) - d->releaseItem(d->itemCache.takeLast()); -} - -void QSGPathView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) -{ - Q_D(QSGPathView); - if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete()) - return; - - if (reset) { - d->modelCount = d->model->count(); - d->regenerate(); - emit countChanged(); - return; - } - - if (changeSet.removes().isEmpty() && changeSet.inserts().isEmpty()) - return; - - const int modelCount = d->modelCount; - int moveId = -1; - int moveOffset; - bool currentChanged = false; - bool changedOffset = false; - bool removed = false; - bool inserted = false; - foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) { - removed = true; - if (moveId == -1 && d->currentIndex >= r.index + r.count) { - d->currentIndex -= r.count; - currentChanged = true; - } else if (moveId == -1 && d->currentIndex >= r.index && d->currentIndex < r.index + r.count) { - // current item has been removed. - d->currentIndex = qMin(r.index, d->modelCount - r.count - 1); - if (r.isMove()) { - moveId = r.moveId; - moveOffset = d->currentIndex - r.index; - } else if (d->currentItem) { - if (QSGPathViewAttached *att = d->attached(d->currentItem)) - att->setIsCurrentItem(true); - d->releaseItem(d->currentItem); - d->currentItem = 0; - } - currentChanged = true; - } - - if (r.index > d->currentIndex) { - if (d->offset >= r.count) { - changedOffset = true; - d->offset -= r.count; - d->offsetAdj -= r.count; - } - } - d->modelCount -= r.count; - } - foreach (const QDeclarativeChangeSet::Insert &i, changeSet.inserts()) { - inserted = true; - if (d->modelCount) { - if (moveId == -1 && i.index <= d->currentIndex) { - d->currentIndex += i.count; - } else if (d->offset != 0) { - if (moveId != -1 && moveId == i.moveId) - d->currentIndex = i.index + moveOffset; - d->offset += i.count; - d->offsetAdj += i.count; - } - } - d->modelCount += i.count; - } - - d->itemCache += d->items; - d->items.clear(); - - if (!d->modelCount) { - while (d->itemCache.count()) - d->releaseItem(d->itemCache.takeLast()); - d->offset = 0; - changedOffset = true; - d->tl.reset(d->moveOffset); - } else if (removed) { - d->regenerate(); - d->updateCurrent(); - if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange) - d->snapToCurrent(); - } else if (inserted) { - d->firstIndex = -1; - d->updateMappedRange(); - d->scheduleLayout(); - } - if (changedOffset) - emit offsetChanged(); - if (currentChanged) - emit currentIndexChanged(); - if (d->modelCount != modelCount) - emit countChanged(); -} - -void QSGPathView::createdItem(int index, QSGItem *item) -{ - Q_D(QSGPathView); - if (d->requestedIndex != index) { - if (!d->attType) { - // pre-create one metatype to share with all attached objects - d->attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(this)); - foreach (const QString &attr, d->path->attributes()) - d->attType->createProperty(attr.toUtf8()); - } - qPathViewAttachedType = d->attType; - QSGPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); - qPathViewAttachedType = 0; - if (att) { - att->m_view = this; - att->setOnPath(false); - } - item->setParentItem(this); - d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0); - } -} - -void QSGPathView::destroyingItem(QSGItem *item) -{ - Q_UNUSED(item); -} - -void QSGPathView::ticked() -{ - Q_D(QSGPathView); - d->updateCurrent(); -} - -void QSGPathView::movementEnding() -{ - Q_D(QSGPathView); - if (d->flicking) { - d->flicking = false; - emit flickingChanged(); - emit flickEnded(); - } - if (d->moving && !d->stealMouse) { - d->moving = false; - emit movingChanged(); - emit movementEnded(); - } -} - -// find the item closest to the snap position -int QSGPathViewPrivate::calcCurrentIndex() -{ - int current = -1; - if (modelCount && model && items.count()) { - offset = qmlMod(offset, modelCount); - if (offset < 0) - offset += modelCount; - current = qRound(qAbs(qmlMod(modelCount - offset, modelCount))); - current = current % modelCount; - } - - return current; -} - -void QSGPathViewPrivate::updateCurrent() -{ - Q_Q(QSGPathView); - if (moveReason != Mouse) - return; - if (!modelCount || !haveHighlightRange || highlightRangeMode != QSGPathView::StrictlyEnforceRange) - return; - - int idx = calcCurrentIndex(); - if (model && idx != currentIndex) { - if (currentItem) { - if (QSGPathViewAttached *att = attached(currentItem)) - att->setIsCurrentItem(false); - releaseItem(currentItem); - } - currentIndex = idx; - currentItem = 0; - int itemIndex = (idx - firstIndex + modelCount) % modelCount; - if (itemIndex < items.count()) { - currentItem = model->item(currentIndex, true); - currentItem->setFocus(true); - if (QSGPathViewAttached *att = attached(currentItem)) - att->setIsCurrentItem(true); - } else if (currentIndex >= 0 && currentIndex < modelCount) { - currentItem = getItem(currentIndex, false); - updateItem(currentItem, currentIndex < firstIndex ? 0.0 : 1.0); - if (QSGPathViewAttached *att = attached(currentItem)) - att->setIsCurrentItem(true); - if (model->completePending()) - model->completeItem(); - } - emit q->currentIndexChanged(); - } -} - -void QSGPathViewPrivate::fixOffsetCallback(void *d) -{ - ((QSGPathViewPrivate *)d)->fixOffset(); -} - -void QSGPathViewPrivate::fixOffset() -{ - Q_Q(QSGPathView); - if (model && items.count()) { - if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) { - int curr = calcCurrentIndex(); - if (curr != currentIndex) - q->setCurrentIndex(curr); - else - snapToCurrent(); - } - } -} - -void QSGPathViewPrivate::snapToCurrent() -{ - if (!model || modelCount <= 0) - return; - - qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount); - - moveReason = Other; - offsetAdj = 0.0; - tl.reset(moveOffset); - moveOffset.setValue(offset); - - const int duration = highlightMoveDuration; - - if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) { - qreal distance = modelCount - targetOffset + offset; - if (targetOffset > moveOffset) { - tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance)); - tl.set(moveOffset, modelCount); - tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance)); - } else { - tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); - } - } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) { - qreal distance = modelCount - offset + targetOffset; - if (targetOffset < moveOffset) { - tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance)); - tl.set(moveOffset, 0.0); - tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance)); - } else { - tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); - } - } else { - tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration); - } - moveDirection = Shortest; -} - -QSGPathViewAttached *QSGPathView::qmlAttachedProperties(QObject *obj) -{ - return new QSGPathViewAttached(obj); -} - -QT_END_NAMESPACE - diff --git a/src/declarative/items/qsgpathview_p.h b/src/declarative/items/qsgpathview_p.h deleted file mode 100644 index b70745ebcb..0000000000 --- a/src/declarative/items/qsgpathview_p.h +++ /dev/null @@ -1,257 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGPATHVIEW_P_H -#define QSGPATHVIEW_P_H - -#include "qsgitem.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QDeclarativeChangeSet; - -class QSGPathViewPrivate; -class QSGPathViewAttached; -class Q_AUTOTEST_EXPORT QSGPathView : public QSGItem -{ - Q_OBJECT - - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged) - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) - Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged) - Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged) - - Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged) - Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged) - - Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged) - Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged) - Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged) - Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) - - Q_PROPERTY(qreal dragMargin READ dragMargin WRITE setDragMargin NOTIFY dragMarginChanged) - Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) - Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) - - Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged) - Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) - - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount NOTIFY pathItemCountChanged) - - Q_ENUMS(HighlightRangeMode) - -public: - QSGPathView(QSGItem *parent=0); - virtual ~QSGPathView(); - - QVariant model() const; - void setModel(const QVariant &); - - QDeclarativePath *path() const; - void setPath(QDeclarativePath *); - - int currentIndex() const; - void setCurrentIndex(int idx); - - QSGItem *currentItem() const; - - qreal offset() const; - void setOffset(qreal offset); - - QDeclarativeComponent *highlight() const; - void setHighlight(QDeclarativeComponent *highlight); - QSGItem *highlightItem(); - - enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; - HighlightRangeMode highlightRangeMode() const; - void setHighlightRangeMode(HighlightRangeMode mode); - - qreal preferredHighlightBegin() const; - void setPreferredHighlightBegin(qreal); - - qreal preferredHighlightEnd() const; - void setPreferredHighlightEnd(qreal); - - int highlightMoveDuration() const; - void setHighlightMoveDuration(int); - - qreal dragMargin() const; - void setDragMargin(qreal margin); - - qreal flickDeceleration() const; - void setFlickDeceleration(qreal dec); - - bool isInteractive() const; - void setInteractive(bool); - - bool isMoving() const; - bool isFlicking() const; - - int count() const; - - QDeclarativeComponent *delegate() const; - void setDelegate(QDeclarativeComponent *); - - int pathItemCount() const; - void setPathItemCount(int); - - static QSGPathViewAttached *qmlAttachedProperties(QObject *); - -public Q_SLOTS: - void incrementCurrentIndex(); - void decrementCurrentIndex(); - -Q_SIGNALS: - void currentIndexChanged(); - void offsetChanged(); - void modelChanged(); - void countChanged(); - void pathChanged(); - void preferredHighlightBeginChanged(); - void preferredHighlightEndChanged(); - void highlightRangeModeChanged(); - void dragMarginChanged(); - void snapPositionChanged(); - void delegateChanged(); - void pathItemCountChanged(); - void flickDecelerationChanged(); - void interactiveChanged(); - void movingChanged(); - void flickingChanged(); - void highlightChanged(); - void highlightItemChanged(); - void highlightMoveDurationChanged(); - void movementStarted(); - void movementEnded(); - void flickStarted(); - void flickEnded(); - -protected: - virtual void updatePolish(); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *); - bool sendMouseEvent(QMouseEvent *event); - bool childMouseEventFilter(QSGItem *, QEvent *); - void mouseUngrabEvent(); - void componentComplete(); - -private Q_SLOTS: - void refill(); - void ticked(); - void movementEnding(); - void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); - void createdItem(int index, QSGItem *item); - void destroyingItem(QSGItem *item); - void pathUpdated(); - -private: - friend class QSGPathViewAttached; - Q_DISABLE_COPY(QSGPathView) - Q_DECLARE_PRIVATE(QSGPathView) -}; - -class QDeclarativeOpenMetaObject; -class QSGPathViewAttached : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QSGPathView *view READ view CONSTANT) - Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) - Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged) - -public: - QSGPathViewAttached(QObject *parent); - ~QSGPathViewAttached(); - - QSGPathView *view() { return m_view; } - - bool isCurrentItem() const { return m_isCurrent; } - void setIsCurrentItem(bool c) { - if (m_isCurrent != c) { - m_isCurrent = c; - emit currentItemChanged(); - } - } - - QVariant value(const QByteArray &name) const; - void setValue(const QByteArray &name, const QVariant &val); - - bool isOnPath() const { return m_onPath; } - void setOnPath(bool on) { - if (on != m_onPath) { - m_onPath = on; - emit pathChanged(); - } - } - qreal m_percent; - -Q_SIGNALS: - void currentItemChanged(); - void pathChanged(); - -private: - friend class QSGPathViewPrivate; - friend class QSGPathView; - QSGPathView *m_view; - QDeclarativeOpenMetaObject *m_metaobject; - bool m_onPath : 1; - bool m_isCurrent : 1; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGPathView) -QML_DECLARE_TYPEINFO(QSGPathView, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - -#endif // QSGPATHVIEW_P_H diff --git a/src/declarative/items/qsgpathview_p_p.h b/src/declarative/items/qsgpathview_p_p.h deleted file mode 100644 index 10549d9709..0000000000 --- a/src/declarative/items/qsgpathview_p_p.h +++ /dev/null @@ -1,194 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QDECLARATIVEPATHVIEW_P_H -#define QDECLARATIVEPATHVIEW_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgpathview_p.h" -#include "qsgitem_p.h" -#include "qsgvisualdatamodel_p.h" - -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QDeclarativeOpenMetaObjectType; -class QSGPathViewAttached; -class QSGPathViewPrivate : public QSGItemPrivate, public QSGItemChangeListener -{ - Q_DECLARE_PUBLIC(QSGPathView) - -public: - QSGPathViewPrivate() - : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0), lastDist(0) - , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0) - , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true) - , autoHighlight(true), highlightUp(false), layoutScheduled(false) - , moving(false), flicking(false) - , dragMargin(0), deceleration(100) - , moveOffset(this, &QSGPathViewPrivate::setAdjustedOffset) - , firstIndex(-1), pathItems(-1), requestedIndex(-1) - , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0) - , moveHighlight(this, &QSGPathViewPrivate::setHighlightPosition) - , highlightPosition(0) - , highlightRangeStart(0), highlightRangeEnd(0) - , highlightRangeMode(QSGPathView::StrictlyEnforceRange) - , highlightMoveDuration(300), modelCount(0) - { - } - - void init(); - - void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { - if ((newGeometry.size() != oldGeometry.size()) - && (!highlightItem || item != highlightItem)) { - if (QSGPathViewAttached *att = attached(item)) - att->m_percent = -1; - scheduleLayout(); - } - } - - void scheduleLayout() { - Q_Q(QSGPathView); - if (!layoutScheduled) { - layoutScheduled = true; - q->polish(); - } - } - - QSGItem *getItem(int modelIndex, bool onPath = true); - void releaseItem(QSGItem *item); - QSGPathViewAttached *attached(QSGItem *item); - void clear(); - void updateMappedRange(); - qreal positionOfIndex(qreal index) const; - void createHighlight(); - void updateHighlight(); - void setHighlightPosition(qreal pos); - bool isValid() const { - return model && model->count() > 0 && model->isValid() && path; - } - - void handleMousePressEvent(QMouseEvent *event); - void handleMouseMoveEvent(QMouseEvent *event); - void handleMouseReleaseEvent(QMouseEvent *); - - int calcCurrentIndex(); - void updateCurrent(); - static void fixOffsetCallback(void*); - void fixOffset(); - void setOffset(qreal offset); - void setAdjustedOffset(qreal offset); - void regenerate(); - void updateItem(QSGItem *, qreal); - void snapToCurrent(); - QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const; - - QDeclarativePath *path; - int currentIndex; - QDeclarativeGuard currentItem; - qreal currentItemOffset; - qreal startPc; - QPointF startPoint; - qreal lastDist; - int lastElapsed; - qreal offset; - qreal offsetAdj; - qreal mappedRange; - bool stealMouse : 1; - bool ownModel : 1; - bool interactive : 1; - bool haveHighlightRange : 1; - bool autoHighlight : 1; - bool highlightUp : 1; - bool layoutScheduled : 1; - bool moving : 1; - bool flicking : 1; - QElapsedTimer lastPosTime; - QPointF lastPos; - qreal dragMargin; - qreal deceleration; - QDeclarativeTimeLine tl; - QDeclarativeTimeLineValueProxy moveOffset; - int firstIndex; - int pathItems; - int requestedIndex; - QList items; - QList itemCache; - QDeclarativeGuard model; - QVariant modelVariant; - enum MovementReason { Other, SetIndex, Mouse }; - MovementReason moveReason; - enum MovementDirection { Shortest, Negative, Positive }; - MovementDirection moveDirection; - QDeclarativeOpenMetaObjectType *attType; - QDeclarativeComponent *highlightComponent; - QSGItem *highlightItem; - QDeclarativeTimeLineValueProxy moveHighlight; - qreal highlightPosition; - qreal highlightRangeStart; - qreal highlightRangeEnd; - QSGPathView::HighlightRangeMode highlightRangeMode; - int highlightMoveDuration; - int modelCount; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/declarative/items/qsgpincharea.cpp b/src/declarative/items/qsgpincharea.cpp deleted file mode 100644 index 2dbe683f26..0000000000 --- a/src/declarative/items/qsgpincharea.cpp +++ /dev/null @@ -1,600 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtSG module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgpincharea_p_p.h" -#include "qsgcanvas.h" - -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -/*! - \qmlclass PinchEvent QSGPinchEvent - \inqmlmodule QtQuick 2 - \ingroup qml-event-elements - \brief The PinchEvent object provides information about a pinch event. - - \bold {The PinchEvent element was added in QtQuick 1.1} - - The \c center, \c startCenter, \c previousCenter properties provide the center position between the two touch points. - - The \c scale and \c previousScale properties provide the scale factor. - - The \c angle, \c previousAngle and \c rotation properties provide the angle between the two points and the amount of rotation. - - The \c point1, \c point2, \c startPoint1, \c startPoint2 properties provide the positions of the touch points. - - The \c accepted property may be set to false in the \c onPinchStarted handler if the gesture should not - be handled. - - \sa PinchArea -*/ - -/*! - \qmlproperty QPointF QtQuick2::PinchEvent::center - \qmlproperty QPointF QtQuick2::PinchEvent::startCenter - \qmlproperty QPointF QtQuick2::PinchEvent::previousCenter - - These properties hold the position of the center point between the two touch points. - - \list - \o \c center is the current center point - \o \c previousCenter is the center point of the previous event. - \o \c startCenter is the center point when the gesture began - \endlist -*/ - -/*! - \qmlproperty real QtQuick2::PinchEvent::scale - \qmlproperty real QtQuick2::PinchEvent::previousScale - - These properties hold the scale factor determined by the change in distance between the two touch points. - - \list - \o \c scale is the current scale factor. - \o \c previousScale is the scale factor of the previous event. - \endlist - - When a pinch gesture is started, the scale is 1.0. -*/ - -/*! - \qmlproperty real QtQuick2::PinchEvent::angle - \qmlproperty real QtQuick2::PinchEvent::previousAngle - \qmlproperty real QtQuick2::PinchEvent::rotation - - These properties hold the angle between the two touch points. - - \list - \o \c angle is the current angle between the two points in the range -180 to 180. - \o \c previousAngle is the angle of the previous event. - \o \c rotation is the total rotation since the pinch gesture started. - \endlist - - When a pinch gesture is started, the rotation is 0.0. -*/ - -/*! - \qmlproperty QPointF QtQuick2::PinchEvent::point1 - \qmlproperty QPointF QtQuick2::PinchEvent::startPoint1 - \qmlproperty QPointF QtQuick2::PinchEvent::point2 - \qmlproperty QPointF QtQuick2::PinchEvent::startPoint2 - - These properties provide the actual touch points generating the pinch. - - \list - \o \c point1 and \c point2 hold the current positions of the points. - \o \c startPoint1 and \c startPoint2 hold the positions of the points when the second point was touched. - \endlist -*/ - -/*! - \qmlproperty bool QtQuick2::PinchEvent::accepted - - Setting this property to false in the \c PinchArea::onPinchStarted handler - will result in no further pinch events being generated, and the gesture - ignored. -*/ - -/*! - \qmlproperty int QtQuick2::PinchEvent::pointCount - - Holds the number of points currently touched. The PinchArea will not react - until two touch points have initited a gesture, but will remain active until - all touch points have been released. -*/ - -QSGPinch::QSGPinch() - : m_target(0), m_minScale(1.0), m_maxScale(1.0) - , m_minRotation(0.0), m_maxRotation(0.0) - , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX) - , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false) -{ -} - -QSGPinchAreaPrivate::~QSGPinchAreaPrivate() -{ - delete pinch; -} - -/*! - \qmlclass PinchArea QSGPinchArea - \inqmlmodule QtQuick 2 - \brief The PinchArea item enables simple pinch gesture handling. - \inherits Item - - \bold {The PinchArea element was added in QtQuick 1.1} - - A PinchArea is an invisible item that is typically used in conjunction with - a visible item in order to provide pinch gesture handling for that item. - - The \l enabled property is used to enable and disable pinch handling for - the proxied item. When disabled, the pinch area becomes transparent to - mouse/touch events. - - PinchArea can be used in two ways: - - \list - \o setting a \c pinch.target to provide automatic interaction with an element - \o using the onPinchStarted, onPinchUpdated and onPinchFinished handlers - \endlist - - \sa PinchEvent -*/ - -/*! - \qmlsignal QtQuick2::PinchArea::onPinchStarted() - - This handler is called when the pinch area detects that a pinch gesture has started. - - The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, - including the scale, center and angle of the pinch. - - To ignore this gesture set the \c pinch.accepted property to false. The gesture - will be cancelled and no further events will be sent. -*/ - -/*! - \qmlsignal QtQuick2::PinchArea::onPinchUpdated() - - This handler is called when the pinch area detects that a pinch gesture has changed. - - The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, - including the scale, center and angle of the pinch. -*/ - -/*! - \qmlsignal QtQuick2::PinchArea::onPinchFinished() - - This handler is called when the pinch area detects that a pinch gesture has finished. - - The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, - including the scale, center and angle of the pinch. -*/ - - -/*! - \qmlproperty Item QtQuick2::PinchArea::pinch.target - \qmlproperty bool QtQuick2::PinchArea::pinch.active - \qmlproperty real QtQuick2::PinchArea::pinch.minimumScale - \qmlproperty real QtQuick2::PinchArea::pinch.maximumScale - \qmlproperty real QtQuick2::PinchArea::pinch.minimumRotation - \qmlproperty real QtQuick2::PinchArea::pinch.maximumRotation - \qmlproperty enumeration QtQuick2::PinchArea::pinch.dragAxis - \qmlproperty real QtQuick2::PinchArea::pinch.minimumX - \qmlproperty real QtQuick2::PinchArea::pinch.maximumX - \qmlproperty real QtQuick2::PinchArea::pinch.minimumY - \qmlproperty real QtQuick2::PinchArea::pinch.maximumY - - \c pinch provides a convenient way to make an item react to pinch gestures. - - \list - \i \c pinch.target specifies the id of the item to drag. - \i \c pinch.active specifies if the target item is currently being dragged. - \i \c pinch.minimumScale and \c pinch.maximumScale limit the range of the Item::scale property. - \i \c pinch.minimumRotation and \c pinch.maximumRotation limit the range of the Item::rotation property. - \i \c pinch.dragAxis specifies whether dragging in not allowed (\c Pinch.NoDrag), can be done horizontally (\c Pinch.XAxis), vertically (\c Pinch.YAxis), or both (\c Pinch.XandYAxis) - \i \c pinch.minimum and \c pinch.maximum limit how far the target can be dragged along the corresponding axes. - \endlist -*/ - -QSGPinchArea::QSGPinchArea(QSGItem *parent) - : QSGItem(*(new QSGPinchAreaPrivate), parent) -{ - Q_D(QSGPinchArea); - d->init(); -} - -QSGPinchArea::~QSGPinchArea() -{ -} -/*! - \qmlproperty bool QtQuick2::PinchArea::enabled - This property holds whether the item accepts pinch gestures. - - This property defaults to true. -*/ -bool QSGPinchArea::isEnabled() const -{ - Q_D(const QSGPinchArea); - return d->absorb; -} - -void QSGPinchArea::setEnabled(bool a) -{ - Q_D(QSGPinchArea); - if (a != d->absorb) { - d->absorb = a; - emit enabledChanged(); - } -} - -void QSGPinchArea::touchEvent(QTouchEvent *event) -{ - Q_D(QSGPinchArea); - if (!d->absorb || !isVisible()) { - QSGItem::event(event); - return; - } - - switch (event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - d->touchPoints.clear(); - for (int i = 0; i < event->touchPoints().count(); ++i) { - if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) { - d->touchPoints << event->touchPoints().at(i); - } - } - updatePinch(); - break; - case QEvent::TouchEnd: - d->touchPoints.clear(); - updatePinch(); - break; - default: - QSGItem::event(event); - } -} - -void QSGPinchArea::updatePinch() -{ - Q_D(QSGPinchArea); - if (d->touchPoints.count() == 0) { - if (d->inPinch) { - d->inPinch = false; - QPointF pinchCenter = mapFromScene(d->sceneLastCenter); - QSGPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation); - pe.setStartCenter(d->pinchStartCenter); - pe.setPreviousCenter(pinchCenter); - pe.setPreviousAngle(d->pinchLastAngle); - pe.setPreviousScale(d->pinchLastScale); - pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); - pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); - pe.setPoint1(mapFromScene(d->lastPoint1)); - pe.setPoint2(mapFromScene(d->lastPoint2)); - emit pinchFinished(&pe); - d->pinchStartDist = 0; - d->pinchActivated = false; - if (d->pinch && d->pinch->target()) - d->pinch->setActive(false); - } - d->initPinch = false; - d->pinchRejected = false; - d->stealMouse = false; - setKeepMouseGrab(false); - QSGCanvas *c = canvas(); - if (c && c->mouseGrabberItem() == this) - ungrabMouse(); - return; - } - QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0); - QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0); - if (d->touchPoints.count() == 2 - && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) { - d->id1 = touchPoint1.id(); - d->sceneStartPoint1 = touchPoint1.scenePos(); - d->sceneStartPoint2 = touchPoint2.scenePos(); - d->pinchActivated = true; - d->initPinch = true; - } - if (d->pinchActivated && !d->pinchRejected){ - const int dragThreshold = qApp->styleHints()->startDragDistance(); - QPointF p1 = touchPoint1.scenePos(); - QPointF p2 = touchPoint2.scenePos(); - qreal dx = p1.x() - p2.x(); - qreal dy = p1.y() - p2.y(); - qreal dist = sqrt(dx*dx + dy*dy); - QPointF sceneCenter = (p1 + p2)/2; - qreal angle = QLineF(p1, p2).angle(); - if (d->touchPoints.count() == 1) { - // If we only have one point then just move the center - if (d->id1 == touchPoint1.id()) - sceneCenter = d->sceneLastCenter + touchPoint1.scenePos() - d->lastPoint1; - else - sceneCenter = d->sceneLastCenter + touchPoint2.scenePos() - d->lastPoint2; - angle = d->pinchLastAngle; - } - d->id1 = touchPoint1.id(); - if (angle > 180) - angle -= 360; - if (!d->inPinch || d->initPinch) { - if (d->touchPoints.count() >= 2 - && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold - || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold - || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold - || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) { - d->initPinch = false; - d->sceneStartCenter = sceneCenter; - d->sceneLastCenter = sceneCenter; - d->pinchStartCenter = mapFromScene(sceneCenter); - d->pinchStartDist = dist; - d->pinchStartAngle = angle; - d->pinchLastScale = 1.0; - d->pinchLastAngle = angle; - d->pinchRotation = 0.0; - d->lastPoint1 = p1; - d->lastPoint2 = p2; - QSGPinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0); - pe.setStartCenter(d->pinchStartCenter); - pe.setPreviousCenter(d->pinchStartCenter); - pe.setPreviousAngle(d->pinchLastAngle); - pe.setPreviousScale(d->pinchLastScale); - pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); - pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); - pe.setPoint1(mapFromScene(d->lastPoint1)); - pe.setPoint2(mapFromScene(d->lastPoint2)); - pe.setPointCount(d->touchPoints.count()); - emit pinchStarted(&pe); - if (pe.accepted()) { - d->inPinch = true; - d->stealMouse = true; - QSGCanvas *c = canvas(); - if (c && c->mouseGrabberItem() != this) - grabMouse(); - setKeepMouseGrab(true); - if (d->pinch && d->pinch->target()) { - d->pinchStartPos = pinch()->target()->pos(); - d->pinchStartScale = d->pinch->target()->scale(); - d->pinchStartRotation = d->pinch->target()->rotation(); - d->pinch->setActive(true); - } - } else { - d->pinchRejected = true; - } - } - } else if (d->pinchStartDist > 0) { - qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale; - qreal da = d->pinchLastAngle - angle; - if (da > 180) - da -= 360; - else if (da < -180) - da += 360; - d->pinchRotation += da; - QPointF pinchCenter = mapFromScene(sceneCenter); - QSGPinchEvent pe(pinchCenter, scale, angle, d->pinchRotation); - pe.setStartCenter(d->pinchStartCenter); - pe.setPreviousCenter(mapFromScene(d->sceneLastCenter)); - pe.setPreviousAngle(d->pinchLastAngle); - pe.setPreviousScale(d->pinchLastScale); - pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); - pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); - pe.setPoint1(touchPoint1.pos()); - pe.setPoint2(touchPoint2.pos()); - pe.setPointCount(d->touchPoints.count()); - d->pinchLastScale = scale; - d->sceneLastCenter = sceneCenter; - d->pinchLastAngle = angle; - d->lastPoint1 = touchPoint1.scenePos(); - d->lastPoint2 = touchPoint2.scenePos(); - emit pinchUpdated(&pe); - if (d->pinch && d->pinch->target()) { - qreal s = d->pinchStartScale * scale; - s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale()); - pinch()->target()->setScale(s); - QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos; - if (pinch()->axis() & QSGPinch::XAxis) { - qreal x = pos.x(); - if (x < pinch()->xmin()) - x = pinch()->xmin(); - else if (x > pinch()->xmax()) - x = pinch()->xmax(); - pinch()->target()->setX(x); - } - if (pinch()->axis() & QSGPinch::YAxis) { - qreal y = pos.y(); - if (y < pinch()->ymin()) - y = pinch()->ymin(); - else if (y > pinch()->ymax()) - y = pinch()->ymax(); - pinch()->target()->setY(y); - } - if (d->pinchStartRotation >= pinch()->minimumRotation() - && d->pinchStartRotation <= pinch()->maximumRotation()) { - qreal r = d->pinchRotation + d->pinchStartRotation; - r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation()); - pinch()->target()->setRotation(r); - } - } - } - } -} - -void QSGPinchArea::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGPinchArea); - d->stealMouse = false; - if (!d->absorb) - QSGItem::mousePressEvent(event); - else { - setKeepMouseGrab(false); - event->setAccepted(true); - } -} - -void QSGPinchArea::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QSGPinchArea); - if (!d->absorb) { - QSGItem::mouseMoveEvent(event); - return; - } -} - -void QSGPinchArea::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGPinchArea); - d->stealMouse = false; - if (!d->absorb) { - QSGItem::mouseReleaseEvent(event); - } else { - QSGCanvas *c = canvas(); - if (c && c->mouseGrabberItem() == this) - ungrabMouse(); - setKeepMouseGrab(false); - } -} - -void QSGPinchArea::mouseUngrabEvent() -{ - setKeepMouseGrab(false); -} - -bool QSGPinchArea::sendMouseEvent(QMouseEvent *event) -{ - Q_D(QSGPinchArea); - QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); - - QSGCanvas *c = canvas(); - QSGItem *grabber = c ? c->mouseGrabberItem() : 0; - bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { - QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - mouseEvent.setAccepted(false); - - switch (mouseEvent.type()) { - case QEvent::MouseMove: - mouseMoveEvent(&mouseEvent); - break; - case QEvent::MouseButtonPress: - mousePressEvent(&mouseEvent); - break; - case QEvent::MouseButtonRelease: - mouseReleaseEvent(&mouseEvent); - break; - default: - break; - } - grabber = c->mouseGrabberItem(); - if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) - grabMouse(); - - return stealThisEvent; - } - if (event->type() == QEvent::MouseButtonRelease) { - d->stealMouse = false; - if (c && c->mouseGrabberItem() == this) - ungrabMouse(); - setKeepMouseGrab(false); - } - return false; -} - -bool QSGPinchArea::childMouseEventFilter(QSGItem *i, QEvent *e) -{ - Q_D(QSGPinchArea); - if (!d->absorb || !isVisible()) - return QSGItem::childMouseEventFilter(i, e); - switch (e->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - return sendMouseEvent(static_cast(e)); - break; - case QEvent::TouchBegin: - case QEvent::TouchUpdate: { - QTouchEvent *touch = static_cast(e); - d->touchPoints.clear(); - for (int i = 0; i < touch->touchPoints().count(); ++i) - if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) - d->touchPoints << touch->touchPoints().at(i); - updatePinch(); - } - return d->inPinch; - case QEvent::TouchEnd: - d->touchPoints.clear(); - updatePinch(); - break; - default: - break; - } - - return QSGItem::childMouseEventFilter(i, e); -} - -void QSGPinchArea::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) -{ - QSGItem::geometryChanged(newGeometry, oldGeometry); -} - -void QSGPinchArea::itemChange(ItemChange change, const ItemChangeData &value) -{ - QSGItem::itemChange(change, value); -} - -QSGPinch *QSGPinchArea::pinch() -{ - Q_D(QSGPinchArea); - if (!d->pinch) - d->pinch = new QSGPinch; - return d->pinch; -} - - -QT_END_NAMESPACE - diff --git a/src/declarative/items/qsgpincharea_p.h b/src/declarative/items/qsgpincharea_p.h deleted file mode 100644 index f27bddb193..0000000000 --- a/src/declarative/items/qsgpincharea_p.h +++ /dev/null @@ -1,315 +0,0 @@ -// Commit: f707672eb4c51ea82fbd98e1da16ece61a74c690 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtSG module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGPINCHAREA_H -#define QSGPINCHAREA_H - -#include "qsgitem.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class Q_AUTOTEST_EXPORT QSGPinch : public QObject -{ - Q_OBJECT - - Q_ENUMS(Axis) - Q_PROPERTY(QSGItem *target READ target WRITE setTarget RESET resetTarget) - Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged) - Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged) - Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged) - Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged) - Q_PROPERTY(Axis dragAxis READ axis WRITE setAxis NOTIFY dragAxisChanged) - Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) - Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged) - Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged) - Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) - Q_PROPERTY(bool active READ active NOTIFY activeChanged) - -public: - QSGPinch(); - - QSGItem *target() const { return m_target; } - void setTarget(QSGItem *target) { - if (target == m_target) - return; - m_target = target; - emit targetChanged(); - } - void resetTarget() { - if (!m_target) - return; - m_target = 0; - emit targetChanged(); - } - - qreal minimumScale() const { return m_minScale; } - void setMinimumScale(qreal s) { - if (s == m_minScale) - return; - m_minScale = s; - emit minimumScaleChanged(); - } - qreal maximumScale() const { return m_maxScale; } - void setMaximumScale(qreal s) { - if (s == m_maxScale) - return; - m_maxScale = s; - emit maximumScaleChanged(); - } - - qreal minimumRotation() const { return m_minRotation; } - void setMinimumRotation(qreal r) { - if (r == m_minRotation) - return; - m_minRotation = r; - emit minimumRotationChanged(); - } - qreal maximumRotation() const { return m_maxRotation; } - void setMaximumRotation(qreal r) { - if (r == m_maxRotation) - return; - m_maxRotation = r; - emit maximumRotationChanged(); - } - - enum Axis { NoDrag=0x00, XAxis=0x01, YAxis=0x02, XandYAxis=0x03 }; - Axis axis() const { return m_axis; } - void setAxis(Axis a) { - if (a == m_axis) - return; - m_axis = a; - emit dragAxisChanged(); - } - - qreal xmin() const { return m_xmin; } - void setXmin(qreal x) { - if (x == m_xmin) - return; - m_xmin = x; - emit minimumXChanged(); - } - qreal xmax() const { return m_xmax; } - void setXmax(qreal x) { - if (x == m_xmax) - return; - m_xmax = x; - emit maximumXChanged(); - } - qreal ymin() const { return m_ymin; } - void setYmin(qreal y) { - if (y == m_ymin) - return; - m_ymin = y; - emit minimumYChanged(); - } - qreal ymax() const { return m_ymax; } - void setYmax(qreal y) { - if (y == m_ymax) - return; - m_ymax = y; - emit maximumYChanged(); - } - - bool active() const { return m_active; } - void setActive(bool a) { - if (a == m_active) - return; - m_active = a; - emit activeChanged(); - } - -signals: - void targetChanged(); - void minimumScaleChanged(); - void maximumScaleChanged(); - void minimumRotationChanged(); - void maximumRotationChanged(); - void dragAxisChanged(); - void minimumXChanged(); - void maximumXChanged(); - void minimumYChanged(); - void maximumYChanged(); - void activeChanged(); - -private: - QSGItem *m_target; - qreal m_minScale; - qreal m_maxScale; - qreal m_minRotation; - qreal m_maxRotation; - Axis m_axis; - qreal m_xmin; - qreal m_xmax; - qreal m_ymin; - qreal m_ymax; - bool m_active; -}; - -class Q_AUTOTEST_EXPORT QSGPinchEvent : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QPointF center READ center) - Q_PROPERTY(QPointF startCenter READ startCenter) - Q_PROPERTY(QPointF previousCenter READ previousCenter) - Q_PROPERTY(qreal scale READ scale) - Q_PROPERTY(qreal previousScale READ previousScale) - Q_PROPERTY(qreal angle READ angle) - Q_PROPERTY(qreal previousAngle READ previousAngle) - Q_PROPERTY(qreal rotation READ rotation) - Q_PROPERTY(QPointF point1 READ point1) - Q_PROPERTY(QPointF startPoint1 READ startPoint1) - Q_PROPERTY(QPointF point2 READ point2) - Q_PROPERTY(QPointF startPoint2 READ startPoint2) - Q_PROPERTY(int pointCount READ pointCount) - Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) - -public: - QSGPinchEvent(QPointF c, qreal s, qreal a, qreal r) - : QObject(), m_center(c), m_scale(s), m_angle(a), m_rotation(r) - , m_pointCount(0), m_accepted(true) {} - - QPointF center() const { return m_center; } - QPointF startCenter() const { return m_startCenter; } - void setStartCenter(QPointF c) { m_startCenter = c; } - QPointF previousCenter() const { return m_lastCenter; } - void setPreviousCenter(QPointF c) { m_lastCenter = c; } - qreal scale() const { return m_scale; } - qreal previousScale() const { return m_lastScale; } - void setPreviousScale(qreal s) { m_lastScale = s; } - qreal angle() const { return m_angle; } - qreal previousAngle() const { return m_lastAngle; } - void setPreviousAngle(qreal a) { m_lastAngle = a; } - qreal rotation() const { return m_rotation; } - QPointF point1() const { return m_point1; } - void setPoint1(QPointF p) { m_point1 = p; } - QPointF startPoint1() const { return m_startPoint1; } - void setStartPoint1(QPointF p) { m_startPoint1 = p; } - QPointF point2() const { return m_point2; } - void setPoint2(QPointF p) { m_point2 = p; } - QPointF startPoint2() const { return m_startPoint2; } - void setStartPoint2(QPointF p) { m_startPoint2 = p; } - int pointCount() const { return m_pointCount; } - void setPointCount(int count) { m_pointCount = count; } - - bool accepted() const { return m_accepted; } - void setAccepted(bool a) { m_accepted = a; } - -private: - QPointF m_center; - QPointF m_startCenter; - QPointF m_lastCenter; - qreal m_scale; - qreal m_lastScale; - qreal m_angle; - qreal m_lastAngle; - qreal m_rotation; - QPointF m_point1; - QPointF m_point2; - QPointF m_startPoint1; - QPointF m_startPoint2; - int m_pointCount; - bool m_accepted; -}; - - -class QSGMouseEvent; -class QSGPinchAreaPrivate; -class Q_AUTOTEST_EXPORT QSGPinchArea : public QSGItem -{ - Q_OBJECT - - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) - Q_PROPERTY(QSGPinch *pinch READ pinch CONSTANT) - -public: - QSGPinchArea(QSGItem *parent=0); - ~QSGPinchArea(); - - bool isEnabled() const; - void setEnabled(bool); - - QSGPinch *pinch(); - -Q_SIGNALS: - void enabledChanged(); - void pinchStarted(QSGPinchEvent *pinch); - void pinchUpdated(QSGPinchEvent *pinch); - void pinchFinished(QSGPinchEvent *pinch); - -protected: - virtual void mousePressEvent(QMouseEvent *event); - virtual void mouseReleaseEvent(QMouseEvent *event); - virtual void mouseMoveEvent(QMouseEvent *event); - virtual void mouseUngrabEvent(); - virtual bool sendMouseEvent(QMouseEvent *event); - virtual bool childMouseEventFilter(QSGItem *i, QEvent *e); - virtual void touchEvent(QTouchEvent *event); - - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - virtual void itemChange(ItemChange change, const ItemChangeData& value); - -private: - void updatePinch(); - void handlePress(); - void handleRelease(); - -private: - Q_DISABLE_COPY(QSGPinchArea) - Q_DECLARE_PRIVATE(QSGPinchArea) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGPinch) -QML_DECLARE_TYPE(QSGPinchEvent) -QML_DECLARE_TYPE(QSGPinchArea) - -QT_END_HEADER - -#endif // QSGPINCHAREA_H - diff --git a/src/declarative/items/qsgpincharea_p_p.h b/src/declarative/items/qsgpincharea_p_p.h deleted file mode 100644 index 9cf928b828..0000000000 --- a/src/declarative/items/qsgpincharea_p_p.h +++ /dev/null @@ -1,116 +0,0 @@ -// Commit: f707672eb4c51ea82fbd98e1da16ece61a74c690 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtSG module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGPINCHAREA_P_H -#define QSGPINCHAREA_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include "qsgitem_p.h" -#include "qsgpincharea_p.h" - -QT_BEGIN_NAMESPACE - -class QSGPinch; -class QSGPinchAreaPrivate : public QSGItemPrivate -{ - Q_DECLARE_PUBLIC(QSGPinchArea) -public: - QSGPinchAreaPrivate() - : absorb(true), stealMouse(false), inPinch(false) - , pinchRejected(false), pinchActivated(false), initPinch(false) - , pinch(0), pinchStartDist(0), pinchStartScale(1.0) - , pinchLastScale(1.0), pinchStartRotation(0.0), pinchStartAngle(0.0) - , pinchLastAngle(0.0), pinchRotation(0.0) - { - } - - ~QSGPinchAreaPrivate(); - - void init() - { - Q_Q(QSGPinchArea); - q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFiltersChildMouseEvents(true); - } - - bool absorb : 1; - bool stealMouse : 1; - bool inPinch : 1; - bool pinchRejected : 1; - bool pinchActivated : 1; - bool initPinch : 1; - QSGPinch *pinch; - QPointF sceneStartPoint1; - QPointF sceneStartPoint2; - QPointF lastPoint1; - QPointF lastPoint2; - qreal pinchStartDist; - qreal pinchStartScale; - qreal pinchLastScale; - qreal pinchStartRotation; - qreal pinchStartAngle; - qreal pinchLastAngle; - qreal pinchRotation; - QPointF sceneStartCenter; - QPointF pinchStartCenter; - QPointF sceneLastCenter; - QPointF pinchStartPos; - QList touchPoints; - int id1; -}; - -QT_END_NAMESPACE - -#endif // QSGPINCHAREA_P_H - diff --git a/src/declarative/items/qsgpositioners.cpp b/src/declarative/items/qsgpositioners.cpp deleted file mode 100644 index eaca2101e2..0000000000 --- a/src/declarative/items/qsgpositioners.cpp +++ /dev/null @@ -1,1533 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgpositioners_p.h" -#include "qsgpositioners_p_p.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -static const QSGItemPrivate::ChangeTypes watchedChanges - = QSGItemPrivate::Geometry - | QSGItemPrivate::SiblingOrder - | QSGItemPrivate::Visibility - | QSGItemPrivate::Destroyed; - -void QSGBasePositionerPrivate::watchChanges(QSGItem *other) -{ - QSGItemPrivate *otherPrivate = QSGItemPrivate::get(other); - otherPrivate->addItemChangeListener(this, watchedChanges); -} - -void QSGBasePositionerPrivate::unwatchChanges(QSGItem* other) -{ - QSGItemPrivate *otherPrivate = QSGItemPrivate::get(other); - otherPrivate->removeItemChangeListener(this, watchedChanges); -} - -QSGBasePositioner::QSGBasePositioner(PositionerType at, QSGItem *parent) - : QSGImplicitSizeItem(*(new QSGBasePositionerPrivate), parent) -{ - Q_D(QSGBasePositioner); - d->init(at); -} -/*! - \internal - \class QSGBasePositioner - \brief The QSGBasePositioner class provides a base for QSGGraphics layouts. - - To create a QSGGraphics Positioner, simply subclass QSGBasePositioner and implement - doLayout(), which is automatically called when the layout might need - updating. In doLayout() use the setX and setY functions from QSGBasePositioner, and the - base class will apply the positions along with the appropriate transitions. The items to - position are provided in order as the protected member positionedItems. - - You also need to set a PositionerType, to declare whether you are positioning the x, y or both - for the child items. Depending on the chosen type, only x or y changes will be applied. - - Note that the subclass is responsible for adding the spacing in between items. - - Positioning is usually delayed until before a frame is rendered, to batch multiple repositioning - changes into one calculation. -*/ - -QSGBasePositioner::QSGBasePositioner(QSGBasePositionerPrivate &dd, PositionerType at, QSGItem *parent) - : QSGImplicitSizeItem(dd, parent) -{ - Q_D(QSGBasePositioner); - d->init(at); -} - -QSGBasePositioner::~QSGBasePositioner() -{ - Q_D(QSGBasePositioner); - for (int i = 0; i < positionedItems.count(); ++i) - d->unwatchChanges(positionedItems.at(i).item); - positionedItems.clear(); -} - -void QSGBasePositioner::updatePolish() -{ - Q_D(QSGBasePositioner); - if (d->positioningDirty) - prePositioning(); -} - -int QSGBasePositioner::spacing() const -{ - Q_D(const QSGBasePositioner); - return d->spacing; -} - -void QSGBasePositioner::setSpacing(int s) -{ - Q_D(QSGBasePositioner); - if (s==d->spacing) - return; - d->spacing = s; - d->setPositioningDirty(); - emit spacingChanged(); -} - -QDeclarativeTransition *QSGBasePositioner::move() const -{ - Q_D(const QSGBasePositioner); - return d->moveTransition; -} - -void QSGBasePositioner::setMove(QDeclarativeTransition *mt) -{ - Q_D(QSGBasePositioner); - if (mt == d->moveTransition) - return; - d->moveTransition = mt; - emit moveChanged(); -} - -QDeclarativeTransition *QSGBasePositioner::add() const -{ - Q_D(const QSGBasePositioner); - return d->addTransition; -} - -void QSGBasePositioner::setAdd(QDeclarativeTransition *add) -{ - Q_D(QSGBasePositioner); - if (add == d->addTransition) - return; - - d->addTransition = add; - emit addChanged(); -} - -void QSGBasePositioner::componentComplete() -{ - QSGItem::componentComplete(); - positionedItems.reserve(childItems().count()); - prePositioning(); - reportConflictingAnchors(); -} - -void QSGBasePositioner::itemChange(ItemChange change, const ItemChangeData &value) -{ - Q_D(QSGBasePositioner); - if (change == ItemChildAddedChange){ - d->setPositioningDirty(); - } else if (change == ItemChildRemovedChange) { - QSGItem *child = value.item; - QSGBasePositioner::PositionedItem posItem(child); - int idx = positionedItems.find(posItem); - if (idx >= 0) { - d->unwatchChanges(child); - positionedItems.remove(idx); - } - d->setPositioningDirty(); - } - - QSGItem::itemChange(change, value); -} - -void QSGBasePositioner::prePositioning() -{ - Q_D(QSGBasePositioner); - if (!isComponentComplete()) - return; - - if (d->doingPositioning) - return; - - d->positioningDirty = false; - d->doingPositioning = true; - //Need to order children by creation order modified by stacking order - QList children = childItems(); - - QPODVector oldItems; - positionedItems.copyAndClear(oldItems); - for (int ii = 0; ii < children.count(); ++ii) { - QSGItem *child = children.at(ii); - QSGItemPrivate *childPrivate = QSGItemPrivate::get(child); - PositionedItem *item = 0; - PositionedItem posItem(child); - int wIdx = oldItems.find(posItem); - if (wIdx < 0) { - d->watchChanges(child); - positionedItems.append(posItem); - item = &positionedItems[positionedItems.count()-1]; - item->isNew = true; - if (!childPrivate->explicitVisible || !child->width() || !child->height()) - item->isVisible = false; - } else { - item = &oldItems[wIdx]; - // Items are only omitted from positioning if they are explicitly hidden - // i.e. their positioning is not affected if an ancestor is hidden. - if (!childPrivate->explicitVisible || !child->width() || !child->height()) { - item->isVisible = false; - } else if (!item->isVisible) { - item->isVisible = true; - item->isNew = true; - } else { - item->isNew = false; - } - positionedItems.append(*item); - } - } - QSizeF contentSize(0,0); - doPositioning(&contentSize); - updateAttachedProperties(); - if (!d->addActions.isEmpty() || !d->moveActions.isEmpty()) - finishApplyTransitions(); - d->doingPositioning = false; - //Set implicit size to the size of its children - setImplicitHeight(contentSize.height()); - setImplicitWidth(contentSize.width()); -} - -void QSGBasePositioner::positionX(int x, const PositionedItem &target) -{ - Q_D(QSGBasePositioner); - if (d->type == Horizontal || d->type == Both) { - if (target.isNew) { - if (!d->addTransition || !d->addTransition->enabled()) - target.item->setX(x); - else - d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); - } else if (x != target.item->x()) { - if (!d->moveTransition || !d->moveTransition->enabled()) - target.item->setX(x); - else - d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); - } - } -} - -void QSGBasePositioner::positionY(int y, const PositionedItem &target) -{ - Q_D(QSGBasePositioner); - if (d->type == Vertical || d->type == Both) { - if (target.isNew) { - if (!d->addTransition || !d->addTransition->enabled()) - target.item->setY(y); - else - d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); - } else if (y != target.item->y()) { - if (!d->moveTransition || !d->moveTransition->enabled()) - target.item->setY(y); - else - d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); - } - } -} - -void QSGBasePositioner::finishApplyTransitions() -{ - Q_D(QSGBasePositioner); - // Note that if a transition is not set the transition manager will - // apply the changes directly, in the case add/move aren't set - d->addTransitionManager.transition(d->addActions, d->addTransition); - d->moveTransitionManager.transition(d->moveActions, d->moveTransition); - d->addActions.clear(); - d->moveActions.clear(); -} - -QSGPositionerAttached *QSGBasePositioner::qmlAttachedProperties(QObject *obj) -{ - return new QSGPositionerAttached(obj); -} - -void QSGBasePositioner::updateAttachedProperties(QSGPositionerAttached *specificProperty, QSGItem *specificPropertyOwner) const -{ - // If this function is deemed too expensive or shows up in profiles, it could - // be changed to run only when there are attached properties present. This - // could be a flag in the positioner that is set by the attached property - // constructor. - QSGPositionerAttached *prevLastProperty = 0; - QSGPositionerAttached *lastProperty = 0; - - int visibleItemIndex = 0; - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (!child.item) - continue; - - QSGPositionerAttached *property = 0; - - if (specificProperty) { - if (specificPropertyOwner == child.item) { - property = specificProperty; - } - } else { - property = static_cast(qmlAttachedPropertiesObject(child.item, false)); - } - - if (child.isVisible) { - if (property) { - property->setIndex(visibleItemIndex); - property->setIsFirstItem(visibleItemIndex == 0); - - if (property->isLastItem()) - prevLastProperty = property; - } - - lastProperty = property; - ++visibleItemIndex; - } else if (property) { - property->setIndex(-1); - property->setIsFirstItem(false); - property->setIsLastItem(false); - } - } - - if (prevLastProperty && prevLastProperty != lastProperty) - prevLastProperty->setIsLastItem(false); - if (lastProperty) - lastProperty->setIsLastItem(true); -} - -/*! - \qmlclass Positioner QSGPositionerAttached - \inqmlmodule QtQuick 2 - \ingroup qml-positioning-elements - \brief The Positioner type provides attached properties that contain details on where an item exists in a positioner. - - Positioner items (such as Column, Row, Flow and Grid) provide automatic layout - for child items. Attaching this property allows a child item to determine - where it exists within the positioner. -*/ - -QSGPositionerAttached::QSGPositionerAttached(QObject *parent) : QObject(parent), m_index(-1), m_isFirstItem(false), m_isLastItem(false) -{ - QSGItem *attachedItem = qobject_cast(parent); - if (attachedItem) { - QSGBasePositioner *positioner = qobject_cast(attachedItem->parent()); - if (positioner) { - positioner->updateAttachedProperties(this, attachedItem); - } - } -} - -/*! - \qmlattachedproperty Item QtQuick2::Positioner::index - - This property allows the item to determine - its index within the positioner. -*/ -void QSGPositionerAttached::setIndex(int index) -{ - if (m_index == index) - return; - m_index = index; - emit indexChanged(); -} - -/*! - \qmlattachedproperty Item QtQuick2::Positioner::isFirstItem - \qmlattachedproperty Item QtQuick2::Positioner::isLastItem - - These properties allow the item to determine if it - is the first or last item in the positioner, respectively. -*/ -void QSGPositionerAttached::setIsFirstItem(bool isFirstItem) -{ - if (m_isFirstItem == isFirstItem) - return; - m_isFirstItem = isFirstItem; - emit isFirstItemChanged(); -} - -void QSGPositionerAttached::setIsLastItem(bool isLastItem) -{ - if (m_isLastItem == isLastItem) - return; - m_isLastItem = isLastItem; - emit isLastItemChanged(); -} - -/*! - \qmlclass Column QSGColumn - \inqmlmodule QtQuick 2 - \ingroup qml-positioning-elements - \brief The Column item arranges its children vertically. - \inherits Item - - The Column item positions its child items so that they are vertically - aligned and not overlapping. - - Spacing between items can be added using the \l spacing property. - Transitions can be used for cases where items managed by a Column are - added or moved. These are stored in the \l add and \l move properties - respectively. - - See \l{Using QML Positioner and Repeater Items} for more details about this item and other - related items. - - \section1 Example Usage - - The following example positions differently shaped rectangles using a Column - item. - - \image verticalpositioner_example.png - - \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document - - \section1 Using Transitions - - Transitions can be used to animate items that are added to, moved within, - or removed from a Column item. The \l add and \l move properties can be set to - the transitions that will be applied when items are added to, removed from, - or re-positioned within a Column item. - - The use of transitions with positioners is described in more detail in the - \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML - Positioner and Repeater Items} document. - - \image verticalpositioner_transition.gif - - \qml - Column { - spacing: 2 - add: Transition { - // Define an animation for adding a new item... - } - move: Transition { - // Define an animation for moving items within the column... - } - // ... - } - \endqml - - \section1 Limitations - - Note that the positioner assumes that the x and y positions of its children - will not change. If you manually change the x or y properties in script, bind - the x or y properties, use anchors on a child of a positioner, or have the - height of a child depend on the position of a child, then the - positioner may exhibit strange behavior. If you need to perform any of these - actions, consider positioning the items without the use of a Column. - - Items with a width or height of 0 will not be positioned. - - Positioning is batched and syncronized with painting to reduce the number of - calculations needed. This means that positioners may not reposition items immediately - when changes occur, but it will have moved by the next frame. - - \sa Row, Grid, Flow, Positioner, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty Transition QtQuick2::Column::add - - This property holds the transition to be applied when adding an - item to the positioner. The transition will only be applied to the - added item(s). Positioner transitions will only affect the - position (x, y) of items. - - For a positioner, adding an item can mean that either the object - has been created or reparented, and thus is now a child or the - positioner, or that the object has had its opacity increased from - zero, and thus is now visible. - - \sa move -*/ -/*! - \qmlproperty Transition QtQuick2::Column::move - - This property holds the transition to apply when moving an item - within the positioner. Positioner transitions will only affect - the position (x, y) of items. - - This transition can be performed when other items are added or removed - from the positioner, or when items resize themselves. - - \image positioner-move.gif - - \qml - Column { - move: Transition { - NumberAnimation { - properties: "y" - duration: 1000 - } - } - } - \endqml - - \sa add, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty int QtQuick2::Column::spacing - - The spacing is the amount in pixels left empty between adjacent - items. The default spacing is 0. - - \sa Grid::spacing -*/ -QSGColumn::QSGColumn(QSGItem *parent) -: QSGBasePositioner(Vertical, parent) -{ -} - -void QSGColumn::doPositioning(QSizeF *contentSize) -{ - int voffset = 0; - - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (!child.item || !child.isVisible) - continue; - - if (child.item->y() != voffset) - positionY(voffset, child); - - contentSize->setWidth(qMax(contentSize->width(), child.item->width())); - - voffset += child.item->height(); - voffset += spacing(); - } - - if (voffset != 0)//If we positioned any items, undo the spacing from the last item - voffset -= spacing(); - contentSize->setHeight(voffset); -} - -void QSGColumn::reportConflictingAnchors() -{ - QSGBasePositionerPrivate *d = static_cast(QSGBasePositionerPrivate::get(this)); - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (child.item) { - QSGAnchors *anchors = QSGItemPrivate::get(static_cast(child.item))->_anchors; - if (anchors) { - QSGAnchors::Anchors usedAnchors = anchors->usedAnchors(); - if (usedAnchors & QSGAnchors::TopAnchor || - usedAnchors & QSGAnchors::BottomAnchor || - usedAnchors & QSGAnchors::VCenterAnchor || - anchors->fill() || anchors->centerIn()) { - d->anchorConflict = true; - break; - } - } - } - } - if (d->anchorConflict) { - qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"; - } -} -/*! - \qmlclass Row QSGRow - \inqmlmodule QtQuick 2 - \ingroup qml-positioning-elements - \brief The Row item arranges its children horizontally. - \inherits Item - - The Row item positions its child items so that they are horizontally - aligned and not overlapping. - - Use \l spacing to set the spacing between items in a Row, and use the - \l add and \l move properties to set the transitions that should be applied - when items are added to, removed from, or re-positioned within the Row. - - See \l{Using QML Positioner and Repeater Items} for more details about this item and other - related items. - - \section1 Example Usage - - The following example lays out differently shaped rectangles using a Row. - - \image horizontalpositioner_example.png - - \snippet doc/src/snippets/declarative/row/row.qml document - - \section1 Using Transitions - - Transitions can be used to animate items that are added to, moved within, - or removed from a Grid item. The \l add and \l move properties can be set to - the transitions that will be applied when items are added to, removed from, - or re-positioned within a Row item. - - \section1 Limitations - - Note that the positioner assumes that the x and y positions of its children - will not change. If you manually change the x or y properties in script, bind - the x or y properties, use anchors on a child of a positioner, or have the - width of a child depend on the position of a child, then the - positioner may exhibit strange behaviour. If you need to perform any of these - actions, consider positioning the items without the use of a Row. - - Items with a width or height of 0 will not be positioned. - - Positioning is batched and syncronized with painting to reduce the number of - calculations needed. This means that positioners may not reposition items immediately - when changes occur, but it will have moved by the next frame. - - \sa Column, Grid, Flow, Positioner, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty Transition QtQuick2::Row::add - - This property holds the transition to be applied when adding an - item to the positioner. The transition will only be applied to the - added item(s). Positioner transitions will only affect the - position (x, y) of items. - - For a positioner, adding an item can mean that either the object - has been created or reparented, and thus is now a child or the - positioner, or that the object has had its opacity increased from - zero, and thus is now visible. - - \sa move -*/ -/*! - \qmlproperty Transition QtQuick2::Row::move - - This property holds the transition to be applied when moving an - item within the positioner. Positioner transitions will only affect - the position (x, y) of items. - - This transition can be performed when other items are added or removed - from the positioner, or when items resize themselves. - - \qml - Row { - id: positioner - move: Transition { - NumberAnimation { - properties: "x" - duration: 1000 - } - } - } - \endqml - - \sa add, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty int QtQuick2::Row::spacing - - The spacing is the amount in pixels left empty between adjacent - items. The default spacing is 0. - - \sa Grid::spacing -*/ - -QSGRow::QSGRow(QSGItem *parent) -: QSGBasePositioner(Horizontal, parent) -{ -} -/*! - \qmlproperty enumeration QtQuick2::Row::layoutDirection - - This property holds the layoutDirection of the row. - - Possible values: - - \list - \o Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set, - the left anchor remains to the left of the row. - \o Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set, - the right anchor remains to the right of the row. - \endlist - - \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} -*/ - -Qt::LayoutDirection QSGRow::layoutDirection() const -{ - return QSGBasePositionerPrivate::getLayoutDirection(this); -} - -void QSGRow::setLayoutDirection(Qt::LayoutDirection layoutDirection) -{ - QSGBasePositionerPrivate *d = static_cast(QSGBasePositionerPrivate::get(this)); - if (d->layoutDirection != layoutDirection) { - d->layoutDirection = layoutDirection; - // For RTL layout the positioning changes when the width changes. - if (d->layoutDirection == Qt::RightToLeft) - d->addItemChangeListener(d, QSGItemPrivate::Geometry); - else - d->removeItemChangeListener(d, QSGItemPrivate::Geometry); - prePositioning(); - emit layoutDirectionChanged(); - emit effectiveLayoutDirectionChanged(); - } -} -/*! - \qmlproperty enumeration QtQuick2::Row::effectiveLayoutDirection - This property holds the effective layout direction of the row positioner. - - When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, - the visual layout direction of the row positioner will be mirrored. However, the - property \l {Row::layoutDirection}{layoutDirection} will remain unchanged. - - \sa Row::layoutDirection, {LayoutMirroring}{LayoutMirroring} -*/ - -Qt::LayoutDirection QSGRow::effectiveLayoutDirection() const -{ - return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this); -} - -void QSGRow::doPositioning(QSizeF *contentSize) -{ - QSGBasePositionerPrivate *d = static_cast(QSGBasePositionerPrivate::get(this)); - int hoffset = 0; - - QList hoffsets; - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (!child.item || !child.isVisible) - continue; - - if (d->isLeftToRight()) { - if (child.item->x() != hoffset) - positionX(hoffset, child); - } else { - hoffsets << hoffset; - } - - contentSize->setHeight(qMax(contentSize->height(), child.item->height())); - - hoffset += child.item->width(); - hoffset += spacing(); - } - - if (hoffset != 0)//If we positioned any items, undo the extra spacing from the last item - hoffset -= spacing(); - contentSize->setWidth(hoffset); - - if (d->isLeftToRight()) - return; - - //Right to Left layout - int end = 0; - if (!widthValid()) - end = contentSize->width(); - else - end = width(); - - int acc = 0; - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (!child.item || !child.isVisible) - continue; - hoffset = end - hoffsets[acc++] - child.item->width(); - if (child.item->x() != hoffset) - positionX(hoffset, child); - } -} - -void QSGRow::reportConflictingAnchors() -{ - QSGBasePositionerPrivate *d = static_cast(QSGBasePositionerPrivate::get(this)); - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (child.item) { - QSGAnchors *anchors = QSGItemPrivate::get(static_cast(child.item))->_anchors; - if (anchors) { - QSGAnchors::Anchors usedAnchors = anchors->usedAnchors(); - if (usedAnchors & QSGAnchors::LeftAnchor || - usedAnchors & QSGAnchors::RightAnchor || - usedAnchors & QSGAnchors::HCenterAnchor || - anchors->fill() || anchors->centerIn()) { - d->anchorConflict = true; - break; - } - } - } - } - if (d->anchorConflict) - qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"; -} - -/*! - \qmlclass Grid QSGGrid - \inqmlmodule QtQuick 2 - \ingroup qml-positioning-elements - \brief The Grid item positions its children in a grid. - \inherits Item - - The Grid item positions its child items so that they are - aligned in a grid and are not overlapping. - - The grid positioner calculates a grid of rectangular cells of sufficient - size to hold all items, placing the items in the cells, from left to right - and top to bottom. Each item is positioned in the top-left corner of its - cell with position (0, 0). - - A Grid defaults to four columns, and as many rows as are necessary to - fit all child items. The number of rows and columns can be constrained - by setting the \l rows and \l columns properties. - - Spacing can be added between child items by setting the \l spacing - property. The amount of spacing applied will be the same in the - horizontal and vertical directions. - - See \l{Using QML Positioner and Repeater Items} for more details about this item and other - related items. - - \section1 Example Usage - - The following example demonstrates this. - - \image gridLayout_example.png - - \snippet doc/src/snippets/declarative/grid/grid.qml document - - \section1 Using Transitions - - Transitions can be used to animate items that are added to, moved within, - or removed from a Grid item. The \l add and \l move properties can be set to - the transitions that will be applied when items are added to, removed from, - or re-positioned within a Grid item. - - \section1 Limitations - - Note that the positioner assumes that the x and y positions of its children - will not change. If you manually change the x or y properties in script, bind - the x or y properties, use anchors on a child of a positioner, or have the - width or height of a child depend on the position of a child, then the - positioner may exhibit strange behaviour. If you need to perform any of these - actions, consider positioning the items without the use of a Grid. - - Items with a width or height of 0 will not be positioned. - - Positioning is batched and syncronized with painting to reduce the number of - calculations needed. This means that positioners may not reposition items immediately - when changes occur, but it will have moved by the next frame. - - \sa Flow, Row, Column, Positioner, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty Transition QtQuick2::Grid::add - - This property holds the transition to be applied when adding an - item to the positioner. The transition will only be applied to the - added item(s). Positioner transitions will only affect the - position (x, y) of items. - - For a positioner, adding an item can mean that either the object - has been created or reparented, and thus is now a child or the - positioner, or that the object has had its opacity increased from - zero, and thus is now visible. - - \sa move -*/ -/*! - \qmlproperty Transition QtQuick2::Grid::move - - This property holds the transition to be applied when moving an - item within the positioner. Positioner transitions will only affect - the position (x, y) of items. - - This transition can be performed when other items are added or removed - from the positioner, or when items resize themselves. - - \qml - Grid { - move: Transition { - NumberAnimation { - properties: "x,y" - duration: 1000 - } - } - } - \endqml - - \sa add, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty int QtQuick2::Grid::spacing - - The spacing is the amount in pixels left empty between adjacent - items. The default spacing is 0. - - The below example places a Grid containing a red, a blue and a - green rectangle on a gray background. The area the grid positioner - occupies is colored white. The positioner on the left has the - no spacing (the default), and the positioner on the right has - a spacing of 6. - - \inlineimage qml-grid-no-spacing.png - \inlineimage qml-grid-spacing.png - - \sa rows, columns -*/ -QSGGrid::QSGGrid(QSGItem *parent) : - QSGBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_rowSpacing(-1), m_columnSpacing(-1), m_flow(LeftToRight) -{ -} - -/*! - \qmlproperty int QtQuick2::Grid::columns - - This property holds the number of columns in the grid. The default - number of columns is 4. - - If the grid does not have enough items to fill the specified - number of columns, some columns will be of zero width. -*/ - -/*! - \qmlproperty int QtQuick2::Grid::rows - This property holds the number of rows in the grid. - - If the grid does not have enough items to fill the specified - number of rows, some rows will be of zero width. -*/ - -void QSGGrid::setColumns(const int columns) -{ - if (columns == m_columns) - return; - m_columns = columns; - prePositioning(); - emit columnsChanged(); -} - -void QSGGrid::setRows(const int rows) -{ - if (rows == m_rows) - return; - m_rows = rows; - prePositioning(); - emit rowsChanged(); -} - -/*! - \qmlproperty enumeration QtQuick2::Grid::flow - This property holds the flow of the layout. - - Possible values are: - - \list - \o Grid.LeftToRight (default) - Items are positioned next to - each other in the \l layoutDirection, then wrapped to the next line. - \o Grid.TopToBottom - Items are positioned next to each - other from top to bottom, then wrapped to the next column. - \endlist -*/ -QSGGrid::Flow QSGGrid::flow() const -{ - return m_flow; -} - -void QSGGrid::setFlow(Flow flow) -{ - if (m_flow != flow) { - m_flow = flow; - prePositioning(); - emit flowChanged(); - } -} - -/*! - \qmlproperty int QtQuick2::Grid::rowSpacing - - This property holds the spacing in pixels between rows. - - \sa columnSpacing - \since QtQuick2.0 -*/ -void QSGGrid::setRowSpacing(const int rowSpacing) -{ - if (rowSpacing == m_rowSpacing) - return; - m_rowSpacing = rowSpacing; - prePositioning(); - emit rowSpacingChanged(); -} - -/*! - \qmlproperty int QtQuick2::Grid::columnSpacing - - This property holds the spacing in pixels between columns. - - \sa rowSpacing - \since QtQuick2.0 -*/ -void QSGGrid::setColumnSpacing(const int columnSpacing) -{ - if (columnSpacing == m_columnSpacing) - return; - m_columnSpacing = columnSpacing; - prePositioning(); - emit columnSpacingChanged(); -} - -/*! - \qmlproperty enumeration QtQuick2::Grid::layoutDirection - - This property holds the layout direction of the layout. - - Possible values are: - - \list - \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, - and left to right. The flow direction is dependent on the - \l Grid::flow property. - \o Qt.RightToLeft - Items are positioned from the top to bottom, - and right to left. The flow direction is dependent on the - \l Grid::flow property. - \endlist - - \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} -*/ -Qt::LayoutDirection QSGGrid::layoutDirection() const -{ - return QSGBasePositionerPrivate::getLayoutDirection(this); -} - -void QSGGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection) -{ - QSGBasePositionerPrivate *d = static_cast(QSGBasePositionerPrivate::get(this)); - if (d->layoutDirection != layoutDirection) { - d->layoutDirection = layoutDirection; - // For RTL layout the positioning changes when the width changes. - if (d->layoutDirection == Qt::RightToLeft) - d->addItemChangeListener(d, QSGItemPrivate::Geometry); - else - d->removeItemChangeListener(d, QSGItemPrivate::Geometry); - prePositioning(); - emit layoutDirectionChanged(); - emit effectiveLayoutDirectionChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick2::Grid::effectiveLayoutDirection - This property holds the effective layout direction of the grid positioner. - - When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, - the visual layout direction of the grid positioner will be mirrored. However, the - property \l {Grid::layoutDirection}{layoutDirection} will remain unchanged. - - \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring} -*/ -Qt::LayoutDirection QSGGrid::effectiveLayoutDirection() const -{ - return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this); -} - -void QSGGrid::doPositioning(QSizeF *contentSize) -{ - QSGBasePositionerPrivate *d = static_cast(QSGBasePositionerPrivate::get(this)); - int c = m_columns; - int r = m_rows; - //Is allocating the extra QPODVector too much overhead? - QPODVector visibleItems;//we aren't concerned with invisible items - visibleItems.reserve(positionedItems.count()); - for (int i=0; i maxColWidth; - QList maxRowHeight; - int childIndex =0; - if (m_flow == LeftToRight) { - for (int i=0; i < r; i++){ - for (int j=0; j < c; j++){ - if (j==0) - maxRowHeight << 0; - if (i==0) - maxColWidth << 0; - - if (childIndex == visibleItems.count()) - break; - - const PositionedItem &child = visibleItems.at(childIndex++); - if (child.item->width() > maxColWidth[j]) - maxColWidth[j] = child.item->width(); - if (child.item->height() > maxRowHeight[i]) - maxRowHeight[i] = child.item->height(); - } - } - } else { - for (int j=0; j < c; j++){ - for (int i=0; i < r; i++){ - if (j==0) - maxRowHeight << 0; - if (i==0) - maxColWidth << 0; - - if (childIndex == visibleItems.count()) - break; - - const PositionedItem &child = visibleItems.at(childIndex++); - if (child.item->width() > maxColWidth[j]) - maxColWidth[j] = child.item->width(); - if (child.item->height() > maxRowHeight[i]) - maxRowHeight[i] = child.item->height(); - } - } - } - - int columnSpacing = m_columnSpacing; - if (columnSpacing == -1) - columnSpacing = spacing(); - - int rowSpacing = m_rowSpacing; - if (rowSpacing == -1) - rowSpacing = spacing(); - - int widthSum = 0; - for (int j=0; j < maxColWidth.size(); j++){ - if (j) - widthSum += columnSpacing; - widthSum += maxColWidth[j]; - } - - int heightSum = 0; - for (int i=0; i < maxRowHeight.size(); i++){ - if (i) - heightSum += rowSpacing; - heightSum += maxRowHeight[i]; - } - - contentSize->setHeight(heightSum); - contentSize->setWidth(widthSum); - - int end = 0; - if (widthValid()) - end = width(); - else - end = widthSum; - - int xoffset=0; - if (!d->isLeftToRight()) - xoffset = end; - int yoffset=0; - int curRow =0; - int curCol =0; - for (int i = 0; i < visibleItems.count(); ++i) { - const PositionedItem &child = visibleItems.at(i); - int childXOffset = xoffset; - if (!d->isLeftToRight()) - childXOffset -= child.item->width(); - if ((child.item->x() != childXOffset) || (child.item->y() != yoffset)){ - positionX(childXOffset, child); - positionY(yoffset, child); - } - - if (m_flow == LeftToRight) { - if (d->isLeftToRight()) - xoffset += maxColWidth[curCol]+columnSpacing; - else - xoffset -= maxColWidth[curCol]+columnSpacing; - curCol++; - curCol%=c; - if (!curCol){ - yoffset += maxRowHeight[curRow]+rowSpacing; - if (d->isLeftToRight()) - xoffset = 0; - else - xoffset = end; - curRow++; - if (curRow>=r) - break; - } - } else { - yoffset+=maxRowHeight[curRow]+rowSpacing; - curRow++; - curRow%=r; - if (!curRow){ - if (d->isLeftToRight()) - xoffset += maxColWidth[curCol]+columnSpacing; - else - xoffset -= maxColWidth[curCol]+columnSpacing; - yoffset=0; - curCol++; - if (curCol>=c) - break; - } - } - } -} - -void QSGGrid::reportConflictingAnchors() -{ - QSGBasePositionerPrivate *d = static_cast(QSGBasePositionerPrivate::get(this)); - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (child.item) { - QSGAnchors *anchors = QSGItemPrivate::get(static_cast(child.item))->_anchors; - if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { - d->anchorConflict = true; - break; - } - } - } - if (d->anchorConflict) - qmlInfo(this) << "Cannot specify anchors for items inside Grid"; -} - -/*! - \qmlclass Flow QSGFlow - \inqmlmodule QtQuick 2 - \ingroup qml-positioning-elements - \brief The Flow item arranges its children side by side, wrapping as necessary. - \inherits Item - - The Flow item positions its child items like words on a page, wrapping them - to create rows or columns of items that do not overlap. - - Spacing between items can be added using the \l spacing property. - Transitions can be used for cases where items managed by a Column are - added or moved. These are stored in the \l add and \l move properties - respectively. - - See \l{Using QML Positioner and Repeater Items} for more details about this item and other - related items. - - \section1 Example Usage - - The following example positions \l Text items within a parent item using - a Flow item. - - \image qml-flow-snippet.png - - \snippet doc/src/snippets/declarative/flow.qml flow item - - \section1 Using Transitions - - Transitions can be used to animate items that are added to, moved within, - or removed from a Flow item. The \l add and \l move properties can be set to - the transitions that will be applied when items are added to, removed from, - or re-positioned within a Flow item. - - The use of transitions with positioners is described in more detail in the - \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML - Positioner and Repeater Items} document. - - \section1 Limitations - - Note that the positioner assumes that the x and y positions of its children - will not change. If you manually change the x or y properties in script, bind - the x or y properties, use anchors on a child of a positioner, or have the - width or height of a child depend on the position of a child, then the - positioner may exhibit strange behaviour. If you need to perform any of these - actions, consider positioning the items without the use of a Flow. - - Items with a width or height of 0 will not be positioned. - - Positioning is batched and syncronized with painting to reduce the number of - calculations needed. This means that positioners may not reposition items immediately - when changes occur, but it will have moved by the next frame. - - \sa Column, Row, Grid, Positioner, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty Transition QtQuick2::Flow::add - - This property holds the transition to be applied when adding an - item to the positioner. The transition will only be applied to the - added item(s). Positioner transitions will only affect the - position (x, y) of items. - - For a positioner, adding an item can mean that either the object - has been created or reparented, and thus is now a child or the - positioner, or that the object has had its opacity increased from - zero, and thus is now visible. - - \sa move -*/ -/*! - \qmlproperty Transition QtQuick2::Flow::move - - This property holds the transition to be applied when moving an - item within the positioner. Positioner transitions will only affect - the position (x, y) of items. - - This transition can be performed when other items are added or removed - from the positioner, or when items resize themselves. - - \qml - Flow { - id: positioner - move: Transition { - NumberAnimation { - properties: "x,y" - ease: "easeOutBounce" - } - } - } - \endqml - - \sa add, {declarative/positioners}{Positioners example} -*/ -/*! - \qmlproperty int QtQuick2::Flow::spacing - - spacing is the amount in pixels left empty between each adjacent - item, and defaults to 0. - - \sa Grid::spacing -*/ - -class QSGFlowPrivate : public QSGBasePositionerPrivate -{ - Q_DECLARE_PUBLIC(QSGFlow) - -public: - QSGFlowPrivate() - : QSGBasePositionerPrivate(), flow(QSGFlow::LeftToRight) - {} - - QSGFlow::Flow flow; -}; - -QSGFlow::QSGFlow(QSGItem *parent) -: QSGBasePositioner(*(new QSGFlowPrivate), Both, parent) -{ - Q_D(QSGFlow); - // Flow layout requires relayout if its own size changes too. - d->addItemChangeListener(d, QSGItemPrivate::Geometry); -} - -/*! - \qmlproperty enumeration QtQuick2::Flow::flow - This property holds the flow of the layout. - - Possible values are: - - \list - \o Flow.LeftToRight (default) - Items are positioned next to - to each other according to the \l layoutDirection until the width of the Flow - is exceeded, then wrapped to the next line. - \o Flow.TopToBottom - Items are positioned next to each - other from top to bottom until the height of the Flow is exceeded, - then wrapped to the next column. - \endlist -*/ -QSGFlow::Flow QSGFlow::flow() const -{ - Q_D(const QSGFlow); - return d->flow; -} - -void QSGFlow::setFlow(Flow flow) -{ - Q_D(QSGFlow); - if (d->flow != flow) { - d->flow = flow; - prePositioning(); - emit flowChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick2::Flow::layoutDirection - - This property holds the layout direction of the layout. - - Possible values are: - - \list - \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, - and left to right. The flow direction is dependent on the - \l Flow::flow property. - \o Qt.RightToLeft - Items are positioned from the top to bottom, - and right to left. The flow direction is dependent on the - \l Flow::flow property. - \endlist - - \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} -*/ - -Qt::LayoutDirection QSGFlow::layoutDirection() const -{ - Q_D(const QSGFlow); - return d->layoutDirection; -} - -void QSGFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection) -{ - Q_D(QSGFlow); - if (d->layoutDirection != layoutDirection) { - d->layoutDirection = layoutDirection; - prePositioning(); - emit layoutDirectionChanged(); - emit effectiveLayoutDirectionChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick2::Flow::effectiveLayoutDirection - This property holds the effective layout direction of the flow positioner. - - When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, - the visual layout direction of the grid positioner will be mirrored. However, the - property \l {Flow::layoutDirection}{layoutDirection} will remain unchanged. - - \sa Flow::layoutDirection, {LayoutMirroring}{LayoutMirroring} -*/ - -Qt::LayoutDirection QSGFlow::effectiveLayoutDirection() const -{ - return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this); -} - -void QSGFlow::doPositioning(QSizeF *contentSize) -{ - Q_D(QSGFlow); - - int hoffset = 0; - int voffset = 0; - int linemax = 0; - QList hoffsets; - - for (int i = 0; i < positionedItems.count(); ++i) { - const PositionedItem &child = positionedItems.at(i); - if (!child.item || !child.isVisible) - continue; - - if (d->flow == LeftToRight) { - if (widthValid() && hoffset && hoffset + child.item->width() > width()) { - hoffset = 0; - voffset += linemax + spacing(); - linemax = 0; - } - } else { - if (heightValid() && voffset && voffset + child.item->height() > height()) { - voffset = 0; - hoffset += linemax + spacing(); - linemax = 0; - } - } - - if (d->isLeftToRight()) { - if (child.item->x() != hoffset) - positionX(hoffset, child); - } else { - hoffsets << hoffset; - } - if (child.item->y() != voffset) - positionY(voffset, child); - - contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width())); - contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height())); - - if (d->flow == LeftToRight) { - hoffset += child.item->width(); - hoffset += spacing(); - linemax = qMax(linemax, qCeil(child.item->height())); - } else { - voffset += child.item->height(); - voffset += spacing(); - linemax = qMax(linemax, qCeil(child.item->width())); - } - } - if (d->isLeftToRight()) - return; - - int end; - if (widthValid()) - end = width(); - else - end = contentSize->width(); - int acc = 0; - for (int i = 0; i < positionedItems.count(); ++i) { - const PositionedItem &child = positionedItems.at(i); - if (!child.item || !child.isVisible) - continue; - hoffset = end - hoffsets[acc++] - child.item->width(); - if (child.item->x() != hoffset) - positionX(hoffset, child); - } -} - -void QSGFlow::reportConflictingAnchors() -{ - Q_D(QSGFlow); - for (int ii = 0; ii < positionedItems.count(); ++ii) { - const PositionedItem &child = positionedItems.at(ii); - if (child.item) { - QSGAnchors *anchors = QSGItemPrivate::get(static_cast(child.item))->_anchors; - if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { - d->anchorConflict = true; - break; - } - } - } - if (d->anchorConflict) - qmlInfo(this) << "Cannot specify anchors for items inside Flow"; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgpositioners_p.h b/src/declarative/items/qsgpositioners_p.h deleted file mode 100644 index f8711251d3..0000000000 --- a/src/declarative/items/qsgpositioners_p.h +++ /dev/null @@ -1,295 +0,0 @@ -// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGPOSITIONERS_P_H -#define QSGPOSITIONERS_P_H - -#include "qsgimplicitsizeitem_p.h" - -#include -#include - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGBasePositionerPrivate; - -class QSGPositionerAttached : public QObject -{ - Q_OBJECT - -public: - QSGPositionerAttached(QObject *parent); - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - Q_PROPERTY(bool isFirstItem READ isFirstItem NOTIFY isFirstItemChanged) - Q_PROPERTY(bool isLastItem READ isLastItem NOTIFY isLastItemChanged) - - int index() const { return m_index; } - void setIndex(int index); - - bool isFirstItem() const { return m_isFirstItem; } - void setIsFirstItem(bool isFirstItem); - - bool isLastItem() const { return m_isLastItem; } - void setIsLastItem(bool isLastItem); - -Q_SIGNALS: - void indexChanged(); - void isFirstItemChanged(); - void isLastItemChanged(); - -private: - int m_index; - bool m_isFirstItem; - bool m_isLastItem; -}; - -class Q_DECLARATIVE_PRIVATE_EXPORT QSGBasePositioner : public QSGImplicitSizeItem -{ - Q_OBJECT - - Q_PROPERTY(int spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) - Q_PROPERTY(QDeclarativeTransition *move READ move WRITE setMove NOTIFY moveChanged) - Q_PROPERTY(QDeclarativeTransition *add READ add WRITE setAdd NOTIFY addChanged) -public: - enum PositionerType { None = 0x0, Horizontal = 0x1, Vertical = 0x2, Both = 0x3 }; - QSGBasePositioner(PositionerType, QSGItem *parent); - ~QSGBasePositioner(); - - int spacing() const; - void setSpacing(int); - - QDeclarativeTransition *move() const; - void setMove(QDeclarativeTransition *); - - QDeclarativeTransition *add() const; - void setAdd(QDeclarativeTransition *); - - static QSGPositionerAttached *qmlAttachedProperties(QObject *obj); - - void updateAttachedProperties(QSGPositionerAttached *specificProperty = 0, QSGItem *specificPropertyOwner = 0) const; - -protected: - QSGBasePositioner(QSGBasePositionerPrivate &dd, PositionerType at, QSGItem *parent); - virtual void componentComplete(); - virtual void itemChange(ItemChange, const ItemChangeData &); - void finishApplyTransitions(); - - virtual void updatePolish(); - -Q_SIGNALS: - void spacingChanged(); - void moveChanged(); - void addChanged(); - -protected Q_SLOTS: - void prePositioning(); - -protected: - virtual void doPositioning(QSizeF *contentSize)=0; - virtual void reportConflictingAnchors()=0; - class PositionedItem { - public : - PositionedItem(QSGItem *i) : item(i), isNew(false), isVisible(true) {} - bool operator==(const PositionedItem &other) const { return other.item == item; } - QSGItem *item; - bool isNew; - bool isVisible; - }; - - QPODVector positionedItems; - void positionX(int,const PositionedItem &target); - void positionY(int,const PositionedItem &target); - -private: - Q_DISABLE_COPY(QSGBasePositioner) - Q_DECLARE_PRIVATE(QSGBasePositioner) -}; - -class Q_AUTOTEST_EXPORT QSGColumn : public QSGBasePositioner -{ - Q_OBJECT -public: - QSGColumn(QSGItem *parent=0); - -protected: - virtual void doPositioning(QSizeF *contentSize); - virtual void reportConflictingAnchors(); -private: - Q_DISABLE_COPY(QSGColumn) -}; - -class Q_AUTOTEST_EXPORT QSGRow: public QSGBasePositioner -{ - Q_OBJECT - Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) - Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) -public: - QSGRow(QSGItem *parent=0); - - Qt::LayoutDirection layoutDirection() const; - void setLayoutDirection (Qt::LayoutDirection); - Qt::LayoutDirection effectiveLayoutDirection() const; - -Q_SIGNALS: - void layoutDirectionChanged(); - void effectiveLayoutDirectionChanged(); - -protected: - virtual void doPositioning(QSizeF *contentSize); - virtual void reportConflictingAnchors(); -private: - Q_DISABLE_COPY(QSGRow) -}; - -class Q_AUTOTEST_EXPORT QSGGrid : public QSGBasePositioner -{ - Q_OBJECT - Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) - Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged) - Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged) - Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged) - Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) - Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) - Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) - -public: - QSGGrid(QSGItem *parent=0); - - int rows() const {return m_rows;} - void setRows(const int rows); - - int columns() const {return m_columns;} - void setColumns(const int columns); - - int rowSpacing() const { return m_rowSpacing; } - void setRowSpacing(int); - - int columnSpacing() const { return m_columnSpacing; } - void setColumnSpacing(int); - - Q_ENUMS(Flow) - enum Flow { LeftToRight, TopToBottom }; - Flow flow() const; - void setFlow(Flow); - - Qt::LayoutDirection layoutDirection() const; - void setLayoutDirection (Qt::LayoutDirection); - Qt::LayoutDirection effectiveLayoutDirection() const; - -Q_SIGNALS: - void rowsChanged(); - void columnsChanged(); - void flowChanged(); - void layoutDirectionChanged(); - void effectiveLayoutDirectionChanged(); - void rowSpacingChanged(); - void columnSpacingChanged(); - -protected: - virtual void doPositioning(QSizeF *contentSize); - virtual void reportConflictingAnchors(); - -private: - int m_rows; - int m_columns; - int m_rowSpacing; - int m_columnSpacing; - Flow m_flow; - Q_DISABLE_COPY(QSGGrid) -}; - -class QSGFlowPrivate; -class Q_AUTOTEST_EXPORT QSGFlow: public QSGBasePositioner -{ - Q_OBJECT - Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) - Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) - Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) -public: - QSGFlow(QSGItem *parent=0); - - Q_ENUMS(Flow) - enum Flow { LeftToRight, TopToBottom }; - Flow flow() const; - void setFlow(Flow); - - Qt::LayoutDirection layoutDirection() const; - void setLayoutDirection (Qt::LayoutDirection); - Qt::LayoutDirection effectiveLayoutDirection() const; - -Q_SIGNALS: - void flowChanged(); - void layoutDirectionChanged(); - void effectiveLayoutDirectionChanged(); - -protected: - virtual void doPositioning(QSizeF *contentSize); - virtual void reportConflictingAnchors(); -protected: - QSGFlow(QSGFlowPrivate &dd, QSGItem *parent); -private: - Q_DISABLE_COPY(QSGFlow) - Q_DECLARE_PRIVATE(QSGFlow) -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGColumn) -QML_DECLARE_TYPE(QSGRow) -QML_DECLARE_TYPE(QSGGrid) -QML_DECLARE_TYPE(QSGFlow) - -QML_DECLARE_TYPE(QSGBasePositioner) -QML_DECLARE_TYPEINFO(QSGBasePositioner, QML_HAS_ATTACHED_PROPERTIES) - -QT_END_HEADER - -#endif // QSGPOSITIONERS_P_H diff --git a/src/declarative/items/qsgpositioners_p_p.h b/src/declarative/items/qsgpositioners_p_p.h deleted file mode 100644 index 3c1185378b..0000000000 --- a/src/declarative/items/qsgpositioners_p_p.h +++ /dev/null @@ -1,164 +0,0 @@ -// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGPOSITIONERS_P_P_H -#define QSGPOSITIONERS_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgpositioners_p.h" -#include "qsgimplicitsizeitem_p_p.h" - -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGBasePositionerPrivate : public QSGImplicitSizeItemPrivate, public QSGItemChangeListener -{ - Q_DECLARE_PUBLIC(QSGBasePositioner) - -public: - QSGBasePositionerPrivate() - : spacing(0), type(QSGBasePositioner::None) - , moveTransition(0), addTransition(0), positioningDirty(false) - , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) - { - } - - void init(QSGBasePositioner::PositionerType at) - { - type = at; - childrenDoNotOverlap = true; - } - - int spacing; - - QSGBasePositioner::PositionerType type; - QDeclarativeTransition *moveTransition; - QDeclarativeTransition *addTransition; - QDeclarativeStateOperation::ActionList addActions; - QDeclarativeStateOperation::ActionList moveActions; - QDeclarativeTransitionManager addTransitionManager; - QDeclarativeTransitionManager moveTransitionManager; - - void watchChanges(QSGItem *other); - void unwatchChanges(QSGItem* other); - void setPositioningDirty() { - Q_Q(QSGBasePositioner); - if (!positioningDirty) { - positioningDirty = true; - q->polish(); - } - } - - bool positioningDirty : 1; - bool doingPositioning : 1; - bool anchorConflict : 1; - - Qt::LayoutDirection layoutDirection; - - void mirrorChange() { - if (type != QSGBasePositioner::Vertical) - setPositioningDirty(); - } - bool isLeftToRight() const { - if (type == QSGBasePositioner::Vertical) - return true; - else - return effectiveLayoutMirror ? layoutDirection == Qt::RightToLeft : layoutDirection == Qt::LeftToRight; - } - - virtual void itemSiblingOrderChanged(QSGItem* other) - { - Q_UNUSED(other); - setPositioningDirty(); - } - - void itemGeometryChanged(QSGItem *, const QRectF &newGeometry, const QRectF &oldGeometry) - { - if (newGeometry.size() != oldGeometry.size()) - setPositioningDirty(); - } - - virtual void itemVisibilityChanged(QSGItem *) - { - setPositioningDirty(); - } - - void itemDestroyed(QSGItem *item) - { - Q_Q(QSGBasePositioner); - q->positionedItems.removeOne(QSGBasePositioner::PositionedItem(item)); - } - - static Qt::LayoutDirection getLayoutDirection(const QSGBasePositioner *positioner) - { - return positioner->d_func()->layoutDirection; - } - - static Qt::LayoutDirection getEffectiveLayoutDirection(const QSGBasePositioner *positioner) - { - if (positioner->d_func()->effectiveLayoutMirror) - return positioner->d_func()->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; - else - return positioner->d_func()->layoutDirection; - } -}; - -QT_END_NAMESPACE - -#endif // QSGPOSITIONERS_P_P_H diff --git a/src/declarative/items/qsgrectangle.cpp b/src/declarative/items/qsgrectangle.cpp deleted file mode 100644 index 25f0eda5d5..0000000000 --- a/src/declarative/items/qsgrectangle.cpp +++ /dev/null @@ -1,555 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgrectangle_p.h" -#include "qsgrectangle_p_p.h" - -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -// XXX todo - should we change rectangle to draw entirely within its width/height? -/*! - \internal - \class QSGPen - \brief The QSGPen class provides a pen used for drawing rectangle borders on a QSGView. - - By default, the pen is invalid and nothing is drawn. You must either set a color (then the default - width is 1) or a width (then the default color is black). - - A width of 1 indicates is a single-pixel line on the border of the item being painted. - - Example: - \qml - Rectangle { - border.width: 2 - border.color: "red" - } - \endqml -*/ - -QSGPen::QSGPen(QObject *parent) - : QObject(parent) - , m_width(1) - , m_color("#000000") - , m_aligned(true) - , m_valid(false) -{ -} - -qreal QSGPen::width() const -{ - return m_width; -} - -void QSGPen::setWidth(qreal w) -{ - if (m_width == w && m_valid) - return; - - m_width = w; - m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); - emit penChanged(); -} - -QColor QSGPen::color() const -{ - return m_color; -} - -void QSGPen::setColor(const QColor &c) -{ - m_color = c; - m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); - emit penChanged(); -} - -bool QSGPen::aligned() const -{ - return m_aligned; -} - -void QSGPen::setAligned(bool aligned) -{ - if (aligned == m_aligned) - return; - m_aligned = aligned; - m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0)); - emit penChanged(); -} - -bool QSGPen::isValid() const -{ - return m_valid; -} - -/*! - \qmlclass GradientStop QSGGradientStop - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The GradientStop item defines the color at a position in a Gradient. - - \sa Gradient -*/ - -/*! - \qmlproperty real QtQuick2::GradientStop::position - \qmlproperty color QtQuick2::GradientStop::color - - The position and color properties describe the color used at a given - position in a gradient, as represented by a gradient stop. - - The default position is 0.0; the default color is black. - - \sa Gradient -*/ -QSGGradientStop::QSGGradientStop(QObject *parent) - : QObject(parent) -{ -} - -qreal QSGGradientStop::position() const -{ - return m_position; -} - -void QSGGradientStop::setPosition(qreal position) -{ - m_position = position; updateGradient(); -} - -QColor QSGGradientStop::color() const -{ - return m_color; -} - -void QSGGradientStop::setColor(const QColor &color) -{ - m_color = color; updateGradient(); -} - -void QSGGradientStop::updateGradient() -{ - if (QSGGradient *grad = qobject_cast(parent())) - grad->doUpdate(); -} - -/*! - \qmlclass Gradient QSGGradient - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The Gradient item defines a gradient fill. - - A gradient is defined by two or more colors, which will be blended seamlessly. - - The colors are specified as a set of GradientStop child items, each of - which defines a position on the gradient from 0.0 to 1.0 and a color. - The position of each GradientStop is defined by setting its - \l{GradientStop::}{position} property; its color is defined using its - \l{GradientStop::}{color} property. - - A gradient without any gradient stops is rendered as a solid white fill. - - Note that this item is not a visual representation of a gradient. To display a - gradient, use a visual element (like \l Rectangle) which supports the use - of gradients. - - \section1 Example Usage - - \div {class="float-right"} - \inlineimage qml-gradient.png - \enddiv - - The following example declares a \l Rectangle item with a gradient starting - with red, blending to yellow at one third of the height of the rectangle, - and ending with green: - - \snippet doc/src/snippets/declarative/gradient.qml code - - \clearfloat - \section1 Performance and Limitations - - Calculating gradients can be computationally expensive compared to the use - of solid color fills or images. Consider using gradients for static items - in a user interface. - - In Qt 4.7, only vertical, linear gradients can be applied to items. If you - need to apply different orientations of gradients, a combination of rotation - and clipping will need to be applied to the relevant items. This can - introduce additional performance requirements for your application. - - The use of animations involving gradient stops may not give the desired - result. An alternative way to animate gradients is to use pre-generated - images or SVG drawings containing gradients. - - \sa GradientStop -*/ - -/*! - \qmlproperty list QtQuick2::Gradient::stops - \default - - This property holds the gradient stops describing the gradient. - - By default, this property contains an empty list. - - To set the gradient stops, define them as children of the Gradient element. -*/ -QSGGradient::QSGGradient(QObject *parent) -: QObject(parent), m_gradient(0) -{ -} - -QSGGradient::~QSGGradient() -{ - delete m_gradient; -} - -QDeclarativeListProperty QSGGradient::stops() -{ - return QDeclarativeListProperty(this, m_stops); -} - -const QGradient *QSGGradient::gradient() const -{ - if (!m_gradient && !m_stops.isEmpty()) { - m_gradient = new QLinearGradient(0,0,0,1.0); - for (int i = 0; i < m_stops.count(); ++i) { - const QSGGradientStop *stop = m_stops.at(i); - m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode); - m_gradient->setColorAt(stop->position(), stop->color()); - } - } - - return m_gradient; -} - -void QSGGradient::doUpdate() -{ - delete m_gradient; - m_gradient = 0; - emit updated(); -} - -int QSGRectanglePrivate::doUpdateSlotIdx = -1; - -/*! - \qmlclass Rectangle QSGRectangle - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The Rectangle item provides a filled rectangle with an optional border. - \inherits Item - - Rectangle items are used to fill areas with solid color or gradients, and are - often used to hold other items. - - \section1 Appearance - - Each Rectangle item is painted using either a solid fill color, specified using - the \l color property, or a gradient, defined using a Gradient element and set - using the \l gradient property. If both a color and a gradient are specified, - the gradient is used. - - You can add an optional border to a rectangle with its own color and thickness - by settting the \l border.color and \l border.width properties. - - You can also create rounded rectangles using the \l radius property. Since this - introduces curved edges to the corners of a rectangle, it may be appropriate to - set the \l smooth property to improve its appearance. - - \section1 Example Usage - - \div {class="float-right"} - \inlineimage declarative-rect.png - \enddiv - - The following example shows the effects of some of the common properties on a - Rectangle item, which in this case is used to create a square: - - \snippet doc/src/snippets/declarative/rectangle/rectangle.qml document - - \clearfloat - \section1 Performance - - Using the \l smooth property improves the appearance of a rounded rectangle at - the cost of rendering performance. You should consider unsetting this property - for rectangles in motion, and only set it when they are stationary. - - \sa Image -*/ - -QSGRectangle::QSGRectangle(QSGItem *parent) -: QSGItem(*(new QSGRectanglePrivate), parent) -{ - setFlag(ItemHasContents); -} - -void QSGRectangle::doUpdate() -{ - Q_D(QSGRectangle); - qreal penMargin = 0; - qreal penOffset = 0; - if (d->pen && d->pen->isValid()) { - if (d->pen->aligned()) { - const int pw = qRound(d->pen->width()); - penMargin = qreal(0.5) * pw; - penOffset = (pw & 1) * qreal(0.5); - } else { - penMargin = qreal(0.5) * d->pen->width(); - } - } - if (penMargin != d->penMargin || penOffset != d->penOffset) { - d->penMargin = penMargin; - d->penOffset = penOffset; - d->dirty(QSGItemPrivate::Size); // update clip - } - update(); -} - -/*! - \qmlproperty int QtQuick2::Rectangle::border.width - \qmlproperty color QtQuick2::Rectangle::border.color - - The width and color used to draw the border of the rectangle. - - A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color. - - \note The width of the rectangle's border does not affect the geometry of the - rectangle itself or its position relative to other items if anchors are used. - - If \c border.width is an odd number, the rectangle is painted at a half-pixel offset to retain - border smoothness. Also, the border is rendered evenly on either side of the - rectangle's boundaries, and the spare pixel is rendered to the right and below the - rectangle (as documented for QRect rendering). This can cause unintended effects if - \c border.width is 1 and the rectangle is \l{Item::clip}{clipped} by a parent item: - - \div {class="float-right"} - \inlineimage rect-border-width.png - \enddiv - - \snippet doc/src/snippets/declarative/rectangle/rect-border-width.qml 0 - - \clearfloat - Here, the innermost rectangle's border is clipped on the bottom and right edges by its - parent. To avoid this, the border width can be set to two instead of one. -*/ -QSGPen *QSGRectangle::border() -{ - Q_D(QSGRectangle); - return d->getPen(); -} - -/*! - \qmlproperty Gradient QtQuick2::Rectangle::gradient - - The gradient to use to fill the rectangle. - - This property allows for the construction of simple vertical gradients. - Other gradients may by formed by adding rotation to the rectangle. - - \div {class="float-left"} - \inlineimage declarative-rect_gradient.png - \enddiv - - \snippet doc/src/snippets/declarative/rectangle/rectangle-gradient.qml rectangles - \clearfloat - - If both a gradient and a color are specified, the gradient will be used. - - \sa Gradient, color -*/ -QSGGradient *QSGRectangle::gradient() const -{ - Q_D(const QSGRectangle); - return d->gradient; -} - -void QSGRectangle::setGradient(QSGGradient *gradient) -{ - Q_D(QSGRectangle); - if (d->gradient == gradient) - return; - static int updatedSignalIdx = -1; - if (updatedSignalIdx < 0) - updatedSignalIdx = QSGGradient::staticMetaObject.indexOfSignal("updated()"); - if (d->doUpdateSlotIdx < 0) - d->doUpdateSlotIdx = QSGRectangle::staticMetaObject.indexOfSlot("doUpdate()"); - if (d->gradient) - QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); - d->gradient = gradient; - if (d->gradient) - QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx); - update(); -} - -/*! - \qmlproperty real QtQuick2::Rectangle::radius - This property holds the corner radius used to draw a rounded rectangle. - - If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be - painted as a normal rectangle. The same radius is used by all 4 corners; there is currently - no way to specify different radii for different corners. -*/ -qreal QSGRectangle::radius() const -{ - Q_D(const QSGRectangle); - return d->radius; -} - -void QSGRectangle::setRadius(qreal radius) -{ - Q_D(QSGRectangle); - if (d->radius == radius) - return; - - d->radius = radius; - update(); - emit radiusChanged(); -} - -/*! - \qmlproperty color QtQuick2::Rectangle::color - This property holds the color used to fill the rectangle. - - The default color is white. - - \div {class="float-right"} - \inlineimage rect-color.png - \enddiv - - The following example shows rectangles with colors specified - using hexadecimal and named color notation: - - \snippet doc/src/snippets/declarative/rectangle/rectangle-colors.qml rectangles - - \clearfloat - If both a gradient and a color are specified, the gradient will be used. - - \sa gradient -*/ -QColor QSGRectangle::color() const -{ - Q_D(const QSGRectangle); - return d->color; -} - -void QSGRectangle::setColor(const QColor &c) -{ - Q_D(QSGRectangle); - if (d->color == c) - return; - - d->color = c; - update(); - emit colorChanged(); -} - -QSGNode *QSGRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) -{ - Q_UNUSED(data); - Q_D(QSGRectangle); - - if (width() <= 0 || height() <= 0) { - delete oldNode; - return 0; - } - - QSGRectangleNode *rectangle = static_cast(oldNode); - if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode(); - - rectangle->setRect(QRectF(0, 0, width(), height())); - rectangle->setColor(d->color); - - if (d->pen && d->pen->isValid()) { - rectangle->setPenColor(d->pen->color()); - rectangle->setPenWidth(d->pen->width()); - rectangle->setAligned(d->pen->aligned()); - } else { - rectangle->setPenWidth(0); - } - - rectangle->setRadius(d->radius); - - QGradientStops stops; - if (d->gradient) { - QList qxstops = d->gradient->m_stops; - for (int i = 0; i < qxstops.size(); ++i){ - int j = 0; - while (j < stops.size() && stops.at(j).first < qxstops[i]->position()) - j++; - stops.insert(j, QGradientStop(qxstops.at(i)->position(), qxstops.at(i)->color())); - } - } - rectangle->setGradientStops(stops); - - rectangle->update(); - - return rectangle; -} -/*! - \qmlproperty bool QtQuick2::Rectangle::smooth - - Set this property if you want the item to be smoothly scaled or - transformed. Smooth filtering gives better visual quality, but is slower. If - the item is displayed at its natural size, this property has no visual or - performance effect. - - \note Generally scaling artifacts are only visible if the item is stationary on - the screen. A common pattern when animating an item is to disable smooth - filtering at the beginning of the animation and reenable it at the conclusion. - - \image rect-smooth.png - On this image, smooth is turned off on the top half and on on the bottom half. -*/ - -QRectF QSGRectangle::boundingRect() const -{ - Q_D(const QSGRectangle); - return QRectF(d->penOffset - d->penMargin, d->penOffset - d->penMargin, - d->width + 2 * d->penMargin, d->height + 2 * d->penMargin); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgrectangle_p.h b/src/declarative/items/qsgrectangle_p.h deleted file mode 100644 index 4d93e6662a..0000000000 --- a/src/declarative/items/qsgrectangle_p.h +++ /dev/null @@ -1,189 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGRECTANGLE_P_H -#define QSGRECTANGLE_P_H - -#include "qsgitem.h" - -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) -class Q_DECLARATIVE_PRIVATE_EXPORT QSGPen : public QObject -{ - Q_OBJECT - - Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY penChanged) - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY penChanged) - Q_PROPERTY(bool aligned READ aligned WRITE setAligned NOTIFY penChanged) -public: - QSGPen(QObject *parent=0); - - qreal width() const; - void setWidth(qreal w); - - QColor color() const; - void setColor(const QColor &c); - - bool aligned() const; - void setAligned(bool aligned); - - bool isValid() const; - -Q_SIGNALS: - void penChanged(); - -private: - qreal m_width; - QColor m_color; - bool m_aligned : 1; - bool m_valid : 1; -}; - -class Q_AUTOTEST_EXPORT QSGGradientStop : public QObject -{ - Q_OBJECT - - Q_PROPERTY(qreal position READ position WRITE setPosition) - Q_PROPERTY(QColor color READ color WRITE setColor) - -public: - QSGGradientStop(QObject *parent=0); - - qreal position() const; - void setPosition(qreal position); - - QColor color() const; - void setColor(const QColor &color); - -private: - void updateGradient(); - -private: - qreal m_position; - QColor m_color; -}; - -class Q_AUTOTEST_EXPORT QSGGradient : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QDeclarativeListProperty stops READ stops) - Q_CLASSINFO("DefaultProperty", "stops") - -public: - QSGGradient(QObject *parent=0); - ~QSGGradient(); - - QDeclarativeListProperty stops(); - - const QGradient *gradient() const; - -Q_SIGNALS: - void updated(); - -private: - void doUpdate(); - -private: - QList m_stops; - mutable QGradient *m_gradient; - friend class QSGRectangle; - friend class QSGGradientStop; -}; - -class QSGRectanglePrivate; -class Q_DECLARATIVE_PRIVATE_EXPORT QSGRectangle : public QSGItem -{ - Q_OBJECT - - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(QSGGradient *gradient READ gradient WRITE setGradient) - Q_PROPERTY(QSGPen * border READ border CONSTANT) - Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) -public: - QSGRectangle(QSGItem *parent=0); - - QColor color() const; - void setColor(const QColor &); - - QSGPen *border(); - - QSGGradient *gradient() const; - void setGradient(QSGGradient *gradient); - - qreal radius() const; - void setRadius(qreal radius); - - virtual QRectF boundingRect() const; - -Q_SIGNALS: - void colorChanged(); - void radiusChanged(); - -protected: - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - -private Q_SLOTS: - void doUpdate(); - -private: - Q_DISABLE_COPY(QSGRectangle) - Q_DECLARE_PRIVATE(QSGRectangle) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGPen) -QML_DECLARE_TYPE(QSGGradientStop) -QML_DECLARE_TYPE(QSGGradient) -QML_DECLARE_TYPE(QSGRectangle) - -QT_END_HEADER - -#endif // QSGRECTANGLE_P_H diff --git a/src/declarative/items/qsgrectangle_p_p.h b/src/declarative/items/qsgrectangle_p_p.h deleted file mode 100644 index 234a029aaf..0000000000 --- a/src/declarative/items/qsgrectangle_p_p.h +++ /dev/null @@ -1,103 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGRECTANGLE_P_P_H -#define QSGRECTANGLE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgitem_p.h" - -QT_BEGIN_NAMESPACE - -class QSGGradient; -class QSGRectangle; -class QSGRectanglePrivate : public QSGItemPrivate -{ - Q_DECLARE_PUBLIC(QSGRectangle) - -public: - QSGRectanglePrivate() : - color(Qt::white), gradient(0), pen(0), radius(0), penMargin(0), penOffset(0) - { - } - - ~QSGRectanglePrivate() - { - delete pen; - } - - QColor color; - QSGGradient *gradient; - QSGPen *pen; - qreal radius; - qreal penMargin; - qreal penOffset; - static int doUpdateSlotIdx; - - QSGPen *getPen() { - if (!pen) { - Q_Q(QSGRectangle); - pen = new QSGPen; - static int penChangedSignalIdx = -1; - if (penChangedSignalIdx < 0) - penChangedSignalIdx = QSGPen::staticMetaObject.indexOfSignal("penChanged()"); - if (doUpdateSlotIdx < 0) - doUpdateSlotIdx = QSGRectangle::staticMetaObject.indexOfSlot("doUpdate()"); - QMetaObject::connect(pen, penChangedSignalIdx, q, doUpdateSlotIdx); - } - return pen; - } -}; - -QT_END_NAMESPACE - -#endif // QSGRECTANGLE_P_P_H diff --git a/src/declarative/items/qsgrepeater.cpp b/src/declarative/items/qsgrepeater.cpp deleted file mode 100644 index 10adb75893..0000000000 --- a/src/declarative/items/qsgrepeater.cpp +++ /dev/null @@ -1,438 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgrepeater_p.h" -#include "qsgrepeater_p_p.h" -#include "qsgvisualdatamodel_p.h" - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QSGRepeaterPrivate::QSGRepeaterPrivate() -: model(0), ownModel(false) -{ -} - -QSGRepeaterPrivate::~QSGRepeaterPrivate() -{ - if (ownModel) - delete model; -} - -/*! - \qmlclass Repeater QSGRepeater - \inqmlmodule QtQuick 2 - \ingroup qml-utility-elements - \inherits Item - - \brief The Repeater element allows you to repeat an Item-based component using a model. - - The Repeater element is used to create a large number of - similar items. Like other view elements, a Repeater has a \l model and a \l delegate: - for each entry in the model, the delegate is instantiated - in a context seeded with data from the model. A Repeater item is usually - enclosed in a positioner element such as \l Row or \l Column to visually - position the multiple delegate items created by the Repeater. - - The following Repeater creates three instances of a \l Rectangle item within - a \l Row: - - \snippet doc/src/snippets/declarative/repeaters/repeater.qml import - \codeline - \snippet doc/src/snippets/declarative/repeaters/repeater.qml simple - - \image repeater-simple.png - - A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}. - Additionally, like delegates for other views, a Repeater delegate can access - its index within the repeater, as well as the model data relevant to the - delegate. See the \l delegate property documentation for details. - - Items instantiated by the Repeater are inserted, in order, as - children of the Repeater's parent. The insertion starts immediately after - the repeater's position in its parent stacking list. This allows - a Repeater to be used inside a layout. For example, the following Repeater's - items are stacked between a red rectangle and a blue rectangle: - - \snippet doc/src/snippets/declarative/repeaters/repeater.qml layout - - \image repeater.png - - - \note A Repeater item owns all items it instantiates. Removing or dynamically destroying - an item created by a Repeater results in unpredictable behavior. - - - \section2 Considerations when using Repeater - - The Repeater element creates all of its delegate items when the repeater is first - created. This can be inefficient if there are a large number of delegate items and - not all of the items are required to be visible at the same time. If this is the case, - consider using other view elements like ListView (which only creates delegate items - when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to - create items as they are required. - - Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects. - For example, it cannot be used to repeat QtObjects: - \badcode - Item { - //XXX does not work! Can't repeat QtObject as it doesn't derive from Item. - Repeater { - model: 10 - QtObject {} - } - } - \endcode - */ - -/*! - \qmlsignal QtQuick2::Repeater::onItemAdded(int index, Item item) - - This handler is called when an item is added to the repeater. The \a index - parameter holds the index at which the item has been inserted within the - repeater, and the \a item parameter holds the \l Item that has been added. -*/ - -/*! - \qmlsignal QtQuick2::Repeater::onItemRemoved(int index, Item item) - - This handler is called when an item is removed from the repeater. The \a index - parameter holds the index at which the item was removed from the repeater, - and the \a item parameter holds the \l Item that was removed. - - Do not keep a reference to \a item if it was created by this repeater, as - in these cases it will be deleted shortly after the handler is called. -*/ -QSGRepeater::QSGRepeater(QSGItem *parent) - : QSGItem(*(new QSGRepeaterPrivate), parent) -{ -} - -QSGRepeater::~QSGRepeater() -{ -} - -/*! - \qmlproperty any QtQuick2::Repeater::model - - The model providing data for the repeater. - - This property can be set to any of the supported \l {qmlmodels}{data models}: - - \list - \o A number that indicates the number of delegates to be created by the repeater - \o A model (e.g. a ListModel item, or a QAbstractItemModel subclass) - \o A string list - \o An object list - \endlist - - The type of model affects the properties that are exposed to the \l delegate. - - \sa {qmlmodels}{Data Models} -*/ -QVariant QSGRepeater::model() const -{ - Q_D(const QSGRepeater); - return d->dataSource; -} - -void QSGRepeater::setModel(const QVariant &model) -{ - Q_D(QSGRepeater); - if (d->dataSource == model) - return; - - clear(); - if (d->model) { - disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), - this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - /* - disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - */ - } - d->dataSource = model; - QObject *object = qvariant_cast(model); - QSGVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { - if (d->ownModel) { - delete d->model; - d->ownModel = false; - } - d->model = vim; - } else { - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this)); - d->ownModel = true; - if (isComponentComplete()) - static_cast(d->model)->componentComplete(); - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - dataModel->setModel(model); - } - if (d->model) { - connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), - this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); - /* - connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*))); - connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*))); - */ - regenerate(); - } - emit modelChanged(); - emit countChanged(); -} - -/*! - \qmlproperty Component QtQuick2::Repeater::delegate - \default - - The delegate provides a template defining each item instantiated by the repeater. - - Delegates are exposed to a read-only \c index property that indicates the index - of the delegate within the repeater. For example, the following \l Text delegate - displays the index of each repeated item: - - \table - \row - \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index - \o \image repeater-index.png - \endtable - - If the \l model is a \l{QStringList-based model}{string list} or - \l{QObjectList-based model}{object list}, the delegate is also exposed to - a read-only \c modelData property that holds the string or object data. For - example: - - \table - \row - \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata - \o \image repeater-modeldata.png - \endtable - - If the \l model is a model object (such as a \l ListModel) the delegate - can access all model roles as named properties, in the same way that delegates - do for view classes like ListView. - - \sa {QML Data Models} - */ -QDeclarativeComponent *QSGRepeater::delegate() const -{ - Q_D(const QSGRepeater); - if (d->model) { - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - return dataModel->delegate(); - } - - return 0; -} - -void QSGRepeater::setDelegate(QDeclarativeComponent *delegate) -{ - Q_D(QSGRepeater); - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) - if (delegate == dataModel->delegate()) - return; - - if (!d->ownModel) { - d->model = new QSGVisualDataModel(qmlContext(this)); - d->ownModel = true; - } - if (QSGVisualDataModel *dataModel = qobject_cast(d->model)) { - dataModel->setDelegate(delegate); - regenerate(); - emit delegateChanged(); - } -} - -/*! - \qmlproperty int QtQuick2::Repeater::count - - This property holds the number of items in the repeater. -*/ -int QSGRepeater::count() const -{ - Q_D(const QSGRepeater); - if (d->model) - return d->model->count(); - return 0; -} - -/*! - \qmlmethod Item QtQuick2::Repeater::itemAt(index) - - Returns the \l Item that has been created at the given \a index, or \c null - if no item exists at \a index. -*/ -QSGItem *QSGRepeater::itemAt(int index) const -{ - Q_D(const QSGRepeater); - if (index >= 0 && index < d->deletables.count()) - return d->deletables[index]; - return 0; -} - -void QSGRepeater::componentComplete() -{ - Q_D(QSGRepeater); - if (d->model && d->ownModel) - static_cast(d->model)->componentComplete(); - QSGItem::componentComplete(); - regenerate(); - if (d->model && d->model->count()) - emit countChanged(); -} - -void QSGRepeater::itemChange(ItemChange change, const ItemChangeData &value) -{ - QSGItem::itemChange(change, value); - if (change == ItemParentHasChanged) { - regenerate(); - } -} - -void QSGRepeater::clear() -{ - Q_D(QSGRepeater); - bool complete = isComponentComplete(); - - if (d->model) { - while (d->deletables.count() > 0) { - QSGItem *item = d->deletables.takeLast(); - if (complete) - emit itemRemoved(d->deletables.count()-1, item); - d->model->release(item); - } - } - d->deletables.clear(); -} - -void QSGRepeater::regenerate() -{ - Q_D(QSGRepeater); - if (!isComponentComplete()) - return; - - clear(); - - if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete()) - return; - - for (int ii = 0; ii < count(); ++ii) { - QSGItem *item = d->model->item(ii); - if (item) { - QDeclarative_setParent_noEvent(item, parentItem()); - item->setParentItem(parentItem()); - item->stackBefore(this); - d->deletables << item; - emit itemAdded(ii, item); - } - } -} - -void QSGRepeater::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) -{ - Q_D(QSGRepeater); - - if (!isComponentComplete()) - return; - - if (reset) { - regenerate(); - emit countChanged(); - } - - int difference = 0; - QHash > > moved; - foreach (const QDeclarativeChangeSet::Remove &remove, changeSet.removes()) { - int index = qMin(remove.index, d->deletables.count()); - int count = qMin(remove.index + remove.count, d->deletables.count()) - index; - if (remove.isMove()) { - moved.insert(remove.moveId, d->deletables.mid(index, count)); - d->deletables.erase( - d->deletables.begin() + index, - d->deletables.begin() + index + count); - } else while (count--) { - QSGItem *item = d->deletables.takeAt(index); - emit itemRemoved(index, item); - if (item) - d->model->release(item); - } - - difference -= remove.count; - } - - foreach (const QDeclarativeChangeSet::Insert &insert, changeSet.inserts()) { - int index = qMin(insert.index, d->deletables.count()); - if (insert.isMove()) { - QList > items = moved.value(insert.moveId); - d->deletables = d->deletables.mid(0, index) + items + d->deletables.mid(index); - QSGItem *stackBefore = index + items.count() < d->deletables.count() - ? d->deletables.at(index + items.count()) - : this; - for (int i = index; i < index + items.count(); ++i) - d->deletables.at(i)->stackBefore(stackBefore); - } else for (int i = 0; i < insert.count; ++i) { - int modelIndex = index + i; - QSGItem *item = d->model->item(modelIndex); - if (item) { - QDeclarative_setParent_noEvent(item, parentItem()); - item->setParentItem(parentItem()); - if (modelIndex < d->deletables.count()) - item->stackBefore(d->deletables.at(modelIndex)); - else - item->stackBefore(this); - d->deletables.insert(modelIndex, item); - emit itemAdded(modelIndex, item); - } - } - difference += insert.count; - } - - if (difference != 0) - emit countChanged(); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgrepeater_p.h b/src/declarative/items/qsgrepeater_p.h deleted file mode 100644 index 2094d94993..0000000000 --- a/src/declarative/items/qsgrepeater_p.h +++ /dev/null @@ -1,110 +0,0 @@ -// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGREPEATER_P_H -#define QSGREPEATER_P_H - -#include "qsgitem.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QDeclarativeChangeSet; - -class QSGRepeaterPrivate; -class Q_AUTOTEST_EXPORT QSGRepeater : public QSGItem -{ - Q_OBJECT - - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - -public: - QSGRepeater(QSGItem *parent=0); - virtual ~QSGRepeater(); - - QVariant model() const; - void setModel(const QVariant &); - - QDeclarativeComponent *delegate() const; - void setDelegate(QDeclarativeComponent *); - - int count() const; - - Q_INVOKABLE QSGItem *itemAt(int index) const; - -Q_SIGNALS: - void modelChanged(); - void delegateChanged(); - void countChanged(); - - void itemAdded(int index, QSGItem *item); - void itemRemoved(int index, QSGItem *item); - -private: - void clear(); - void regenerate(); - -protected: - virtual void componentComplete(); - void itemChange(ItemChange change, const ItemChangeData &value); - -private Q_SLOTS: - void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); - -private: - Q_DISABLE_COPY(QSGRepeater) - Q_DECLARE_PRIVATE(QSGRepeater) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGRepeater) - -QT_END_HEADER - -#endif // QSGREPEATER_P_H diff --git a/src/declarative/items/qsgrepeater_p_p.h b/src/declarative/items/qsgrepeater_p_p.h deleted file mode 100644 index ae4c78e1a4..0000000000 --- a/src/declarative/items/qsgrepeater_p_p.h +++ /dev/null @@ -1,83 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGREPEATER_P_P_H -#define QSGREPEATER_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgrepeater_p.h" -#include "qsgitem_p.h" - -#include - -QT_BEGIN_NAMESPACE - -class QDeclarativeContext; -class QSGVisualModel; -class QSGRepeaterPrivate : public QSGItemPrivate -{ - Q_DECLARE_PUBLIC(QSGRepeater) - -public: - QSGRepeaterPrivate(); - ~QSGRepeaterPrivate(); - - QSGVisualModel *model; - QVariant dataSource; - bool ownModel; - - QList > deletables; -}; - -QT_END_NAMESPACE - -#endif // QSGREPEATER_P_P_H diff --git a/src/declarative/items/qsgscalegrid.cpp b/src/declarative/items/qsgscalegrid.cpp deleted file mode 100644 index 1c4ce8d289..0000000000 --- a/src/declarative/items/qsgscalegrid.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgscalegrid_p_p.h" - -#include - -QT_BEGIN_NAMESPACE - -/*! - \internal - \class QSGScaleGrid - \brief The QSGScaleGrid class allows you to specify a 3x3 grid to use in scaling an image. -*/ - -QSGScaleGrid::QSGScaleGrid(QObject *parent) : QObject(parent), _left(0), _top(0), _right(0), _bottom(0) -{ -} - -QSGScaleGrid::~QSGScaleGrid() -{ -} - -bool QSGScaleGrid::isNull() const -{ - return !_left && !_top && !_right && !_bottom; -} - -void QSGScaleGrid::setLeft(int pos) -{ - if (_left != pos) { - _left = pos; - emit borderChanged(); - } -} - -void QSGScaleGrid::setTop(int pos) -{ - if (_top != pos) { - _top = pos; - emit borderChanged(); - } -} - -void QSGScaleGrid::setRight(int pos) -{ - if (_right != pos) { - _right = pos; - emit borderChanged(); - } -} - -void QSGScaleGrid::setBottom(int pos) -{ - if (_bottom != pos) { - _bottom = pos; - emit borderChanged(); - } -} - -QSGGridScaledImage::QSGGridScaledImage() -: _l(-1), _r(-1), _t(-1), _b(-1), - _h(QSGBorderImage::Stretch), _v(QSGBorderImage::Stretch) -{ -} - -QSGGridScaledImage::QSGGridScaledImage(const QSGGridScaledImage &o) -: _l(o._l), _r(o._r), _t(o._t), _b(o._b), _h(o._h), _v(o._v), _pix(o._pix) -{ -} - -QSGGridScaledImage &QSGGridScaledImage::operator=(const QSGGridScaledImage &o) -{ - _l = o._l; - _r = o._r; - _t = o._t; - _b = o._b; - _h = o._h; - _v = o._v; - _pix = o._pix; - return *this; -} - -QSGGridScaledImage::QSGGridScaledImage(QIODevice *data) -: _l(-1), _r(-1), _t(-1), _b(-1), _h(QSGBorderImage::Stretch), _v(QSGBorderImage::Stretch) -{ - int l = -1; - int r = -1; - int t = -1; - int b = -1; - QString imgFile; - - QByteArray raw; - while (raw = data->readLine(), !raw.isEmpty()) { - QString line = QString::fromUtf8(raw.trimmed()); - if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) - continue; - - int colonId = line.indexOf(QLatin1Char(':')); - if (colonId <= 0) - return; - - QStringList list; - list.append(line.left(colonId).trimmed()); - list.append(line.mid(colonId+1).trimmed()); - - if (list[0] == QLatin1String("border.left")) - l = list[1].toInt(); - else if (list[0] == QLatin1String("border.right")) - r = list[1].toInt(); - else if (list[0] == QLatin1String("border.top")) - t = list[1].toInt(); - else if (list[0] == QLatin1String("border.bottom")) - b = list[1].toInt(); - else if (list[0] == QLatin1String("source")) - imgFile = list[1]; - else if (list[0] == QLatin1String("horizontalTileRule")) - _h = stringToRule(list[1]); - else if (list[0] == QLatin1String("verticalTileRule")) - _v = stringToRule(list[1]); - } - - if (l < 0 || r < 0 || t < 0 || b < 0 || imgFile.isEmpty()) - return; - - _l = l; _r = r; _t = t; _b = b; - - _pix = imgFile; - if (_pix.startsWith(QLatin1Char('"')) && _pix.endsWith(QLatin1Char('"'))) - _pix = _pix.mid(1, _pix.size() - 2); // remove leading/trailing quotes. -} - -QSGBorderImage::TileMode QSGGridScaledImage::stringToRule(const QString &s) -{ - if (s == QLatin1String("Stretch")) - return QSGBorderImage::Stretch; - if (s == QLatin1String("Repeat")) - return QSGBorderImage::Repeat; - if (s == QLatin1String("Round")) - return QSGBorderImage::Round; - - qWarning("QSGGridScaledImage: Invalid tile rule specified. Using Stretch."); - return QSGBorderImage::Stretch; -} - -bool QSGGridScaledImage::isValid() const -{ - return _l >= 0; -} - -int QSGGridScaledImage::gridLeft() const -{ - return _l; -} - -int QSGGridScaledImage::gridRight() const -{ - return _r; -} - -int QSGGridScaledImage::gridTop() const -{ - return _t; -} - -int QSGGridScaledImage::gridBottom() const -{ - return _b; -} - -QString QSGGridScaledImage::pixmapUrl() const -{ - return _pix; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgscalegrid_p_p.h b/src/declarative/items/qsgscalegrid_p_p.h deleted file mode 100644 index 03d68c4683..0000000000 --- a/src/declarative/items/qsgscalegrid_p_p.h +++ /dev/null @@ -1,134 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGSCALEGRID_P_P_H -#define QSGSCALEGRID_P_P_H - -#include "qsgborderimage_p.h" - -#include -#include - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class Q_DECLARATIVE_PRIVATE_EXPORT QSGScaleGrid : public QObject -{ - Q_OBJECT - Q_ENUMS(TileRule) - - Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged) - Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged) - Q_PROPERTY(int right READ right WRITE setRight NOTIFY borderChanged) - Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY borderChanged) - -public: - QSGScaleGrid(QObject *parent=0); - ~QSGScaleGrid(); - - bool isNull() const; - - int left() const { return _left; } - void setLeft(int); - - int top() const { return _top; } - void setTop(int); - - int right() const { return _right; } - void setRight(int); - - int bottom() const { return _bottom; } - void setBottom(int); - -Q_SIGNALS: - void borderChanged(); - -private: - int _left; - int _top; - int _right; - int _bottom; -}; - -class Q_DECLARATIVE_PRIVATE_EXPORT QSGGridScaledImage -{ -public: - QSGGridScaledImage(); - QSGGridScaledImage(const QSGGridScaledImage &); - QSGGridScaledImage(QIODevice*); - QSGGridScaledImage &operator=(const QSGGridScaledImage &); - bool isValid() const; - int gridLeft() const; - int gridRight() const; - int gridTop() const; - int gridBottom() const; - QSGBorderImage::TileMode horizontalTileRule() const { return _h; } - QSGBorderImage::TileMode verticalTileRule() const { return _v; } - - QString pixmapUrl() const; - -private: - static QSGBorderImage::TileMode stringToRule(const QString &); - -private: - int _l; - int _r; - int _t; - int _b; - QSGBorderImage::TileMode _h; - QSGBorderImage::TileMode _v; - QString _pix; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGScaleGrid) - -QT_END_HEADER - -#endif // QSGSCALEGRID_P_P_H diff --git a/src/declarative/items/qsgshadereffect.cpp b/src/declarative/items/qsgshadereffect.cpp deleted file mode 100644 index f84d023906..0000000000 --- a/src/declarative/items/qsgshadereffect.cpp +++ /dev/null @@ -1,649 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include - -#include "qsgmaterial.h" -#include "qsgitem_p.h" - -#include -#include -#include "qsgcanvas.h" - -#include "qsgimage_p.h" -#include "qsgshadereffectsource_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -static const char qt_default_vertex_code[] = - "uniform highp mat4 qt_Matrix; \n" - "attribute highp vec4 qt_Vertex; \n" - "attribute highp vec2 qt_MultiTexCoord0; \n" - "varying highp vec2 qt_TexCoord0; \n" - "void main() { \n" - " qt_TexCoord0 = qt_MultiTexCoord0; \n" - " gl_Position = qt_Matrix * qt_Vertex; \n" - "}"; - -static const char qt_default_fragment_code[] = - "varying highp vec2 qt_TexCoord0; \n" - "uniform sampler2D source; \n" - "uniform lowp float qt_Opacity; \n" - "void main() { \n" - " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n" - "}"; - -static const char qt_position_attribute_name[] = "qt_Vertex"; -static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; - -const char *qtPositionAttributeName() -{ - return qt_position_attribute_name; -} - -const char *qtTexCoordAttributeName() -{ - return qt_texcoord_attribute_name; -} - -// TODO: Remove after grace period. -QSGShaderEffectItem::QSGShaderEffectItem(QSGItem *parent) - : QSGShaderEffect(parent) -{ - qWarning("ShaderEffectItem has been deprecated. Use ShaderEffect instead."); -} - - -/*! - \qmlclass ShaderEffect QSGShaderEffect - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The ShaderEffect element applies custom shaders to a rectangle. - \inherits Item - - The ShaderEffect element applies a custom OpenGL - \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a - rectangle. It allows you to write effects such as drop shadow, blur, - colorize and page curl directly in QML. - - There are two types of input to the \l vertexShader: - uniform variables and attributes. Some are predefined: - \list - \o uniform mat4 qt_Matrix - combined transformation - matrix, the product of the matrices from the root item to this - ShaderEffect, and an orthogonal projection. - \o uniform float qt_Opacity - combined opacity, the product of the - opacities from the root item to this ShaderEffect. - \o attribute vec4 qt_Vertex - vertex position, the top-left vertex has - position (0, 0), the bottom-right (\l{Item::width}{width}, - \l{Item::height}{height}). - \o attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left - coordinate is (0, 0), the bottom-right (1, 1). - \endlist - - In addition, any property that can be mapped to an OpenGL Shading Language - (GLSL) type is available as a uniform variable. The following list shows - how properties are mapped to GLSL uniform variables: - \list - \o bool, int, qreal -> bool, int, float - If the type in the shader is not - the same as in QML, the value is converted automatically. - \o QColor -> vec4 - When colors are passed to the shader, they are first - premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes - vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example. - \o QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in - the shader. - \o QPoint, QPointF, QSize, QSizeF -> vec2 - \o QVector3D -> vec3 - \o QTransform -> mat4 - \o \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left - corner, and the color values are premultiplied. - \endlist - - The output from the \l fragmentShader should be premultiplied. If - \l blending is enabled, source-over blending is used. However, additive - blending can be achieved by outputting zero in the alpha channel. - - \row - \o \image declarative-shadereffectitem.png - \o \qml - import QtQuick 2.0 - - Rectangle { - width: 200; height: 100 - Row { - Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" } - ShaderEffect { - width: 100; height: 100 - property variant src: img - vertexShader: " - uniform highp mat4 qt_Matrix; - attribute highp vec4 qt_Vertex; - attribute highp vec2 qt_MultiTexCoord0; - varying highp vec2 coord; - void main() { - coord = qt_MultiTexCoord0; - gl_Position = qt_Matrix * qt_Vertex; - }" - fragmentShader: " - varying highp vec2 coord; - uniform sampler2D src; - uniform lowp float qt_Opacity; - void main() { - lowp vec4 tex = texture2D(src, coord); - gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity; - }" - } - } - } - \endqml - \endrow - - By default, the ShaderEffect consists of four vertices, one for each - corner. For non-linear vertex transformations, like page curl, you can - specify a fine grid of vertices by specifying a \l mesh resolution. - - \note Scene Graph textures have origin in the top-left corner rather than - bottom-left which is common in OpenGL. -*/ - -QSGShaderEffect::QSGShaderEffect(QSGItem *parent) - : QSGItem(parent) - , m_meshResolution(1, 1) - , m_mesh(0) - , m_cullMode(NoCulling) - , m_blending(true) - , m_dirtyData(true) - , m_programDirty(true) - , m_dirtyMesh(true) - , m_dirtyGeometry(true) -{ - setFlag(QSGItem::ItemHasContents); -} - -QSGShaderEffect::~QSGShaderEffect() -{ - reset(); -} - -void QSGShaderEffect::componentComplete() -{ - updateProperties(); - QSGItem::componentComplete(); -} - -/*! - \qmlproperty string QtQuick2::ShaderEffect::fragmentShader - - This property holds the fragment shader's GLSL source code. - The default shader passes the texture coordinate along to the fragment - shader as "varying highp vec2 qt_TexCoord0". -*/ - -void QSGShaderEffect::setFragmentShader(const QByteArray &code) -{ - if (m_source.fragmentCode.constData() == code.constData()) - return; - m_source.fragmentCode = code; - if (isComponentComplete()) { - reset(); - updateProperties(); - } - emit fragmentShaderChanged(); -} - -/*! - \qmlproperty string QtQuick2::ShaderEffect::vertexShader - - This property holds the vertex shader's GLSL source code. - The default shader expects the texture coordinate to be passed from the - vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a - sampler2D named "source". -*/ - -void QSGShaderEffect::setVertexShader(const QByteArray &code) -{ - if (m_source.vertexCode.constData() == code.constData()) - return; - m_source.vertexCode = code; - if (isComponentComplete()) { - reset(); - updateProperties(); - } - emit vertexShaderChanged(); -} - -/*! - \qmlproperty bool QtQuick2::ShaderEffect::blending - - If this property is true, the output from the \l fragmentShader is blended - with the background using source-over blend mode. If false, the background - is disregarded. Blending decreases the performance, so you should set this - property to false when blending is not needed. The default value is true. -*/ - -void QSGShaderEffect::setBlending(bool enable) -{ - if (blending() == enable) - return; - - m_blending = enable; - update(); - - emit blendingChanged(); -} - -/*! - \qmlproperty variant QtQuick2::ShaderEffect::mesh - - This property defines the mesh used to draw the ShaderEffect. It can hold - any mesh object deriving from \l QSGShaderEffectMesh, such as \l GridMesh. - If a size value is assigned to this property, the ShaderEffect implicitly - uses a \l GridMesh with the value as - \l{GridMesh::resolution}{mesh resolution}. By default, this property is - the size 1x1. - - \sa GridMesh -*/ - -QVariant QSGShaderEffect::mesh() const -{ - return m_mesh ? qVariantFromValue(static_cast(m_mesh)) - : qVariantFromValue(m_meshResolution); -} - -void QSGShaderEffect::setMesh(const QVariant &mesh) -{ - QSGShaderEffectMesh *newMesh = qobject_cast(qVariantValue(mesh)); - if (newMesh && newMesh == m_mesh) - return; - if (m_mesh) - disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0); - m_mesh = newMesh; - if (m_mesh) { - connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry())); - } else { - if (qVariantCanConvert(mesh)) { - m_meshResolution = mesh.toSize(); - } else { - QList res = mesh.toByteArray().split('x'); - bool ok = res.size() == 2; - if (ok) { - int w = res.at(0).toInt(&ok); - if (ok) { - int h = res.at(1).toInt(&ok); - if (ok) - m_meshResolution = QSize(w, h); - } - } - if (!ok) - qWarning("ShaderEffect: mesh property must be size or object deriving from QSGShaderEffectMesh."); - } - m_defaultMesh.setResolution(m_meshResolution); - } - - m_dirtyMesh = true; - update(); - emit meshChanged(); -} - -/*! - \qmlproperty enumeration QtQuick2::ShaderEffect::cullMode - - This property defines which sides of the element should be visible. - - \list - \o ShaderEffect.NoCulling - Both sides are visible - \o ShaderEffect.BackFaceCulling - only front side is visible - \o ShaderEffect.FrontFaceCulling - only back side is visible - \endlist - - The default is NoCulling. -*/ - -void QSGShaderEffect::setCullMode(CullMode face) -{ - if (face == m_cullMode) - return; - m_cullMode = face; - update(); - emit cullModeChanged(); -} - -void QSGShaderEffect::changeSource(int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - QVariant v = property(m_sources.at(index).name.constData()); - setSource(v, index); -} - -void QSGShaderEffect::updateData() -{ - m_dirtyData = true; - update(); -} - -void QSGShaderEffect::updateGeometry() -{ - m_dirtyGeometry = true; - update(); -} - -void QSGShaderEffect::setSource(const QVariant &var, int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - - SourceData &source = m_sources[index]; - - source.sourceObject = 0; - if (var.isNull()) { - return; - } else if (!qVariantCanConvert(var)) { - qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); - return; - } - - QObject *obj = qVariantValue(var); - QSGItem *item = qobject_cast(obj); - if (!item || !item->isTextureProvider()) { - qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]", - source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className()); - return; - } - - source.sourceObject = item; - - - // TODO: Find better solution. - // 'item' needs a canvas to get a scenegraph node. - // The easiest way to make sure it gets a canvas is to - // make it a part of the same item tree as 'this'. - if (item && item->parentItem() == 0) { - item->setParentItem(this); - item->setVisible(false); - } -} - -void QSGShaderEffect::disconnectPropertySignals() -{ - disconnect(this, 0, this, SLOT(updateData())); - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - disconnect(this, 0, source.mapper, 0); - disconnect(source.mapper, 0, this, 0); - } -} - -void QSGShaderEffect::connectPropertySignals() -{ - QSet::const_iterator it; - for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { - int pi = metaObject()->indexOfProperty(it->constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - if (!mp.hasNotifySignal()) - qWarning("QSGShaderEffect: property '%s' does not have notification method!", it->constData()); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().signature()); - connect(this, signalName, this, SLOT(updateData())); - } else { - qWarning("QSGShaderEffect: '%s' does not have a matching property!", it->constData()); - } - } - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - int pi = metaObject()->indexOfProperty(source.name.constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().signature()); - connect(this, signalName, source.mapper, SLOT(map())); - source.mapper->setMapping(this, i); - connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); - } else { - qWarning("QSGShaderEffect: '%s' does not have a matching source!", source.name.constData()); - } - } -} - -void QSGShaderEffect::reset() -{ - disconnectPropertySignals(); - - m_source.attributeNames.clear(); - m_source.uniformNames.clear(); - m_source.respectsOpacity = false; - m_source.respectsMatrix = false; - m_source.className = metaObject()->className(); - - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - delete source.mapper; - QSGItem *item = qobject_cast(source.sourceObject); - if (item && item->parentItem() == this) - item->setParentItem(0); - } - m_sources.clear(); - - m_programDirty = true; - m_dirtyMesh = true; -} - -void QSGShaderEffect::updateProperties() -{ - QByteArray vertexCode = m_source.vertexCode; - QByteArray fragmentCode = m_source.fragmentCode; - if (vertexCode.isEmpty()) - vertexCode = qt_default_vertex_code; - if (fragmentCode.isEmpty()) - fragmentCode = qt_default_fragment_code; - - lookThroughShaderCode(vertexCode); - lookThroughShaderCode(fragmentCode); - - if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) - qWarning("QSGShaderEffect: Missing reference to \'%s\'.", qt_position_attribute_name); - if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) - qWarning("QSGShaderEffect: Missing reference to \'%s\'.", qt_texcoord_attribute_name); - if (!m_source.respectsMatrix) - qWarning("QSGShaderEffect: Missing reference to \'qt_Matrix\'."); - if (!m_source.respectsOpacity) - qWarning("QSGShaderEffect: Missing reference to \'qt_Opacity\'."); - - for (int i = 0; i < m_sources.size(); ++i) { - QVariant v = property(m_sources.at(i).name); - setSource(v, i); - } - - connectPropertySignals(); -} - -void QSGShaderEffect::lookThroughShaderCode(const QByteArray &code) -{ - // Regexp for matching attributes and uniforms. - // In human readable form: attribute|uniform [lowp|mediump|highp] - static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); - Q_ASSERT(re.isValid()); - - int pos = -1; - - QString wideCode = QString::fromLatin1(code.constData(), code.size()); - - while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { - QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute - QByteArray type = re.cap(2).toLatin1(); // type - QByteArray name = re.cap(3).toLatin1(); // variable name - - if (decl == "attribute") { - m_source.attributeNames.append(name); - } else { - Q_ASSERT(decl == "uniform"); - - if (name == "qt_Matrix") { - m_source.respectsMatrix = true; - } else if (name == "qt_ModelViewProjectionMatrix") { - // TODO: Remove after grace period. - qWarning("ShaderEffect: qt_ModelViewProjectionMatrix is deprecated. Use qt_Matrix instead."); - m_source.respectsMatrix = true; - } else if (name == "qt_Opacity") { - m_source.respectsOpacity = true; - } else { - m_source.uniformNames.insert(name); - if (type == "sampler2D") { - SourceData d; - d.mapper = new QSignalMapper; - d.name = name; - d.sourceObject = 0; - m_sources.append(d); - } - } - } - } -} - -void QSGShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - m_dirtyGeometry = true; - QSGItem::geometryChanged(newGeometry, oldGeometry); -} - -QSGNode *QSGShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - QSGShaderEffectNode *node = static_cast(oldNode); - - // In the case of a bad vertex shader, don't try to create a node... - if (m_source.attributeNames.isEmpty()) { - if (node) - delete node; - return 0; - } - - if (!node) { - node = new QSGShaderEffectNode; - m_programDirty = true; - m_dirtyData = true; - m_dirtyGeometry = true; - } - - QSGShaderEffectMaterial *material = node->shaderMaterial(); - - if (m_dirtyMesh) { - node->setGeometry(0); - m_dirtyMesh = false; - m_dirtyGeometry = true; - } - - if (m_dirtyGeometry) { - node->setFlag(QSGNode::OwnsGeometry, false); - QSGGeometry *geometry = node->geometry(); - QRectF rect(0, 0, width(), height()); - QSGShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh; - - geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect); - if (!geometry) { - delete node; - return 0; - } - - node->setGeometry(geometry); - node->setFlag(QSGNode::OwnsGeometry, true); - - m_dirtyGeometry = false; - } - - if (m_programDirty) { - QSGShaderEffectProgram s = m_source; - if (s.fragmentCode.isEmpty()) - s.fragmentCode = qt_default_fragment_code; - if (s.vertexCode.isEmpty()) - s.vertexCode = qt_default_vertex_code; - s.className = metaObject()->className(); - - material->setProgramSource(s); - node->markDirty(QSGNode::DirtyMaterial); - m_programDirty = false; - } - - // Update blending - if (bool(material->flags() & QSGMaterial::Blending) != m_blending) { - material->setFlag(QSGMaterial::Blending, m_blending); - node->markDirty(QSGNode::DirtyMaterial); - } - - if (int(material->cullMode()) != int(m_cullMode)) { - material->setCullMode(QSGShaderEffectMaterial::CullMode(m_cullMode)); - node->markDirty(QSGNode::DirtyMaterial); - } - - if (m_dirtyData) { - QVector > values; - QVector > textures; - const QVector > &oldTextures = material->textureProviders(); - - for (QSet::const_iterator it = m_source.uniformNames.begin(); - it != m_source.uniformNames.end(); ++it) { - values.append(qMakePair(*it, property(*it))); - } - for (int i = 0; i < oldTextures.size(); ++i) { - QSGTextureProvider *t = oldTextures.at(i).second; - if (t) - disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - } - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - QSGTextureProvider *t = source.sourceObject->textureProvider(); - textures.append(qMakePair(source.name, t)); - if (t) - connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); - } - material->setUniforms(values); - material->setTextureProviders(textures); - node->markDirty(QSGNode::DirtyMaterial); - m_dirtyData = false; - } - - return node; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgshadereffect_p.h b/src/declarative/items/qsgshadereffect_p.h deleted file mode 100644 index 08af5f6bff..0000000000 --- a/src/declarative/items/qsgshadereffect_p.h +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SHADEREFFECTITEM_H -#define SHADEREFFECTITEM_H - -#include "qsgitem.h" - -#include "qsgmaterial.h" -#include -#include -#include "qsgshadereffectmesh_p.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -const char *qtPositionAttributeName(); -const char *qtTexCoordAttributeName(); - -class QSGContext; -class QSignalMapper; -class QSGCustomMaterialShader; - -class QSGShaderEffect : public QSGItem -{ - Q_OBJECT - Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged) - Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged) - Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged) - Q_PROPERTY(QVariant mesh READ mesh WRITE setMesh NOTIFY meshChanged) - Q_PROPERTY(CullMode culling READ cullMode WRITE setCullMode NOTIFY cullModeChanged) - Q_ENUMS(CullMode) - -public: - enum CullMode - { - NoCulling = QSGShaderEffectMaterial::NoCulling, - BackFaceCulling = QSGShaderEffectMaterial::BackFaceCulling, - FrontFaceCulling = QSGShaderEffectMaterial::FrontFaceCulling - }; - - QSGShaderEffect(QSGItem *parent = 0); - ~QSGShaderEffect(); - - virtual void componentComplete(); - - QByteArray fragmentShader() const { return m_source.fragmentCode; } - void setFragmentShader(const QByteArray &code); - - QByteArray vertexShader() const { return m_source.vertexCode; } - void setVertexShader(const QByteArray &code); - - bool blending() const { return m_blending; } - void setBlending(bool enable); - - QVariant mesh() const; - void setMesh(const QVariant &mesh); - - CullMode cullMode() const { return m_cullMode; } - void setCullMode(CullMode face); - -Q_SIGNALS: - void fragmentShaderChanged(); - void vertexShaderChanged(); - void blendingChanged(); - void meshChanged(); - void cullModeChanged(); - -protected: - virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - -private Q_SLOTS: - void changeSource(int index); - void updateData(); - void updateGeometry(); - -private: - friend class QSGCustomMaterialShader; - friend class QSGShaderEffectNode; - - void setSource(const QVariant &var, int index); - void disconnectPropertySignals(); - void connectPropertySignals(); - void reset(); - void updateProperties(); - void lookThroughShaderCode(const QByteArray &code); - - QSGShaderEffectProgram m_source; - QSize m_meshResolution; - QSGShaderEffectMesh *m_mesh; - QSGGridMesh m_defaultMesh; - CullMode m_cullMode; - - struct SourceData - { - QSignalMapper *mapper; - QPointer sourceObject; - QByteArray name; - }; - QVector m_sources; - - uint m_blending : 1; - uint m_dirtyData : 1; - - uint m_programDirty : 1; - uint m_dirtyMesh : 1; - uint m_dirtyGeometry : 1; -}; - -// TODO: Remove after grace period. -class QSGShaderEffectItem : public QSGShaderEffect -{ -public: - QSGShaderEffectItem(QSGItem *parent = 0); -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SHADEREFFECTITEM_H diff --git a/src/declarative/items/qsgshadereffectmesh.cpp b/src/declarative/items/qsgshadereffectmesh.cpp deleted file mode 100644 index 53fd917e06..0000000000 --- a/src/declarative/items/qsgshadereffectmesh.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgshadereffectmesh_p.h" -#include "qsggeometry.h" -#include "qsgshadereffect_p.h" - -QT_BEGIN_NAMESPACE - -QSGShaderEffectMesh::QSGShaderEffectMesh(QObject *parent) - : QObject(parent) -{ -} - -/*! - \qmlclass GridMesh QSGGridMesh - \inqmlmodule QtQuick 2 - \ingroup qml-utility-elements - \brief GridMesh defines a mesh with vertices arranged in a grid. - - GridMesh defines a rectangular mesh consisting of vertices arranged in an - evenly spaced grid. It is used to generate \l{QSGGeometry}{geometry}. - The grid resolution is specified with the \l resolution property. -*/ - -QSGGridMesh::QSGGridMesh(QObject *parent) - : QSGShaderEffectMesh(parent) - , m_resolution(1, 1) -{ - connect(this, SIGNAL(resolutionChanged()), this, SIGNAL(geometryChanged())); -} - -QSGGeometry *QSGGridMesh::updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &dstRect) const -{ - int vmesh = m_resolution.height(); - int hmesh = m_resolution.width(); - int attrCount = attributes.count(); - - if (!geometry) { - bool error = true; - switch (attrCount) { - case 0: - qWarning("QSGGridMesh:: No attributes specified."); - break; - case 1: - if (attributes.at(0) == qtPositionAttributeName()) { - error = false; - break; - } - qWarning("QSGGridMesh:: Missing \'%s\' attribute.", - qtPositionAttributeName()); - break; - case 2: - if (attributes.contains(qtPositionAttributeName()) - && attributes.contains(qtTexCoordAttributeName())) - { - error = false; - break; - } - qWarning("QSGGridMesh:: Missing \'%s\' or \'%s\' attribute.", - qtPositionAttributeName(), qtTexCoordAttributeName()); - break; - default: - qWarning("QSGGridMesh:: Too many attributes specified."); - break; - } - - geometry = new QSGGeometry(attrCount == 1 - ? QSGGeometry::defaultAttributes_Point2D() - : QSGGeometry::defaultAttributes_TexturedPoint2D(), - (vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2), - GL_UNSIGNED_SHORT); - - } else { - geometry->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2)); - } - - QSGGeometry::Point2D *vdata = static_cast(geometry->vertexData()); - - bool positionFirst = attributes.at(0) == qtPositionAttributeName(); - - QRectF srcRect(0, 0, 1, 1); - for (int iy = 0; iy <= vmesh; ++iy) { - float fy = iy / float(vmesh); - float y = float(dstRect.top()) + fy * float(dstRect.height()); - float ty = float(srcRect.top()) + fy * float(srcRect.height()); - for (int ix = 0; ix <= hmesh; ++ix) { - float fx = ix / float(hmesh); - for (int ia = 0; ia < attrCount; ++ia) { - if (positionFirst == (ia == 0)) { - vdata->x = float(dstRect.left()) + fx * float(dstRect.width()); - vdata->y = y; - ++vdata; - } else { - vdata->x = float(srcRect.left()) + fx * float(srcRect.width()); - vdata->y = ty; - ++vdata; - } - } - } - } - - quint16 *indices = (quint16 *)geometry->indexDataAsUShort(); - int i = 0; - for (int iy = 0; iy < vmesh; ++iy) { - *(indices++) = i + hmesh + 1; - for (int ix = 0; ix <= hmesh; ++ix, ++i) { - *(indices++) = i + hmesh + 1; - *(indices++) = i; - } - *(indices++) = i - 1; - } - - return geometry; -} - -/*! - \qmlproperty size QtQuick2::GridMesh::resolution - - This property holds the grid resolution. The resolution's width and height - specify the number of cells or spacings between vertices horizontally and - vertically respectively. The minimum and default is 1x1, which corresponds - to four vertices in total, one in each corner. - For non-linear vertex transformations, you probably want to set the - resolution higher. - - \row - \o \image declarative-gridmesh.png - \o \qml - import QtQuick 2.0 - - ShaderEffect { - width: 200 - height: 200 - mesh: GridMesh { - resolution: Qt.size(20, 20) - } - property variant source: Image { - source: "qt-logo.png" - sourceSize { width: 200; height: 200 } - smooth: true - } - vertexShader: " - uniform highp mat4 qt_Matrix; - attribute highp vec4 qt_Vertex; - attribute highp vec2 qt_MultiTexCoord0; - varying highp vec2 qt_TexCoord0; - uniform highp float width; - void main() { - highp vec4 pos = qt_Vertex; - highp float d = .5 * smoothstep(0., 1., qt_MultiTexCoord0.y); - pos.x = width * mix(d, 1.0 - d, qt_MultiTexCoord0.x); - gl_Position = qt_Matrix * pos; - qt_TexCoord0 = qt_MultiTexCoord0; - }" - } - \endqml - \endrow -*/ - -void QSGGridMesh::setResolution(const QSize &res) -{ - if (res == m_resolution) - return; - if (res.width() < 1 || res.height() < 1) { - return; - } - m_resolution = res; - emit resolutionChanged(); -} - -QSize QSGGridMesh::resolution() const -{ - return m_resolution; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgshadereffectmesh_p.h b/src/declarative/items/qsgshadereffectmesh_p.h deleted file mode 100644 index 428674fdba..0000000000 --- a/src/declarative/items/qsgshadereffectmesh_p.h +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdeclarativeparserstatus.h" - -#include -#include -#include -#include -#include - -#ifndef SHADEREFFECTMESH_H -#define SHADEREFFECTMESH_H - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGGeometry; -class QRectF; - -class Q_DECLARATIVE_EXPORT QSGShaderEffectMesh : public QObject -{ - Q_OBJECT -public: - QSGShaderEffectMesh(QObject *parent = 0); - // If 'geometry' != 0, 'attributes' is the same as last time the function was called. - virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &rect) const = 0; - -Q_SIGNALS: - // Emitted when the geometry needs to be updated. - void geometryChanged(); -}; - -class QSGGridMesh : public QSGShaderEffectMesh -{ - Q_OBJECT - Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged) -public: - QSGGridMesh(QObject *parent = 0); - virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &rect) const; - - void setResolution(const QSize &res); - QSize resolution() const; - -Q_SIGNALS: - void resolutionChanged(); - -private: - QSize m_resolution; -}; - -inline QColor qt_premultiply_color(const QColor &c) -{ - return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF()); -} - - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SHADEREFFECTITEM_H diff --git a/src/declarative/items/qsgshadereffectnode.cpp b/src/declarative/items/qsgshadereffectnode.cpp deleted file mode 100644 index c5d208345e..0000000000 --- a/src/declarative/items/qsgshadereffectnode.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include - -#include "qsgshadereffectmesh_p.h" -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGCustomMaterialShader : public QSGMaterialShader -{ -public: - QSGCustomMaterialShader(const QSGShaderEffectMaterialKey &key, const QVector &attributes); - virtual void deactivate(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; - -protected: - friend class QSGShaderEffectNode; - - virtual void initialize(); - virtual const char *vertexShader() const; - virtual const char *fragmentShader() const; - - const QSGShaderEffectMaterialKey m_key; - QVector m_attributeNames; - const QVector m_attributes; - - QVector m_uniformLocs; - int m_opacityLoc; - int m_matrixLoc; - uint m_textureIndicesSet; -}; - -QSGCustomMaterialShader::QSGCustomMaterialShader(const QSGShaderEffectMaterialKey &key, const QVector &attributes) - : m_key(key) - , m_attributes(attributes) - , m_textureIndicesSet(false) -{ - for (int i = 0; i < attributes.count(); ++i) - m_attributeNames.append(attributes.at(i).constData()); - m_attributeNames.append(0); -} - -void QSGCustomMaterialShader::deactivate() -{ - QSGMaterialShader::deactivate(); - glDisable(GL_CULL_FACE); -} - -void QSGCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) -{ - Q_ASSERT(newEffect != 0); - - const QSGShaderEffectMaterial *material = static_cast(newEffect); - - if (!m_textureIndicesSet) { - for (int i = 0; i < material->m_textures.size(); ++i) - program()->setUniformValue(material->m_textures.at(i).first.constData(), i); - m_textureIndicesSet = true; - } - - if (m_uniformLocs.size() != material->m_uniformValues.size()) { - m_uniformLocs.reserve(material->m_uniformValues.size()); - for (int i = 0; i < material->m_uniformValues.size(); ++i) { - const QByteArray &name = material->m_uniformValues.at(i).first; - m_uniformLocs.append(program()->uniformLocation(name.constData())); - } - } - - QOpenGLFunctions *functions = state.context()->functions(); - for (int i = material->m_textures.size() - 1; i >= 0; --i) { - functions->glActiveTexture(GL_TEXTURE0 + i); - if (QSGTextureProvider *provider = material->m_textures.at(i).second) { - if (QSGTexture *texture = provider->texture()) { - texture->bind(); - continue; - } - } - qWarning("ShaderEffectItem: source or provider missing when binding textures"); - glBindTexture(GL_TEXTURE_2D, 0); - } - - if (material->m_source.respectsOpacity) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - for (int i = 0; i < material->m_uniformValues.count(); ++i) { - const QVariant &v = material->m_uniformValues.at(i).second; - - switch (v.type()) { - case QVariant::Color: - program()->setUniformValue(m_uniformLocs.at(i), qt_premultiply_color(qvariant_cast(v))); - break; - case QVariant::Double: - program()->setUniformValue(m_uniformLocs.at(i), (float) qvariant_cast(v)); - break; - case QVariant::Transform: - program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast(v)); - break; - case QVariant::Int: - program()->setUniformValue(m_uniformLocs.at(i), v.toInt()); - break; - case QVariant::Bool: - program()->setUniformValue(m_uniformLocs.at(i), GLint(v.toBool())); - break; - case QVariant::Size: - case QVariant::SizeF: - program()->setUniformValue(m_uniformLocs.at(i), v.toSizeF()); - break; - case QVariant::Point: - case QVariant::PointF: - program()->setUniformValue(m_uniformLocs.at(i), v.toPointF()); - break; - case QVariant::Rect: - case QVariant::RectF: - { - QRectF r = v.toRectF(); - program()->setUniformValue(m_uniformLocs.at(i), r.x(), r.y(), r.width(), r.height()); - } - break; - case QVariant::Vector3D: - program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast(v)); - break; - default: - break; - } - } - - const QSGShaderEffectMaterial *oldMaterial = static_cast(oldEffect); - if (oldEffect == 0 || material->cullMode() != oldMaterial->cullMode()) { - switch (material->cullMode()) { - case QSGShaderEffectMaterial::FrontFaceCulling: - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - break; - case QSGShaderEffectMaterial::BackFaceCulling: - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - break; - default: - glDisable(GL_CULL_FACE); - break; - } - } - - if ((state.isMatrixDirty()) && material->m_source.respectsMatrix) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); -} - -char const *const *QSGCustomMaterialShader::attributeNames() const -{ - return m_attributeNames.constData(); -} - -void QSGCustomMaterialShader::initialize() -{ - m_opacityLoc = program()->uniformLocation("qt_Opacity"); - m_matrixLoc = program()->uniformLocation("qt_Matrix"); - // TODO: Remove after grace period. - if (m_matrixLoc == -1) - m_matrixLoc = program()->uniformLocation("qt_ModelViewProjectionMatrix"); -} - -const char *QSGCustomMaterialShader::vertexShader() const -{ - return m_key.vertexCode.constData(); -} - -const char *QSGCustomMaterialShader::fragmentShader() const -{ - return m_key.fragmentCode.constData(); -} - - -bool QSGShaderEffectMaterialKey::operator == (const QSGShaderEffectMaterialKey &other) const -{ - return vertexCode == other.vertexCode && fragmentCode == other.fragmentCode && className == other.className; -} - -uint qHash(const QSGShaderEffectMaterialKey &key) -{ - return qHash(qMakePair(qMakePair(key.vertexCode, key.fragmentCode), key.className)); -} - - -QHash > QSGShaderEffectMaterial::materialMap; - -QSGShaderEffectMaterial::QSGShaderEffectMaterial() - : m_cullMode(NoCulling) -{ - setFlag(Blending, true); -} - -QSGMaterialType *QSGShaderEffectMaterial::type() const -{ - return m_type.data(); -} - -QSGMaterialShader *QSGShaderEffectMaterial::createShader() const -{ - return new QSGCustomMaterialShader(m_source, m_source.attributeNames); -} - -int QSGShaderEffectMaterial::compare(const QSGMaterial *other) const -{ - return this - static_cast(other); -} - -void QSGShaderEffectMaterial::setCullMode(QSGShaderEffectMaterial::CullMode face) -{ - m_cullMode = face; -} - -QSGShaderEffectMaterial::CullMode QSGShaderEffectMaterial::cullMode() const -{ - return m_cullMode; -} - -void QSGShaderEffectMaterial::setProgramSource(const QSGShaderEffectProgram &source) -{ - m_source = source; - m_type = materialMap.value(m_source); - if (m_type.isNull()) { - m_type = QSharedPointer(new QSGMaterialType); - materialMap.insert(m_source, m_type); - } -} - -void QSGShaderEffectMaterial::setUniforms(const QVector > &uniformValues) -{ - m_uniformValues = uniformValues; -} - -void QSGShaderEffectMaterial::setTextureProviders(const QVector > &textures) -{ - m_textures = textures; -} - -const QVector > &QSGShaderEffectMaterial::textureProviders() const -{ - return m_textures; -} - -void QSGShaderEffectMaterial::updateTextures() const -{ - for (int i = 0; i < m_textures.size(); ++i) { - if (QSGTextureProvider *provider = m_textures.at(i).second) { - if (QSGDynamicTexture *texture = qobject_cast(provider->texture())) - texture->updateTexture(); - } - } -} - - -QSGShaderEffectNode::QSGShaderEffectNode() -{ - QSGNode::setFlag(UsePreprocess, true); - setMaterial(&m_material); -} - -QSGShaderEffectNode::~QSGShaderEffectNode() -{ -} - -void QSGShaderEffectNode::markDirtyTexture() -{ - markDirty(DirtyMaterial); -} - -void QSGShaderEffectNode::preprocess() -{ - Q_ASSERT(material()); - static_cast(material())->updateTextures(); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgshadereffectnode_p.h b/src/declarative/items/qsgshadereffectnode_p.h deleted file mode 100644 index d95dfaf3cb..0000000000 --- a/src/declarative/items/qsgshadereffectnode_p.h +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SHADEREFFECTNODE_H -#define SHADEREFFECTNODE_H - -#include "qsgnode.h" -#include "qsgmaterial.h" -#include -#include - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -struct QSGShaderEffectMaterialKey { - QByteArray vertexCode; - QByteArray fragmentCode; - const char *className; - - bool operator == (const QSGShaderEffectMaterialKey &other) const; -}; - -uint qHash(const QSGShaderEffectMaterialKey &key); - -// TODO: Implement support for multisampling. -struct QSGShaderEffectProgram : public QSGShaderEffectMaterialKey -{ - QSGShaderEffectProgram() : respectsOpacity(false), respectsMatrix(false) {} - - QVector attributeNames; - QSet uniformNames; - - uint respectsOpacity : 1; - uint respectsMatrix : 1; -}; - - -class QSGCustomMaterialShader; -class QSGShaderEffectMaterial : public QSGMaterial -{ -public: - enum CullMode - { - NoCulling, - BackFaceCulling, - FrontFaceCulling - }; - - QSGShaderEffectMaterial(); - virtual QSGMaterialType *type() const; - virtual QSGMaterialShader *createShader() const; - virtual int compare(const QSGMaterial *other) const; - - void setCullMode(CullMode face); - CullMode cullMode() const; - - void setProgramSource(const QSGShaderEffectProgram &); - void setUniforms(const QVector > &uniformValues); - void setTextureProviders(const QVector > &textures); - const QVector > &textureProviders() const; - void updateTextures() const; - -protected: - friend class QSGShaderEffect; - friend class QSGCustomMaterialShader; - - // The type pointer needs to be unique. It is not safe to let the type object be part of the - // QSGShaderEffectMaterial, since it can be deleted and a new one constructed on top of the old - // one. The new QSGShaderEffectMaterial would then get the same type pointer as the old one, and - // CustomMaterialShaders based on the old one would incorrectly be used together with the new - // one. To guarantee that the type pointer is unique, the type object must live as long as - // there are any CustomMaterialShaders of that type. - QSharedPointer m_type; - - QSGShaderEffectProgram m_source; - QVector > m_uniformValues; - QVector > m_textures; - CullMode m_cullMode; - - static QHash > materialMap; -}; - - -class QSGShaderEffectMesh; - -class QSGShaderEffectNode : public QObject, public QSGGeometryNode -{ - Q_OBJECT -public: - QSGShaderEffectNode(); - virtual ~QSGShaderEffectNode(); - - virtual void preprocess(); - - QSGShaderEffectMaterial *shaderMaterial() { return &m_material; } - -private Q_SLOTS: - void markDirtyTexture(); - -private: - QSGShaderEffectMaterial m_material; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SHADEREFFECTNODE_H diff --git a/src/declarative/items/qsgshadereffectsource.cpp b/src/declarative/items/qsgshadereffectsource.cpp deleted file mode 100644 index 4bbabe1701..0000000000 --- a/src/declarative/items/qsgshadereffectsource.cpp +++ /dev/null @@ -1,905 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgshadereffectsource_p.h" - -#include "qsgitem_p.h" -#include "qsgcanvas_p.h" -#include -#include - -#include "qopenglframebufferobject.h" -#include "qmath.h" -#include - -QT_BEGIN_NAMESPACE - -DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) - -class QSGShaderEffectSourceTextureProvider : public QSGTextureProvider -{ - Q_OBJECT -public: - QSGShaderEffectSourceTextureProvider() - : sourceTexture(0) - { - } - - QSGTexture *texture() const { - sourceTexture->setMipmapFiltering(mipmapFiltering); - sourceTexture->setFiltering(filtering); - sourceTexture->setHorizontalWrapMode(horizontalWrap); - sourceTexture->setVerticalWrapMode(verticalWrap); - return sourceTexture; - } - - QSGShaderEffectTexture *sourceTexture; - - QSGTexture::Filtering mipmapFiltering; - QSGTexture::Filtering filtering; - QSGTexture::WrapMode horizontalWrap; - QSGTexture::WrapMode verticalWrap; -}; -#include "qsgshadereffectsource.moc" - - -QSGShaderEffectSourceNode::QSGShaderEffectSourceNode() -{ - setFlag(UsePreprocess, true); -} - -void QSGShaderEffectSourceNode::markDirtyTexture() -{ - markDirty(DirtyMaterial); -} - - -QSGShaderEffectTexture::QSGShaderEffectTexture(QSGItem *shaderSource) - : QSGDynamicTexture() - , m_item(0) - , m_format(GL_RGBA) - , m_shaderSource(shaderSource) - , m_renderer(0) - , m_fbo(0) - , m_secondaryFbo(0) -#ifdef QSG_DEBUG_FBO_OVERLAY - , m_debugOverlay(0) -#endif - , m_context(QSGItemPrivate::get(shaderSource)->sceneGraphContext()) - , m_mipmap(false) - , m_live(true) - , m_recursive(false) - , m_dirtyTexture(true) - , m_multisamplingSupportChecked(false) - , m_multisampling(false) - , m_grab(false) -{ -} - -QSGShaderEffectTexture::~QSGShaderEffectTexture() -{ - delete m_renderer; - delete m_fbo; - delete m_secondaryFbo; -#ifdef QSG_DEBUG_FBO_OVERLAY - delete m_debugOverlay; -#endif -} - -int QSGShaderEffectTexture::textureId() const -{ - return m_fbo ? m_fbo->texture() : 0; -} - -bool QSGShaderEffectTexture::hasAlphaChannel() const -{ - return m_format != GL_RGB; -} - -bool QSGShaderEffectTexture::hasMipmaps() const -{ - return m_mipmap; -} - - -void QSGShaderEffectTexture::bind() -{ -#ifndef QT_NO_DEBUG - if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound())) - qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively."); -#endif - glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0); - updateBindOptions(); -} - -bool QSGShaderEffectTexture::updateTexture() -{ - if ((m_live || m_grab) && m_dirtyTexture) { - grab(); - m_grab = false; - return true; - } - return false; -} - -void QSGShaderEffectTexture::setHasMipmaps(bool mipmap) -{ - if (mipmap == m_mipmap) - return; - m_mipmap = mipmap; - if (m_mipmap && m_fbo && !m_fbo->format().mipmap()) - markDirtyTexture(); -} - - -void QSGShaderEffectTexture::setItem(QSGNode *item) -{ - if (item == m_item) - return; - m_item = item; - markDirtyTexture(); -} - -void QSGShaderEffectTexture::setRect(const QRectF &rect) -{ - if (rect == m_rect) - return; - m_rect = rect; - markDirtyTexture(); -} - -void QSGShaderEffectTexture::setSize(const QSize &size) -{ - if (size == m_size) - return; - m_size = size; - markDirtyTexture(); -} - -void QSGShaderEffectTexture::setFormat(GLenum format) -{ - if (format == m_format) - return; - m_format = format; - markDirtyTexture(); -} - -void QSGShaderEffectTexture::setLive(bool live) -{ - if (live == m_live) - return; - m_live = live; - markDirtyTexture(); -} - -void QSGShaderEffectTexture::scheduleUpdate() -{ - if (m_grab) - return; - m_grab = true; - if (m_dirtyTexture) - emit textureChanged(); -} - -void QSGShaderEffectTexture::setRecursive(bool recursive) -{ - m_recursive = recursive; -} - -void QSGShaderEffectTexture::markDirtyTexture() -{ - m_dirtyTexture = true; - if (m_live || m_grab) - emit textureChanged(); -} - -void QSGShaderEffectTexture::grab() -{ - if (!m_item || m_size.isNull()) { - delete m_fbo; - delete m_secondaryFbo; - m_fbo = m_secondaryFbo = 0; - m_dirtyTexture = false; - return; - } - QSGNode *root = m_item; - while (root->firstChild() && root->type() != QSGNode::RootNodeType) - root = root->firstChild(); - if (root->type() != QSGNode::RootNodeType) - return; - - if (m_size.isEmpty()) { - delete m_fbo; - delete m_secondaryFbo; - m_secondaryFbo = m_fbo = 0; - return; - } - - if (!m_renderer) { - m_renderer = m_context->createRenderer(); - connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()), Qt::DirectConnection); - } - m_renderer->setRootNode(static_cast(root)); - - bool deleteFboLater = false; - if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format - || (!m_fbo->format().mipmap() && m_mipmap)) - { - if (!m_multisamplingSupportChecked) { - QList extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' '); - m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample") - && extensions.contains("GL_EXT_framebuffer_blit"); - m_multisamplingSupportChecked = true; - } - if (m_multisampling) { - // Don't delete the FBO right away in case it is used recursively. - deleteFboLater = true; - delete m_secondaryFbo; - QOpenGLFramebufferObjectFormat format; - - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - format.setInternalTextureFormat(m_format); - format.setSamples(8); - m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); - } else { - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - format.setInternalTextureFormat(m_format); - format.setMipmap(m_mipmap); - if (m_recursive) { - deleteFboLater = true; - delete m_secondaryFbo; - m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); - glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); - updateBindOptions(true); - } else { - delete m_fbo; - delete m_secondaryFbo; - m_fbo = new QOpenGLFramebufferObject(m_size, format); - m_secondaryFbo = 0; - glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); - updateBindOptions(true); - } - } - } - - if (m_recursive && !m_secondaryFbo) { - // m_fbo already created, m_recursive was just set. - Q_ASSERT(m_fbo); - Q_ASSERT(!m_multisampling); - - m_secondaryFbo = new QOpenGLFramebufferObject(m_size, m_fbo->format()); - glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture()); - updateBindOptions(true); - } - - // Render texture. - root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update. - m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update. - -#ifdef QSG_DEBUG_FBO_OVERLAY - if (qmlFboOverlay()) { - if (!m_debugOverlay) - m_debugOverlay = m_context->createRectangleNode(); - m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height())); - m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40)); - m_debugOverlay->setPenColor(QColor()); - m_debugOverlay->setPenWidth(0); - m_debugOverlay->setRadius(0); - m_debugOverlay->update(); - root->appendChildNode(m_debugOverlay); - } -#endif - - m_dirtyTexture = false; - - QOpenGLContext *ctx = m_context->glContext(); - m_renderer->setDeviceRect(m_size); - m_renderer->setViewportRect(m_size); - QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height()); - m_renderer->setProjectionMatrixToRect(mirrored); - m_renderer->setClearColor(Qt::transparent); - - if (m_multisampling) { - m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); - - if (deleteFboLater) { - delete m_fbo; - QOpenGLFramebufferObjectFormat format; - format.setInternalTextureFormat(m_format); - format.setAttachment(QOpenGLFramebufferObject::NoAttachment); - format.setMipmap(m_mipmap); - format.setSamples(0); - m_fbo = new QOpenGLFramebufferObject(m_size, format); - glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); - updateBindOptions(true); - } - - QRect r(QPoint(), m_size); - QOpenGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r); - } else { - if (m_recursive) { - m_renderer->renderScene(QSGBindableFbo(m_secondaryFbo)); - - if (deleteFboLater) { - delete m_fbo; - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - format.setInternalTextureFormat(m_format); - format.setMipmap(m_mipmap); - m_fbo = new QOpenGLFramebufferObject(m_size, format); - glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); - updateBindOptions(true); - } - qSwap(m_fbo, m_secondaryFbo); - } else { - m_renderer->renderScene(QSGBindableFbo(m_fbo)); - } - } - - if (m_mipmap) { - glBindTexture(GL_TEXTURE_2D, textureId()); - ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); - } - - root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update. - -#ifdef QSG_DEBUG_FBO_OVERLAY - if (qmlFboOverlay()) - root->removeChildNode(m_debugOverlay); -#endif - if (m_recursive) - markDirtyTexture(); // Continuously update if 'live' and 'recursive'. -} - -QImage QSGShaderEffectTexture::toImage() const -{ - if (m_fbo) - return m_fbo->toImage(); - - return QImage(); -} - -/*! - \qmlclass ShaderEffectSource QSGShaderEffectSource - \since 5.0 - \ingroup qml-basic-visual-elements - \brief The ShaderEffectSource element renders a QML element into a texture - and displays it. - \inherits Item - - The ShaderEffectSource element renders \l sourceItem into a texture and - displays it in the scene. \l sourceItem is drawn into the texture as though - it was a fully opaque root element. Thus \l sourceItem itself can be - invisible, but still appear in the texture. - - ShaderEffectSource can be used as: - \list - \o a texture source in a \l ShaderEffect. - This allows you to apply custom shader effects to any QML element. - \o a cache for a complex element. - The complex element can be rendered once into the texture, which can - then be animated freely without the need to render the complex element - again every frame. - \o an opacity layer. - ShaderEffectSource allows you to apply an opacity to elements as a group - rather than each element individually. - \endlist - - \table - \row - \o \image declarative-shadereffectsource.png - \o \qml - import QtQuick 2.0 - - Rectangle { - width: 200 - height: 100 - gradient: Gradient { - GradientStop { position: 0; color: "white" } - GradientStop { position: 1; color: "black" } - } - Row { - opacity: 0.5 - Item { - id: foo - width: 100; height: 100 - Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" } - Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" } - Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" } - } - ShaderEffectSource { - width: 100; height: 100 - sourceItem: foo - } - } - } - \endqml - \endrow - \endtable - - The ShaderEffectSource element does not redirect any mouse or keyboard - input to \l sourceItem. If you hide the \l sourceItem by setting - \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero, - it will no longer react to input. In cases where the ShaderEffectSource is - meant to replace the \l sourceItem, you typically want to hide the - \l sourceItem while still handling input. For this, you can use - the \l hideSource property. - - \note If \l sourceItem is a \l Rectangle with border, by default half the - border width falls outside the texture. To get the whole border, you can - extend the \l sourceRect. - - \warning In most cases, using a ShaderEffectSource will decrease - performance, and in all cases, it will increase video memory usage. - Rendering through a ShaderEffectSource might also lead to lower quality - since some OpenGL implementations support multisampled backbuffer, - but not multisampled framebuffer objects. -*/ - -QSGShaderEffectSource::QSGShaderEffectSource(QSGItem *parent) - : QSGItem(parent) - , m_provider(0) - , m_texture(0) - , m_wrapMode(ClampToEdge) - , m_sourceItem(0) - , m_textureSize(0, 0) - , m_format(RGBA) - , m_live(true) - , m_hideSource(false) - , m_mipmap(false) - , m_recursive(false) - , m_grab(true) -{ - setFlag(ItemHasContents); -} - -QSGShaderEffectSource::~QSGShaderEffectSource() -{ - if (m_texture) - m_texture->deleteLater(); - - if (m_provider) - m_provider->deleteLater(); - - if (m_sourceItem) - QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource); -} - -void QSGShaderEffectSource::ensureTexture() -{ - if (m_texture) - return; - - Q_ASSERT_X(QSGItemPrivate::get(this)->canvas - && QSGItemPrivate::get(this)->sceneGraphContext() - && QThread::currentThread() == QSGItemPrivate::get(this)->sceneGraphContext()->thread(), - "QSGShaderEffectSource::ensureTexture", - "Cannot be used outside the rendering thread"); - - m_texture = new QSGShaderEffectTexture(this); - connect(m_texture, SIGNAL(textureChanged()), this, SLOT(update())); -} - -QSGTextureProvider *QSGShaderEffectSource::textureProvider() const -{ - if (!m_provider) { - // Make sure it gets thread affinity on the rendering thread so deletion works properly.. - Q_ASSERT_X(QSGItemPrivate::get(this)->canvas - && QSGItemPrivate::get(this)->sceneGraphContext() - && QThread::currentThread() == QSGItemPrivate::get(this)->sceneGraphContext()->thread(), - "QSGShaderEffectSource::textureProvider", - "Cannot be used outside the rendering thread"); - const_cast(this)->m_provider = new QSGShaderEffectSourceTextureProvider(); - - const_cast(this)->ensureTexture(); - connect(m_texture, SIGNAL(textureChanged()), m_provider, SIGNAL(textureChanged()), Qt::DirectConnection); - m_provider->sourceTexture = m_texture; - } - return m_provider; -} - -/*! - \qmlproperty enumeration ShaderEffectSource::wrapMode - - This property defines the OpenGL wrap modes associated with the texture. - Modifying this property makes most sense when the element is used as a - source texture of a \l ShaderEffect. - - \list - \o ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically - \o ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically - \o ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically - \o ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically - \endlist - - \note Some OpenGL ES 2 implementations do not support the GL_REPEAT - wrap mode with non-power-of-two textures. -*/ - -QSGShaderEffectSource::WrapMode QSGShaderEffectSource::wrapMode() const -{ - return m_wrapMode; -} - -void QSGShaderEffectSource::setWrapMode(WrapMode mode) -{ - if (mode == m_wrapMode) - return; - m_wrapMode = mode; - update(); - emit wrapModeChanged(); -} - -/*! - \qmlproperty Item ShaderEffectSource::sourceItem - - This property holds the element to be rendered into the texture. -*/ - -QSGItem *QSGShaderEffectSource::sourceItem() const -{ - return m_sourceItem; -} - -void QSGShaderEffectSource::setSourceItem(QSGItem *item) -{ - if (item == m_sourceItem) - return; - if (m_sourceItem) - QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource); - m_sourceItem = item; - if (m_sourceItem) { - // TODO: Find better solution. - // 'm_sourceItem' needs a canvas to get a scenegraph node. - // The easiest way to make sure it gets a canvas is to - // make it a part of the same item tree as 'this'. - if (m_sourceItem->parentItem() == 0) { - m_sourceItem->setParentItem(this); - m_sourceItem->setVisible(false); - } - QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(m_hideSource); - } - update(); - emit sourceItemChanged(); -} - -/*! - \qmlproperty rect ShaderEffectSource::sourceRect - - This property defines which rectangular area of the \l sourceItem to - render into the texture. The source rectangle can be larger than - \l sourceItem itself. If the rectangle is null, which is the default, - the whole \l sourceItem is rendered to texture. -*/ - -QRectF QSGShaderEffectSource::sourceRect() const -{ - return m_sourceRect; -} - -void QSGShaderEffectSource::setSourceRect(const QRectF &rect) -{ - if (rect == m_sourceRect) - return; - m_sourceRect = rect; - update(); - emit sourceRectChanged(); -} - -/*! - \qmlproperty size ShaderEffectSource::textureSize - - This property holds the requested size of the texture. If it is empty, - which is the default, the size of the source rectangle is used. - - \note Some platforms have a limit on how small framebuffer objects can be, - which means the actual texture size might be larger than the requested - size. -*/ - -QSize QSGShaderEffectSource::textureSize() const -{ - return m_textureSize; -} - -void QSGShaderEffectSource::setTextureSize(const QSize &size) -{ - if (size == m_textureSize) - return; - m_textureSize = size; - update(); - emit textureSizeChanged(); -} - -/*! - \qmlproperty enumeration ShaderEffectSource::format - - This property defines the internal OpenGL format of the texture. - Modifying this property makes most sense when the element is used as a - source texture of a \l ShaderEffect. Depending on the OpenGL - implementation, this property might allow you to save some texture memory. - - \list - \o ShaderEffectSource.Alpha - GL_ALPHA - \o ShaderEffectSource.RGB - GL_RGB - \o ShaderEffectSource.RGBA - GL_RGBA - \endlist - - \note Some OpenGL implementations do not support the GL_ALPHA format. -*/ - -QSGShaderEffectSource::Format QSGShaderEffectSource::format() const -{ - return m_format; -} - -void QSGShaderEffectSource::setFormat(QSGShaderEffectSource::Format format) -{ - if (format == m_format) - return; - m_format = format; - update(); - emit formatChanged(); -} - -/*! - \qmlproperty bool ShaderEffectSource::live - - If this property is true, the texture is updated whenever the - \l sourceItem changes. Otherwise, it will be a frozen image of the - \l sourceItem. The property is true by default. -*/ - -bool QSGShaderEffectSource::live() const -{ - return m_live; -} - -void QSGShaderEffectSource::setLive(bool live) -{ - if (live == m_live) - return; - m_live = live; - update(); - emit liveChanged(); -} - -/*! - \qmlproperty bool ShaderEffectSource::hideSource - - If this property is true, the \l sourceItem is hidden, though it will still - be rendered into the texture. As opposed to hiding the \l sourceItem by - setting \l{Item::visible}{visible} to false, setting this property to true - will not prevent mouse or keyboard input from reaching \l sourceItem. - The property is useful when the ShaderEffectSource is anchored on top of, - and meant to replace the \l sourceItem. -*/ - -bool QSGShaderEffectSource::hideSource() const -{ - return m_hideSource; -} - -void QSGShaderEffectSource::setHideSource(bool hide) -{ - if (hide == m_hideSource) - return; - if (m_sourceItem) { - QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(hide); - QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource); - } - m_hideSource = hide; - update(); - emit hideSourceChanged(); -} - -/*! - \qmlproperty bool ShaderEffectSource::mipmap - - If this property is true, mipmaps are generated for the texture. - - \note Some OpenGL ES 2 implementations do not support mipmapping of - non-power-of-two textures. -*/ - -bool QSGShaderEffectSource::mipmap() const -{ - return m_mipmap; -} - -void QSGShaderEffectSource::setMipmap(bool enabled) -{ - if (enabled == m_mipmap) - return; - m_mipmap = enabled; - update(); - emit mipmapChanged(); -} - -/*! - \qmlproperty bool ShaderEffectSource::recursive - - Set this property to true if the ShaderEffectSource has a dependency on - itself. ShaderEffectSources form a dependency chain, where one - ShaderEffectSource can be part of the \l sourceItem of another. - If there is a loop in this chain, a ShaderEffectSource could end up trying - to render into the same texture it is using as source, which is not allowed - by OpenGL. When this property is set to true, an extra texture is allocated - so that ShaderEffectSource can keep a copy of the texture from the previous - frame. It can then render into one texture and use the texture from the - previous frame as source. - - Setting both this property and \l live to true will cause the scene graph - to render continuously. Since the ShaderEffectSource depends on itself, - updating it means that it immediately becomes dirty again. -*/ - -bool QSGShaderEffectSource::recursive() const -{ - return m_recursive; -} - -void QSGShaderEffectSource::setRecursive(bool enabled) -{ - if (enabled == m_recursive) - return; - m_recursive = enabled; - emit recursiveChanged(); -} - -/*! - \qmlmethod ShaderEffectSource::scheduleUpdate() - - Schedules a re-rendering of the texture for the next frame. - Use this to update the texture when \l live is false. -*/ - -void QSGShaderEffectSource::scheduleUpdate() -{ - if (m_grab) - return; - m_grab = true; - update(); -} - -static void get_wrap_mode(QSGShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap) -{ - switch (mode) { - case QSGShaderEffectSource::RepeatHorizontally: - *hWrap = QSGTexture::Repeat; - *vWrap = QSGTexture::ClampToEdge; - break; - case QSGShaderEffectSource::RepeatVertically: - *vWrap = QSGTexture::Repeat; - *hWrap = QSGTexture::ClampToEdge; - break; - case QSGShaderEffectSource::Repeat: - *hWrap = *vWrap = QSGTexture::Repeat; - break; - default: - // QSGShaderEffectSource::ClampToEdge - *hWrap = *vWrap = QSGTexture::ClampToEdge; - break; - } -} - - -QSGNode *QSGShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) { - delete oldNode; - return 0; - } - - ensureTexture(); - - QSGShaderEffectTexture *tex = qobject_cast(m_texture); - tex->setLive(m_live); - tex->setItem(QSGItemPrivate::get(m_sourceItem)->itemNode()); - QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0 - ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height()) - : m_sourceRect; - tex->setRect(sourceRect); - QSize textureSize = m_textureSize.isEmpty() - ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height()))) - : m_textureSize; - Q_ASSERT(!textureSize.isEmpty()); - QSGItemPrivate *d = static_cast(QObjectPrivate::get(this)); - const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize(); - // Keep power-of-two by doubling the size. - while (textureSize.width() < minTextureSize.width()) - textureSize.rwidth() *= 2; - while (textureSize.height() < minTextureSize.height()) - textureSize.rheight() *= 2; - - tex->setSize(textureSize); - tex->setRecursive(m_recursive); - tex->setFormat(GLenum(m_format)); - tex->setHasMipmaps(m_mipmap); - - if (m_grab) - tex->scheduleUpdate(); - m_grab = false; - - QSGTexture::Filtering filtering = QSGItemPrivate::get(this)->smooth - ? QSGTexture::Linear - : QSGTexture::Nearest; - QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None; - QSGTexture::WrapMode hWrap, vWrap; - get_wrap_mode(m_wrapMode, &hWrap, &vWrap); - - if (m_provider) { - m_provider->mipmapFiltering = mmFiltering; - m_provider->filtering = filtering; - m_provider->horizontalWrap = hWrap; - m_provider->verticalWrap = vWrap; - } - - // Don't create the paint node if we're not spanning any area - if (width() == 0 || height() == 0) { - delete oldNode; - return 0; - } - - QSGShaderEffectSourceNode *node = static_cast(oldNode); - if (!node) { - node = new QSGShaderEffectSourceNode; - node->setTexture(m_texture); - connect(m_texture, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); - } - - // If live and recursive, update continuously. - if (m_live && m_recursive) - node->markDirty(QSGNode::DirtyMaterial); - - node->setMipmapFiltering(mmFiltering); - node->setFiltering(filtering); - node->setHorizontalWrapMode(hWrap); - node->setVerticalWrapMode(vWrap); - node->setTargetRect(QRectF(0, 0, width(), height())); - node->setSourceRect(QRectF(0, 0, 1, 1)); - node->update(); - - return node; -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgshadereffectsource_p.h b/src/declarative/items/qsgshadereffectsource_p.h deleted file mode 100644 index 5d65b26cca..0000000000 --- a/src/declarative/items/qsgshadereffectsource_p.h +++ /dev/null @@ -1,255 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SHADEREFFECTSOURCE_H -#define SHADEREFFECTSOURCE_H - -#include "qsgitem.h" -#include -#include -#include -#include - -#include "qpointer.h" -#include "qsize.h" -#include "qrect.h" - -#define QSG_DEBUG_FBO_OVERLAY - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGNode; -class UpdatePaintNodeData; -class QOpenGLFramebufferObject; - -class QSGShaderEffectSourceTextureProvider; - -class QSGShaderEffectSourceNode : public QObject, public QSGDefaultImageNode -{ - Q_OBJECT - -public: - QSGShaderEffectSourceNode(); - -private Q_SLOTS: - void markDirtyTexture(); -}; - -class Q_DECLARATIVE_EXPORT QSGShaderEffectTexture : public QSGDynamicTexture -{ - Q_OBJECT -public: - QSGShaderEffectTexture(QSGItem *shaderSource); - ~QSGShaderEffectTexture(); - - virtual bool updateTexture(); - - // The item's "paint node", not effect node. - QSGNode *item() const { return m_item; } - void setItem(QSGNode *item); - - QRectF rect() const { return m_rect; } - void setRect(const QRectF &rect); - - QSize size() const { return m_size; } - void setSize(const QSize &size); - - void setHasMipmaps(bool mipmap); - - void bind(); - - bool hasAlphaChannel() const; - bool hasMipmaps() const; - int textureId() const; - QSize textureSize() const { return m_size; } - - GLenum format() const { return m_format; } - void setFormat(GLenum format); - - bool live() const { return bool(m_live); } - void setLive(bool live); - - bool recursive() const { return bool(m_recursive); } - void setRecursive(bool recursive); - - void scheduleUpdate(); - - QImage toImage() const; - -Q_SIGNALS: - void textureChanged(); - -public Q_SLOTS: - void markDirtyTexture(); - -private: - void grab(); - - QSGNode *m_item; - QRectF m_rect; - QSize m_size; - GLenum m_format; - - QSGItem *m_shaderSource; - QSGRenderer *m_renderer; - QOpenGLFramebufferObject *m_fbo; - QOpenGLFramebufferObject *m_secondaryFbo; - -#ifdef QSG_DEBUG_FBO_OVERLAY - QSGRectangleNode *m_debugOverlay; -#endif - - QSGContext *m_context; - - uint m_mipmap : 1; - uint m_live : 1; - uint m_recursive : 1; - uint m_dirtyTexture : 1; - uint m_multisamplingSupportChecked : 1; - uint m_multisampling : 1; - uint m_grab : 1; -}; - -class Q_DECLARATIVE_EXPORT QSGShaderEffectSource : public QSGItem -{ - Q_OBJECT - Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) - Q_PROPERTY(QSGItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged) - Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged) - Q_PROPERTY(QSize textureSize READ textureSize WRITE setTextureSize NOTIFY textureSizeChanged) - Q_PROPERTY(Format format READ format WRITE setFormat NOTIFY formatChanged) - Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged) - Q_PROPERTY(bool hideSource READ hideSource WRITE setHideSource NOTIFY hideSourceChanged) - Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged) - Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged) - - Q_ENUMS(Format WrapMode) -public: - enum WrapMode { - ClampToEdge, - RepeatHorizontally, - RepeatVertically, - Repeat - }; - - enum Format { - Alpha = GL_ALPHA, - RGB = GL_RGB, - RGBA = GL_RGBA - }; - - QSGShaderEffectSource(QSGItem *parent = 0); - ~QSGShaderEffectSource(); - - WrapMode wrapMode() const; - void setWrapMode(WrapMode mode); - - QSGItem *sourceItem() const; - void setSourceItem(QSGItem *item); - - QRectF sourceRect() const; - void setSourceRect(const QRectF &rect); - - QSize textureSize() const; - void setTextureSize(const QSize &size); - - Format format() const; - void setFormat(Format format); - - bool live() const; - void setLive(bool live); - - bool hideSource() const; - void setHideSource(bool hide); - - bool mipmap() const; - void setMipmap(bool enabled); - - bool recursive() const; - void setRecursive(bool enabled); - - bool isTextureProvider() const { return true; } - QSGTextureProvider *textureProvider() const; - - Q_INVOKABLE void scheduleUpdate(); - -Q_SIGNALS: - void wrapModeChanged(); - void sourceItemChanged(); - void sourceRectChanged(); - void textureSizeChanged(); - void formatChanged(); - void liveChanged(); - void hideSourceChanged(); - void mipmapChanged(); - void recursiveChanged(); - - void textureChanged(); - -protected: - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - -private: - void ensureTexture(); - - QSGShaderEffectSourceTextureProvider *m_provider; - QSGShaderEffectTexture *m_texture; - WrapMode m_wrapMode; - QPointer m_sourceItem; - QRectF m_sourceRect; - QSize m_textureSize; - Format m_format; - uint m_live : 1; - uint m_hideSource : 1; - uint m_mipmap : 1; - uint m_recursive : 1; - uint m_grab : 1; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SHADEREFFECTSOURCE_H diff --git a/src/declarative/items/qsgsprite.cpp b/src/declarative/items/qsgsprite.cpp deleted file mode 100644 index afd32a651f..0000000000 --- a/src/declarative/items/qsgsprite.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgsprite_p.h" -#include - -QT_BEGIN_NAMESPACE - -/*! - \qmlclass Sprite QSGSprite - \inqmlmodule QtQuick 2 - \brief The Sprite element represents a sprite animation - -*/ -/*! - \qmlproperty int QtQuick2::Sprite::duration - - Time between frames. -*/ -/*! - \qmlproperty int QtQuick2::Sprite::durationVariation - - The time between frames can vary by up to this amount. - - Default is 0. -*/ - -/*! - \qmlproperty string QtQuick2::Sprite::name - - The name of this sprite, for use in the to property of other sprites. -*/ -/*! - \qmlproperty QVariantMap QtQuick2::Sprite::to - - A list of other sprites and weighted transitions to them, - for example {"a":1, "b":2, "c":0} would specify that one-third should - transition to sprite "a" when this sprite is done, and two-thirds should - transition to sprite "b" when this sprite is done. As the transitions are - chosen randomly, these proportions will not be exact. With "c":0 in the list, - no sprites will randomly transition to "c", but it wll be a valid path if a sprite - goal is set. - - If no list is specified, or the sum of weights in the list is zero, then the sprite - will repeat itself after completing. -*/ -/*! - \qmlproperty int QtQuick2::Sprite::frames - - Number of frames in this sprite. -*/ -/*! - \qmlproperty int QtQuick2::Sprite::frameHeight - - Height of a single frame in this sprite. -*/ -/*! - \qmlproperty int QtQuick2::Sprite::frameWidth - - Width of a single frame in this sprite. -*/ -/*! - \qmlproperty url QtQuick2::Sprite::source - - The image source for the animation. - - If frameHeight and frameWidth are not specified, it is assumed to be a single long row of square frames. - Otherwise, it can be multiple contiguous rows or rectangluar frames, when one row runs out the next will be used. -*/ - Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) - Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged) - Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged) - Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged) - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - //If frame height or width is not specified, it is assumed to be a single long row of square frames. - //Otherwise, it can be multiple contiguous rows, when one row runs out the next will be used. - Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged) - Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged) - -QSGSprite::QSGSprite(QObject *parent) : - QSGStochasticState(parent) - , m_generatedCount(0) - , m_framesPerRow(0) - , m_frameHeight(0) - , m_frameWidth(0) - , m_rowY(0) -{ -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgsprite_p.h b/src/declarative/items/qsgsprite_p.h deleted file mode 100644 index 58b6c13f97..0000000000 --- a/src/declarative/items/qsgsprite_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SPRITESTATE_H -#define SPRITESTATE_H - -#include -#include -#include -#include -#include "qsgspriteengine_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - - -class QSGSprite : public QSGStochasticState -{ - Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - //If frame height or width is not specified, it is assumed to be a single long row of square frames. - //Otherwise, it can be multiple contiguous rows, when one row runs out the next will be used. - Q_PROPERTY(int frameHeight READ frameHeight WRITE setFrameHeight NOTIFY frameHeightChanged) - Q_PROPERTY(int frameWidth READ frameWidth WRITE setFrameWidth NOTIFY frameWidthChanged) - -public: - explicit QSGSprite(QObject *parent = 0); - - QUrl source() const - { - return m_source; - } - - int frameHeight() const - { - return m_frameHeight; - } - - int frameWidth() const - { - return m_frameWidth; - } - - -signals: - - void sourceChanged(QUrl arg); - - void frameHeightChanged(int arg); - - void frameWidthChanged(int arg); - -public slots: - - void setSource(QUrl arg) - { - if (m_source != arg) { - m_source = arg; - emit sourceChanged(arg); - } - } - - void setFrameHeight(int arg) - { - if (m_frameHeight != arg) { - m_frameHeight = arg; - emit frameHeightChanged(arg); - } - } - - void setFrameWidth(int arg) - { - if (m_frameWidth != arg) { - m_frameWidth = arg; - emit frameWidthChanged(arg); - } - } - - -private: - friend class QSGImageParticle; - friend class QSGSpriteEngine; - friend class QSGStochasticEngine; - int m_generatedCount; - int m_framesPerRow; - QUrl m_source; - int m_frameHeight; - int m_frameWidth; - int m_rowY; - -}; - -QT_END_NAMESPACE -QT_END_HEADER -#endif // SPRITESTATE_H diff --git a/src/declarative/items/qsgspriteengine.cpp b/src/declarative/items/qsgspriteengine.cpp deleted file mode 100644 index a376a06483..0000000000 --- a/src/declarative/items/qsgspriteengine.cpp +++ /dev/null @@ -1,500 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgspriteengine_p.h" -#include "qsgsprite_p.h" -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -/* TODO: Split out image logic from stochastic state logic - Also make sharable - Also solve the state data initialization/transfer issue so as to not need to make friends -*/ - -QSGStochasticEngine::QSGStochasticEngine(QObject *parent) : - QObject(parent), m_timeOffset(0) -{ - //Default size 1 - setCount(1); - m_advanceTime.start(); -} - -QSGStochasticEngine::QSGStochasticEngine(QList states, QObject *parent) : - QObject(parent), m_states(states), m_timeOffset(0) -{ - //Default size 1 - setCount(1); - m_advanceTime.start(); -} - -QSGStochasticEngine::~QSGStochasticEngine() -{ -} - -QSGSpriteEngine::QSGSpriteEngine(QObject *parent) - : QSGStochasticEngine(parent) -{ -} - -QSGSpriteEngine::QSGSpriteEngine(QList sprites, QObject *parent) - : QSGStochasticEngine(parent) -{ - foreach (QSGSprite* sprite, sprites) - m_states << (QSGStochasticState*)sprite; -} - -QSGSpriteEngine::~QSGSpriteEngine() -{ -} - - -int QSGSpriteEngine::maxFrames() -{ - return m_maxFrames; -} - -/* States too large to fit in one row are split into multiple rows - This is more efficient for the implementation, but should remain an implementation detail (invisible from QML) - Therefore the below functions abstract sprite from the viewpoint of classes that pass the details onto shaders - But States maintain their listed index for internal structures -TODO: All these calculations should be pre-calculated and cached during initialization for a significant performance boost -TODO: Above idea needs to have the varying duration offset added to it -*/ -int QSGSpriteEngine::spriteState(int sprite) -{ - int state = m_things[sprite]; - if (!m_sprites[state]->m_generatedCount) - return state; - int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; - int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; - return state + extra; -} - -int QSGSpriteEngine::spriteStart(int sprite) -{ - int state = m_things[sprite]; - if (!m_sprites[state]->m_generatedCount) - return m_startTimes[sprite]; - int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; - int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; - return state + extra*rowDuration; -} - -int QSGSpriteEngine::spriteFrames(int sprite) -{ - int state = m_things[sprite]; - if (!m_sprites[state]->m_generatedCount) - return m_sprites[state]->frames(); - int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; - int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; - if (extra == m_sprites[state]->m_generatedCount - 1)//last state - return m_sprites[state]->frames() % m_sprites[state]->m_framesPerRow; - else - return m_sprites[state]->m_framesPerRow; -} - -int QSGSpriteEngine::spriteDuration(int sprite) -{ - int state = m_things[sprite]; - if (!m_sprites[state]->m_generatedCount) - return m_duration[sprite]; - int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; - int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; - if (extra == m_sprites[state]->m_generatedCount - 1)//last state - return (m_duration[sprite] * m_sprites[state]->frames()) % rowDuration; - else - return rowDuration; -} - -int QSGSpriteEngine::spriteY(int sprite) -{ - int state = m_things[sprite]; - if (!m_sprites[state]->m_generatedCount) - return m_sprites[state]->m_rowY; - int rowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow; - int extra = (m_timeOffset - m_startTimes[sprite])/rowDuration; - return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra; -} - -int QSGSpriteEngine::spriteWidth(int sprite) -{ - int state = m_things[sprite]; - return m_sprites[state]->m_frameWidth; -} - -int QSGSpriteEngine::spriteHeight(int sprite) -{ - int state = m_things[sprite]; - return m_sprites[state]->m_frameHeight; -} - -int QSGSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together -{ - return m_imageStateCount; -} - -void QSGStochasticEngine::setGoal(int state, int sprite, bool jump) -{ - if (sprite >= m_things.count() || state >= m_states.count()) - return; - if (!jump){ - m_goals[sprite] = state; - return; - } - - if (m_things[sprite] == state) - return;//Already there - m_things[sprite] = state; - m_duration[sprite] = m_states[state]->variedDuration(); - m_goals[sprite] = -1; - restart(sprite); - emit stateChanged(sprite); - emit m_states[state]->entered(); - return; -} - -QImage QSGSpriteEngine::assembledImage() -{ - int h = 0; - int w = 0; - m_maxFrames = 0; - m_imageStateCount = 0; - int maxSize = 0; - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize); - foreach (QSGStochasticState* s, m_states){ - QSGSprite* sprite = qobject_cast(s); - if (sprite) - m_sprites << sprite; - else - qDebug() << "Error: Non-sprite in QSGSpriteEngine"; - } - - foreach (QSGSprite* state, m_sprites){ - if (state->frames() > m_maxFrames) - m_maxFrames = state->frames(); - - QImage img(state->source().toLocalFile()); - if (img.isNull()) { - qWarning() << "SpriteEngine: loading image failed..." << state->source().toLocalFile(); - return QImage(); - } - - //Check that the frame sizes are the same within one engine - if (!state->m_frameWidth) - state->m_frameWidth = img.width() / state->frames(); - - if (!state->m_frameHeight) - state->m_frameHeight = img.height(); - - if (state->frames() * state->frameWidth() > maxSize){ - struct helper{ - static int divRoundUp(int a, int b){return (a+b-1)/b;} - }; - int rowsNeeded = helper::divRoundUp(state->frames(), helper::divRoundUp(maxSize, state->frameWidth())); - if (rowsNeeded * state->frameHeight() > maxSize){ - qWarning() << "SpriteEngine: Animation too large to fit in one texture..." << state->source().toLocalFile(); - qWarning() << "SpriteEngine: Your texture max size today is " << maxSize; - } - state->m_generatedCount = rowsNeeded; - h += state->frameHeight() * rowsNeeded; - w = qMax(w, helper::divRoundUp(maxSize, state->frameWidth())); - m_imageStateCount += rowsNeeded; - }else{ - h += state->frameHeight(); - w = qMax(w, state->frameWidth() * state->frames()); - m_imageStateCount++; - } - } - - //maxFrames is max number in a line of the texture - QImage image(w, h, QImage::Format_ARGB32); - image.fill(0); - QPainter p(&image); - int y = 0; - foreach (QSGSprite* state, m_sprites){ - QImage img(state->source().toLocalFile()); - int frameWidth = state->m_frameWidth; - int frameHeight = state->m_frameHeight; - if (img.height() == frameHeight && img.width() < maxSize){//Simple case - p.drawImage(0,y,img); - state->m_rowY = y; - y += frameHeight; - }else{//Chopping up image case - state->m_framesPerRow = image.width()/frameWidth; - state->m_rowY = y; - int x = 0; - int curX = 0; - int curY = 0; - int framesLeft = state->frames(); - while (framesLeft > 0){ - if (image.width() - x + curX <= img.width()){//finish a row in image (dest) - int copied = image.width() - x; - Q_ASSERT(!(copied % frameWidth));//XXX: Just checking - framesLeft -= copied/frameWidth; - p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight)); - y += frameHeight; - curX += copied; - x = 0; - if (curX == img.width()){ - curX = 0; - curY += frameHeight; - } - }else{//finish a row in img (src) - int copied = img.width() - curX; - Q_ASSERT(!(copied % frameWidth));//XXX: Just checking - framesLeft -= copied/frameWidth; - p.drawImage(x,y,img.copy(curX,curY,copied,frameHeight)); - curY += frameHeight; - x += copied; - curX = 0; - } - } - if (x) - y += frameHeight; - } - } - - if (image.height() > maxSize){ - qWarning() << "SpriteEngine: Too many animations to fit in one texture..."; - qWarning() << "SpriteEngine: Your texture max size today is " << maxSize; - return QImage(); - } - return image; -} - -void QSGStochasticEngine::setCount(int c) -{ - m_things.resize(c); - m_goals.resize(c); - m_duration.resize(c); - m_startTimes.resize(c); -} - -void QSGStochasticEngine::start(int index, int state) -{ - if (index >= m_things.count()) - return; - m_things[index] = state; - m_duration[index] = m_states[state]->variedDuration(); - m_goals[index] = -1; - restart(index); -} - -void QSGStochasticEngine::stop(int index) -{ - if (index >= m_things.count()) - return; - //Will never change until start is called again with a new state - this is not a 'pause' - for (int i=0; iframes() + m_startTimes[index]; - for (int i=0; i changedIndexes; - while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){ - foreach (int idx, m_stateUpdates.first().second){ - if (idx >= m_things.count()) - continue;//TODO: Proper fix(because this does happen and I'm just ignoring it) - int stateIdx = m_things[idx]; - int nextIdx = -1; - int goalPath = goalSeek(stateIdx, idx); - if (goalPath == -1){//Random - qreal r =(qreal) qrand() / (qreal) RAND_MAX; - qreal total = 0.0; - for (QVariantMap::const_iterator iter=m_states[stateIdx]->m_to.constBegin(); - iter!=m_states[stateIdx]->m_to.constEnd(); iter++) - total += (*iter).toReal(); - r*=total; - for (QVariantMap::const_iterator iter= m_states[stateIdx]->m_to.constBegin(); - iter!=m_states[stateIdx]->m_to.constEnd(); iter++){ - if (r < (*iter).toReal()){ - bool superBreak = false; - for (int i=0; iname() == iter.key()){ - nextIdx = i; - superBreak = true; - break; - } - } - if (superBreak) - break; - } - r -= (*iter).toReal(); - } - }else{//Random out of shortest paths to goal - nextIdx = goalPath; - } - if (nextIdx == -1)//No to states means stay here - nextIdx = stateIdx; - - m_things[idx] = nextIdx; - m_duration[idx] = m_states[nextIdx]->variedDuration(); - m_startTimes[idx] = time; - if (nextIdx != stateIdx){ - changedIndexes << idx; - emit m_states[nextIdx]->entered(); - } - addToUpdateList((m_duration[idx] * m_states[nextIdx]->frames()) + time, idx); - } - m_stateUpdates.pop_front(); - } - - m_timeOffset = time; - m_advanceTime.start(); - //TODO: emit this when a psuedostate changes too - foreach (int idx, changedIndexes){//Batched so that update list doesn't change midway - emit stateChanged(idx); - } - if (m_stateUpdates.isEmpty()) - return -1; - return m_stateUpdates.first().first; -} - -int QSGStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist) -{ - QString goalName; - if (m_goals[spriteIdx] != -1) - goalName = m_states[m_goals[spriteIdx]]->name(); - else - goalName = m_globalGoal; - if (goalName.isEmpty()) - return -1; - //TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitarily anyways) - // Paraphrased - implement in an *efficient* manner - for (int i=0; iname() == goalName) - return curIdx; - if (dist < 0) - dist = m_states.count(); - QSGStochasticState* curState = m_states[curIdx]; - for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); - iter!=curState->m_to.constEnd(); iter++){ - if (iter.key() == goalName) - for (int i=0; iname() == goalName) - return i; - } - QSet options; - for (int i=1; im_to.constBegin(); - iter!=curState->m_to.constEnd(); iter++){ - int option = -1; - for (int j=0; jname() == iter.key()) - if (goalSeek(j, spriteIdx, i) != -1) - option = j; - if (option != -1) - options << option; - } - if (!options.isEmpty()){ - if (options.count()==1) - return *(options.begin()); - int option = -1; - qreal r =(qreal) qrand() / (qreal) RAND_MAX; - qreal total = 0; - for (QSet::const_iterator iter=options.constBegin(); - iter!=options.constEnd(); iter++) - total += curState->m_to.value(m_states[(*iter)]->name()).toReal(); - r *= total; - for (QVariantMap::const_iterator iter = curState->m_to.constBegin(); - iter!=curState->m_to.constEnd(); iter++){ - bool superContinue = true; - for (int j=0; jname() == iter.key()) - if (options.contains(j)) - superContinue = false; - if (superContinue) - continue; - if (r < (*iter).toReal()){ - bool superBreak = false; - for (int j=0; jname() == iter.key()){ - option = j; - superBreak = true; - break; - } - } - if (superBreak) - break; - } - r-=(*iter).toReal(); - } - return option; - } - } - return -1; -} - -void QSGStochasticEngine::addToUpdateList(uint t, int idx) -{ - for (int i=0; i t){ - QList tmpList; - tmpList << idx; - m_stateUpdates.insert(i, qMakePair(t, tmpList)); - return; - } - } - QList tmpList; - tmpList << idx; - m_stateUpdates << qMakePair(t, tmpList); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgspriteengine_p.h b/src/declarative/items/qsgspriteengine_p.h deleted file mode 100644 index 8140b3814d..0000000000 --- a/src/declarative/items/qsgspriteengine_p.h +++ /dev/null @@ -1,318 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SPRITEENGINE_H -#define SPRITEENGINE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGSprite; -class Q_AUTOTEST_EXPORT QSGStochasticState : public QObject //For internal use -{ - Q_OBJECT - Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) - Q_PROPERTY(int durationVariation READ durationVariance WRITE setDurationVariance NOTIFY durationVarianceChanged) - Q_PROPERTY(QVariantMap to READ to WRITE setTo NOTIFY toChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(qreal speedModifiesDuration READ speedModifer WRITE setSpeedModifier NOTIFY speedModifierChanged) - Q_PROPERTY(int frames READ frames WRITE setFrames NOTIFY framesChanged) - -public: - QSGStochasticState(QObject* parent = 0) - : QObject(parent) - , m_frames(1) - , m_duration(1000) - { - } - - int duration() const - { - return m_duration; - } - - QString name() const - { - return m_name; - } - - QVariantMap to() const - { - return m_to; - } - - qreal speedModifer() const - { - return m_speedModifier; - } - - int durationVariance() const - { - return m_durationVariance; - } - - - int variedDuration() const - { - return m_duration - + (m_durationVariance * ((qreal)qrand()/RAND_MAX) * 2) - - m_durationVariance; - } - - int frames() const - { - return m_frames; - } - -signals: - void durationChanged(int arg); - - void nameChanged(QString arg); - - void toChanged(QVariantMap arg); - - void speedModifierChanged(qreal arg); - - void durationVarianceChanged(int arg); - - void entered();//### Just playing around - don't expect full state API - void framesChanged(int arg); - -public slots: - void setDuration(int arg) - { - if (m_duration != arg) { - m_duration = arg; - emit durationChanged(arg); - } - } - - void setName(QString arg) - { - if (m_name != arg) { - m_name = arg; - emit nameChanged(arg); - } - } - - void setTo(QVariantMap arg) - { - if (m_to != arg) { - m_to = arg; - emit toChanged(arg); - } - } - - void setSpeedModifier(qreal arg) - { - if (m_speedModifier != arg) { - m_speedModifier = arg; - emit speedModifierChanged(arg); - } - } - - void setDurationVariance(int arg) - { - if (m_durationVariance != arg) { - m_durationVariance = arg; - emit durationVarianceChanged(arg); - } - } - - void setFrames(int arg) - { - if (m_frames != arg) { - m_frames = arg; - emit framesChanged(arg); - } - } - -private: - QString m_name; - int m_frames; - QVariantMap m_to; - int m_duration; - qreal m_speedModifier; - int m_durationVariance; - - friend class QSGStochasticEngine; -}; - -class Q_AUTOTEST_EXPORT QSGStochasticEngine : public QObject -{ - Q_OBJECT - //TODO: Optimize single state case? - Q_PROPERTY(QString globalGoal READ globalGoal WRITE setGlobalGoal NOTIFY globalGoalChanged) - Q_PROPERTY(QDeclarativeListProperty states READ states) -public: - explicit QSGStochasticEngine(QObject *parent = 0); - QSGStochasticEngine(QList states, QObject *parent=0); - ~QSGStochasticEngine(); - - QDeclarativeListProperty states() - { - return QDeclarativeListProperty(this, m_states); - } - - QString globalGoal() const - { - return m_globalGoal; - } - - int count() const {return m_things.count();} - void setCount(int c); - - - - void setGoal(int state, int sprite=0, bool jump=false); - void start(int index=0, int state=0); - void stop(int index=0); - int curState(int index=0) {return m_things[index];} - - QSGStochasticState* state(int idx){return m_states[idx];} - int stateIndex(QSGStochasticState* s){return m_states.indexOf(s);} - int stateCount() {return m_states.count();} -private: -signals: - - void globalGoalChanged(QString arg); - void stateChanged(int idx); - -public slots: - void setGlobalGoal(QString arg) - { - if (m_globalGoal != arg) { - m_globalGoal = arg; - emit globalGoalChanged(arg); - } - } - - uint updateSprites(uint time); - -protected: - friend class QSGParticleSystem; - void restart(int index); - void addToUpdateList(uint t, int idx); - int goalSeek(int curState, int idx, int dist=-1); - QList m_states; - //### Consider struct or class for the four data variables? - QVector m_things;//int is the index in m_states of the current state - QVector m_goals; - QVector m_duration; - QVector m_startTimes; - QList > > m_stateUpdates;//### This could be done faster - priority queue? - - QTime m_advanceTime; - uint m_timeOffset; - QString m_globalGoal; - int m_maxFrames; - int m_imageStateCount; -}; - -class QSGSpriteEngine : public QSGStochasticEngine -{ - Q_OBJECT - Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) -public: - explicit QSGSpriteEngine(QObject *parent = 0); - QSGSpriteEngine(QList sprites, QObject *parent=0); - ~QSGSpriteEngine(); - QDeclarativeListProperty sprites() - { - return QDeclarativeListProperty(this, m_sprites); - } - - - int spriteState(int sprite=0); - int spriteStart(int sprite=0); - int spriteFrames(int sprite=0); - int spriteDuration(int sprite=0); - int spriteX(int /* sprite */ = 0) { return 0; }//Currently all rows are 0 aligned, if we get more space efficient we might change this - int spriteY(int sprite=0); - int spriteWidth(int sprite=0); - int spriteHeight(int sprite=0); - int spriteCount();//Like state count, but for the image states - int maxFrames(); - QImage assembledImage(); -private: - QList m_sprites; -}; - -//Common use is to have your own list property which is transparently an engine -inline void spriteAppend(QDeclarativeListProperty *p, QSGSprite* s) -{ - reinterpret_cast *>(p->data)->append(s); - p->object->metaObject()->invokeMethod(p->object, "createEngine"); -} - -inline QSGSprite* spriteAt(QDeclarativeListProperty *p, int idx) -{ - return reinterpret_cast *>(p->data)->at(idx); -} - -inline void spriteClear(QDeclarativeListProperty *p) -{ - reinterpret_cast *>(p->data)->clear(); - p->object->metaObject()->invokeMethod(p->object, "createEngine"); -} - -inline int spriteCount(QDeclarativeListProperty *p) -{ - return reinterpret_cast *>(p->data)->count(); -} - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SPRITEENGINE_H diff --git a/src/declarative/items/qsgspriteimage.cpp b/src/declarative/items/qsgspriteimage.cpp deleted file mode 100644 index f6cd35f4f3..0000000000 --- a/src/declarative/items/qsgspriteimage.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgspriteimage_p.h" -#include "qsgsprite_p.h" -#include "qsgspriteengine_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -static const char vertexShaderCode[] = - "attribute highp vec2 vTex;\n" - "uniform highp vec4 animData;// interpolate(bool), duration, frameCount (this anim), timestamp (this anim)\n" - "uniform highp vec4 animPos;//sheet x,y, width/height of this anim\n" - "uniform highp vec4 animSheetSize; //width/height of whole sheet, width/height of element\n" - "\n" - "uniform highp mat4 qt_Matrix;\n" - "uniform highp float timestamp;\n" - "\n" - "varying highp vec4 fTexS;\n" - "varying lowp float progress;\n" - "\n" - "\n" - "void main() {\n" - " //Calculate frame location in texture\n" - " highp float frameIndex = mod((((timestamp - animData.w)*1000.)/animData.y),animData.z);\n" - " progress = mod((timestamp - animData.w)*1000., animData.y) / animData.y;\n" - "\n" - " frameIndex = floor(frameIndex);\n" - " fTexS.xy = vec2(((frameIndex + vTex.x) * animPos.z / animSheetSize.x), ((animPos.y + vTex.y * animPos.w) / animSheetSize.y));\n" - "\n" - " //Next frame is also passed, for interpolation\n" - " //### Should the next anim be precalculated to allow for interpolation there?\n" - " if (animData.x == 1.0 && frameIndex != animData.z - 1.)//Can't do it for the last frame though, this anim may not loop\n" - " frameIndex = mod(frameIndex+1., animData.z);\n" - " fTexS.zw = vec2(((frameIndex + vTex.x) * animPos.z / animSheetSize.x), ((animPos.y + vTex.y * animPos.w) / animSheetSize.y));\n" - "\n" - " gl_Position = qt_Matrix * vec4(animSheetSize.z * vTex.x, animSheetSize.w * vTex.y, 0, 1);\n" - "}\n"; - -static const char fragmentShaderCode[] = - "uniform sampler2D texture;\n" - "uniform lowp float qt_Opacity;\n" - "\n" - "varying highp vec4 fTexS;\n" - "varying lowp float progress;\n" - "\n" - "void main() {\n" - " gl_FragColor = mix(texture2D(texture, fTexS.xy), texture2D(texture, fTexS.zw), progress) * qt_Opacity;\n" - "}\n"; - -class QSGSpriteMaterial : public QSGMaterial -{ -public: - QSGSpriteMaterial(); - virtual ~QSGSpriteMaterial(); - virtual QSGMaterialType *type() const { static QSGMaterialType type; return &type; } - virtual QSGMaterialShader *createShader() const; - virtual int compare(const QSGMaterial *other) const - { - return this - static_cast(other); - } - - QSGTexture *texture; - - qreal timestamp; - float interpolate; - float frameDuration; - float frameCount; - float animT; - float animX; - float animY; - float animWidth; - float animHeight; - float sheetWidth; - float sheetHeight; - float elementWidth; - float elementHeight; -}; - -QSGSpriteMaterial::QSGSpriteMaterial() - : timestamp(0) - , interpolate(1.0f) - , frameDuration(1.0f) - , frameCount(1.0f) - , animT(0.0f) - , animX(0.0f) - , animY(0.0f) - , animWidth(1.0f) - , animHeight(1.0f) - , sheetWidth(1.0f) - , sheetHeight(1.0f) - , elementWidth(1.0f) - , elementHeight(1.0f) -{ - setFlag(Blending, true); -} - -QSGSpriteMaterial::~QSGSpriteMaterial() -{ - delete texture; -} - -class SpriteMaterialData : public QSGMaterialShader -{ -public: - SpriteMaterialData(const char *vertexFile = 0, const char *fragmentFile = 0) - { - } - - void deactivate() { - QSGMaterialShader::deactivate(); - - for (int i=0; i<8; ++i) { - program()->setAttributeArray(i, GL_FLOAT, chunkOfBytes, 1, 0); - } - } - - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) - { - QSGSpriteMaterial *m = static_cast(newEffect); - m->texture->bind(); - - program()->setUniformValue(m_opacity_id, state.opacity()); - program()->setUniformValue(m_timestamp_id, (float) m->timestamp); - program()->setUniformValue(m_animData_id, m->interpolate, m->frameDuration, m->frameCount, m->animT); - program()->setUniformValue(m_animPos_id, m->animX, m->animY, m->animWidth, m->animHeight); - program()->setUniformValue(m_animSheetSize_id, m->sheetWidth, m->sheetHeight, m->elementWidth, m->elementHeight); - - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); - } - - virtual void initialize() { - m_matrix_id = program()->uniformLocation("qt_Matrix"); - m_opacity_id = program()->uniformLocation("qt_Opacity"); - m_timestamp_id = program()->uniformLocation("timestamp"); - m_animData_id = program()->uniformLocation("animData"); - m_animPos_id = program()->uniformLocation("animPos"); - m_animSheetSize_id = program()->uniformLocation("animSheetSize"); - } - - virtual const char *vertexShader() const { return vertexShaderCode; } - virtual const char *fragmentShader() const { return fragmentShaderCode; } - - virtual char const *const *attributeNames() const { - static const char *attr[] = { - "vTex", - 0 - }; - return attr; - } - - int m_matrix_id; - int m_opacity_id; - int m_timestamp_id; - int m_animData_id; - int m_animPos_id; - int m_animSheetSize_id; - - static float chunkOfBytes[1024]; -}; - -float SpriteMaterialData::chunkOfBytes[1024]; - -QSGMaterialShader *QSGSpriteMaterial::createShader() const -{ - return new SpriteMaterialData; -} - -struct SpriteVertex { - float tx; - float ty; -}; - -struct SpriteVertices { - SpriteVertex v1; - SpriteVertex v2; - SpriteVertex v3; - SpriteVertex v4; -}; - -/*! - \qmlclass SpriteImage QSGSpriteImage - \inqmlmodule QtQuick 2 - \inherits Item - \brief The SpriteImage element draws a sprite animation - -*/ -/*! - \qmlproperty bool QtQuick2::SpriteImage::running - - Whether the sprite is animating or not. - - Default is true -*/ -/*! - \qmlproperty bool QtQuick2::SpriteImage::interpolate - - If true, interpolation will occur between sprite frames to make the - animation appear smoother. - - Default is true. -*/ -/*! - \qmlproperty list QtQuick2::SpriteImage::sprites - - The sprite or sprites to draw. Sprites will be scaled to the size of this element. -*/ - -//TODO: Implicitly size element to size of first sprite? -QSGSpriteImage::QSGSpriteImage(QSGItem *parent) : - QSGItem(parent) - , m_node(0) - , m_material(0) - , m_spriteEngine(0) - , m_pleaseReset(false) - , m_running(true) - , m_interpolate(true) -{ - setFlag(ItemHasContents); - connect(this, SIGNAL(runningChanged(bool)), - this, SLOT(update())); -} - -QDeclarativeListProperty QSGSpriteImage::sprites() -{ - return QDeclarativeListProperty(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear); -} - -void QSGSpriteImage::createEngine() -{ - //TODO: delay until component complete - if (m_spriteEngine) - delete m_spriteEngine; - if (m_sprites.count()) - m_spriteEngine = new QSGSpriteEngine(m_sprites, this); - else - m_spriteEngine = 0; - reset(); -} - -static QSGGeometry::Attribute SpriteImage_Attributes[] = { - QSGGeometry::Attribute::create(0, 2, GL_FLOAT), // tex -}; - -static QSGGeometry::AttributeSet SpriteImage_AttributeSet = -{ - 1, // Attribute Count - 2 * sizeof(float), - SpriteImage_Attributes -}; - -QSGGeometryNode* QSGSpriteImage::buildNode() -{ - if (!m_spriteEngine) { - qWarning() << "SpriteImage: No sprite engine..."; - return 0; - } - - m_material = new QSGSpriteMaterial(); - - QImage image = m_spriteEngine->assembledImage(); - if (image.isNull()) - return 0; - m_material->texture = sceneGraphEngine()->createTextureFromImage(image); - m_material->texture->setFiltering(QSGTexture::Linear); - m_spriteEngine->start(0); - m_material->interpolate = m_interpolate ? 1.0 : 0.0; - m_material->frameCount = m_spriteEngine->spriteFrames(); - m_material->frameDuration = m_spriteEngine->spriteDuration(); - m_material->animT = 0; - m_material->animX = m_spriteEngine->spriteX(); - m_material->animY = m_spriteEngine->spriteY(); - m_material->animWidth = m_spriteEngine->spriteWidth(); - m_material->animHeight = m_spriteEngine->spriteHeight(); - m_material->sheetWidth = image.width(); - m_material->sheetHeight = image.height(); - m_material->elementWidth = width(); - m_material->elementHeight = height(); - - int vCount = 4; - int iCount = 6; - QSGGeometry *g = new QSGGeometry(SpriteImage_AttributeSet, vCount, iCount); - g->setDrawingMode(GL_TRIANGLES); - - SpriteVertices *p = (SpriteVertices *) g->vertexData(); - - p->v1.tx = 0; - p->v1.ty = 0; - - p->v2.tx = 1.0; - p->v2.ty = 0; - - p->v3.tx = 0; - p->v3.ty = 1.0; - - p->v4.tx = 1.0; - p->v4.ty = 1.0; - - quint16 *indices = g->indexDataAsUShort(); - indices[0] = 0; - indices[1] = 1; - indices[2] = 2; - indices[3] = 1; - indices[4] = 3; - indices[5] = 2; - - - m_timestamp.start(); - m_node = new QSGGeometryNode(); - m_node->setGeometry(g); - m_node->setMaterial(m_material); - m_node->setFlag(QSGGeometryNode::OwnsMaterial); - return m_node; -} - -void QSGSpriteImage::reset() -{ - m_pleaseReset = true; -} - -QSGNode *QSGSpriteImage::updatePaintNode(QSGNode *, UpdatePaintNodeData *) -{ - if (m_pleaseReset) { - delete m_node; - delete m_material; - - m_node = 0; - m_material = 0; - m_pleaseReset = false; - } - - prepareNextFrame(); - - if (m_running) { - update(); - if (m_node) - m_node->markDirty(QSGNode::DirtyMaterial); - } - - return m_node; -} - -void QSGSpriteImage::prepareNextFrame() -{ - if (m_node == 0) - m_node = buildNode(); - if (m_node == 0) //error creating node - return; - - uint timeInt = m_timestamp.elapsed(); - qreal time = timeInt / 1000.; - m_material->timestamp = time; - m_material->elementHeight = height(); - m_material->elementWidth = width(); - m_material->interpolate = m_interpolate; - - //Advance State - SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData(); - m_spriteEngine->updateSprites(timeInt); - int curY = m_spriteEngine->spriteY(); - if (curY != m_material->animY){ - m_material->animT = m_spriteEngine->spriteStart()/1000.0; - m_material->frameCount = m_spriteEngine->spriteFrames(); - m_material->frameDuration = m_spriteEngine->spriteDuration(); - m_material->animX = m_spriteEngine->spriteX(); - m_material->animY = m_spriteEngine->spriteY(); - m_material->animWidth = m_spriteEngine->spriteWidth(); - m_material->animHeight = m_spriteEngine->spriteHeight(); - } -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgspriteimage_p.h b/src/declarative/items/qsgspriteimage_p.h deleted file mode 100644 index 84c5b50136..0000000000 --- a/src/declarative/items/qsgspriteimage_p.h +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Declarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SPRITEIMAGE_H -#define SPRITEIMAGE_H - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGContext; -class QSGSprite; -class QSGSpriteEngine; -class QSGGeometryNode; -class QSGSpriteMaterial; -class QSGSpriteImage : public QSGItem -{ - Q_OBJECT - Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) - Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate NOTIFY interpolateChanged) - //###try to share similar spriteEngines for less overhead? - Q_PROPERTY(QDeclarativeListProperty sprites READ sprites) - Q_CLASSINFO("DefaultProperty", "sprites") - -public: - explicit QSGSpriteImage(QSGItem *parent = 0); - - QDeclarativeListProperty sprites(); - - bool running() const - { - return m_running; - } - - bool interpolate() const - { - return m_interpolate; - } - -signals: - - void runningChanged(bool arg); - void interpolateChanged(bool arg); - -public slots: - -void setRunning(bool arg) -{ - if (m_running != arg) { - m_running = arg; - emit runningChanged(arg); - } -} - -void setInterpolate(bool arg) -{ - if (m_interpolate != arg) { - m_interpolate = arg; - emit interpolateChanged(arg); - } -} - -private slots: - void createEngine(); -protected: - void reset(); - QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); -private: - void prepareNextFrame(); - QSGGeometryNode* buildNode(); - QSGGeometryNode *m_node; - QSGSpriteMaterial *m_material; - QList m_sprites; - QSGSpriteEngine* m_spriteEngine; - QTime m_timestamp; - int m_maxFrames; - bool m_pleaseReset; - bool m_running; - bool m_interpolate; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // SPRITEIMAGE_H diff --git a/src/declarative/items/qsgstateoperations.cpp b/src/declarative/items/qsgstateoperations.cpp deleted file mode 100644 index 8f878f3aaa..0000000000 --- a/src/declarative/items/qsgstateoperations.cpp +++ /dev/null @@ -1,1346 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgstateoperations_p.h" -#include "qsgitem_p.h" - -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGParentChangePrivate : public QDeclarativeStateOperationPrivate -{ - Q_DECLARE_PUBLIC(QSGParentChange) -public: - QSGParentChangePrivate() : target(0), parent(0), origParent(0), origStackBefore(0), - rewindParent(0), rewindStackBefore(0) {} - - QSGItem *target; - QDeclarativeGuard parent; - QDeclarativeGuard origParent; - QDeclarativeGuard origStackBefore; - QSGItem *rewindParent; - QSGItem *rewindStackBefore; - - QDeclarativeNullableValue xString; - QDeclarativeNullableValue yString; - QDeclarativeNullableValue widthString; - QDeclarativeNullableValue heightString; - QDeclarativeNullableValue scaleString; - QDeclarativeNullableValue rotationString; - - void doChange(QSGItem *targetParent, QSGItem *stackBefore = 0); -}; - -void QSGParentChangePrivate::doChange(QSGItem *targetParent, QSGItem *stackBefore) -{ - if (targetParent && target && target->parentItem()) { - Q_Q(QSGParentChange); - bool ok; - const QTransform &transform = target->parentItem()->itemTransform(targetParent, &ok); - if (transform.type() >= QTransform::TxShear || !ok) { - qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under complex transform"); - ok = false; - } - - qreal scale = 1; - qreal rotation = 0; - bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); - if (ok && !isRotate) { - if (transform.m11() == transform.m22()) - scale = transform.m11(); - else { - qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under non-uniform scale"); - ok = false; - } - } else if (ok && isRotate) { - if (transform.m11() == transform.m22()) - scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); - else { - qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under non-uniform scale"); - ok = false; - } - - if (scale != 0) - rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; - else { - qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under scale of 0"); - ok = false; - } - } - - const QPointF &point = transform.map(QPointF(target->x(),target->y())); - qreal x = point.x(); - qreal y = point.y(); - - // setParentItem will update the transformOriginPoint if needed - target->setParentItem(targetParent); - - if (ok && target->transformOrigin() != QSGItem::TopLeft) { - qreal tempxt = target->transformOriginPoint().x(); - qreal tempyt = target->transformOriginPoint().y(); - QTransform t; - t.translate(-tempxt, -tempyt); - t.rotate(rotation); - t.scale(scale, scale); - t.translate(tempxt, tempyt); - const QPointF &offset = t.map(QPointF(0,0)); - x += offset.x(); - y += offset.y(); - } - - if (ok) { - //qDebug() << x << y << rotation << scale; - target->setX(x); - target->setY(y); - target->setRotation(target->rotation() + rotation); - target->setScale(target->scale() * scale); - } - } else if (target) { - target->setParentItem(targetParent); - } - - //restore the original stack position. - //### if stackBefore has also been reparented this won't work - if (stackBefore) - target->stackBefore(stackBefore); -} - -QSGParentChange::QSGParentChange(QObject *parent) - : QDeclarativeStateOperation(*(new QSGParentChangePrivate), parent) -{ -} - -QSGParentChange::~QSGParentChange() -{ -} - -QDeclarativeScriptString QSGParentChange::x() const -{ - Q_D(const QSGParentChange); - return d->xString.value; -} - -void QSGParentChange::setX(QDeclarativeScriptString x) -{ - Q_D(QSGParentChange); - d->xString = x; -} - -bool QSGParentChange::xIsSet() const -{ - Q_D(const QSGParentChange); - return d->xString.isValid(); -} - -QDeclarativeScriptString QSGParentChange::y() const -{ - Q_D(const QSGParentChange); - return d->yString.value; -} - -void QSGParentChange::setY(QDeclarativeScriptString y) -{ - Q_D(QSGParentChange); - d->yString = y; -} - -bool QSGParentChange::yIsSet() const -{ - Q_D(const QSGParentChange); - return d->yString.isValid(); -} - -QDeclarativeScriptString QSGParentChange::width() const -{ - Q_D(const QSGParentChange); - return d->widthString.value; -} - -void QSGParentChange::setWidth(QDeclarativeScriptString width) -{ - Q_D(QSGParentChange); - d->widthString = width; -} - -bool QSGParentChange::widthIsSet() const -{ - Q_D(const QSGParentChange); - return d->widthString.isValid(); -} - -QDeclarativeScriptString QSGParentChange::height() const -{ - Q_D(const QSGParentChange); - return d->heightString.value; -} - -void QSGParentChange::setHeight(QDeclarativeScriptString height) -{ - Q_D(QSGParentChange); - d->heightString = height; -} - -bool QSGParentChange::heightIsSet() const -{ - Q_D(const QSGParentChange); - return d->heightString.isValid(); -} - -QDeclarativeScriptString QSGParentChange::scale() const -{ - Q_D(const QSGParentChange); - return d->scaleString.value; -} - -void QSGParentChange::setScale(QDeclarativeScriptString scale) -{ - Q_D(QSGParentChange); - d->scaleString = scale; -} - -bool QSGParentChange::scaleIsSet() const -{ - Q_D(const QSGParentChange); - return d->scaleString.isValid(); -} - -QDeclarativeScriptString QSGParentChange::rotation() const -{ - Q_D(const QSGParentChange); - return d->rotationString.value; -} - -void QSGParentChange::setRotation(QDeclarativeScriptString rotation) -{ - Q_D(QSGParentChange); - d->rotationString = rotation; -} - -bool QSGParentChange::rotationIsSet() const -{ - Q_D(const QSGParentChange); - return d->rotationString.isValid(); -} - -QSGItem *QSGParentChange::originalParent() const -{ - Q_D(const QSGParentChange); - return d->origParent; -} - -QSGItem *QSGParentChange::object() const -{ - Q_D(const QSGParentChange); - return d->target; -} - -void QSGParentChange::setObject(QSGItem *target) -{ - Q_D(QSGParentChange); - d->target = target; -} - -QSGItem *QSGParentChange::parent() const -{ - Q_D(const QSGParentChange); - return d->parent; -} - -void QSGParentChange::setParent(QSGItem *parent) -{ - Q_D(QSGParentChange); - d->parent = parent; -} - -QDeclarativeStateOperation::ActionList QSGParentChange::actions() -{ - Q_D(QSGParentChange); - if (!d->target || !d->parent) - return ActionList(); - - ActionList actions; - - QDeclarativeAction a; - a.event = this; - actions << a; - - if (d->xString.isValid()) { - bool ok = false; - QString script = d->xString.value.script(); - qreal x = script.toFloat(&ok); - if (ok) { - QDeclarativeAction xa(d->target, QLatin1String("x"), x); - actions << xa; - } else { - QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); - newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("x"))); - QDeclarativeAction xa; - xa.property = newBinding->property(); - xa.toBinding = newBinding; - xa.fromValue = xa.property.read(); - xa.deletableToBinding = true; - actions << xa; - } - } - - if (d->yString.isValid()) { - bool ok = false; - QString script = d->yString.value.script(); - qreal y = script.toFloat(&ok); - if (ok) { - QDeclarativeAction ya(d->target, QLatin1String("y"), y); - actions << ya; - } else { - QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); - newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("y"))); - QDeclarativeAction ya; - ya.property = newBinding->property(); - ya.toBinding = newBinding; - ya.fromValue = ya.property.read(); - ya.deletableToBinding = true; - actions << ya; - } - } - - if (d->scaleString.isValid()) { - bool ok = false; - QString script = d->scaleString.value.script(); - qreal scale = script.toFloat(&ok); - if (ok) { - QDeclarativeAction sa(d->target, QLatin1String("scale"), scale); - actions << sa; - } else { - QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); - newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("scale"))); - QDeclarativeAction sa; - sa.property = newBinding->property(); - sa.toBinding = newBinding; - sa.fromValue = sa.property.read(); - sa.deletableToBinding = true; - actions << sa; - } - } - - if (d->rotationString.isValid()) { - bool ok = false; - QString script = d->rotationString.value.script(); - qreal rotation = script.toFloat(&ok); - if (ok) { - QDeclarativeAction ra(d->target, QLatin1String("rotation"), rotation); - actions << ra; - } else { - QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); - newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("rotation"))); - QDeclarativeAction ra; - ra.property = newBinding->property(); - ra.toBinding = newBinding; - ra.fromValue = ra.property.read(); - ra.deletableToBinding = true; - actions << ra; - } - } - - if (d->widthString.isValid()) { - bool ok = false; - QString script = d->widthString.value.script(); - qreal width = script.toFloat(&ok); - if (ok) { - QDeclarativeAction wa(d->target, QLatin1String("width"), width); - actions << wa; - } else { - QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); - newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("width"))); - QDeclarativeAction wa; - wa.property = newBinding->property(); - wa.toBinding = newBinding; - wa.fromValue = wa.property.read(); - wa.deletableToBinding = true; - actions << wa; - } - } - - if (d->heightString.isValid()) { - bool ok = false; - QString script = d->heightString.value.script(); - qreal height = script.toFloat(&ok); - if (ok) { - QDeclarativeAction ha(d->target, QLatin1String("height"), height); - actions << ha; - } else { - QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this)); - newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("height"))); - QDeclarativeAction ha; - ha.property = newBinding->property(); - ha.toBinding = newBinding; - ha.fromValue = ha.property.read(); - ha.deletableToBinding = true; - actions << ha; - } - } - - return actions; -} - -void QSGParentChange::saveOriginals() -{ - Q_D(QSGParentChange); - saveCurrentValues(); - d->origParent = d->rewindParent; - d->origStackBefore = d->rewindStackBefore; -} - -/*void QSGParentChange::copyOriginals(QDeclarativeActionEvent *other) -{ - Q_D(QSGParentChange); - QSGParentChange *pc = static_cast(other); - - d->origParent = pc->d_func()->rewindParent; - d->origStackBefore = pc->d_func()->rewindStackBefore; - - saveCurrentValues(); -}*/ - -void QSGParentChange::execute(Reason) -{ - Q_D(QSGParentChange); - d->doChange(d->parent); -} - -bool QSGParentChange::isReversable() -{ - return true; -} - -void QSGParentChange::reverse(Reason) -{ - Q_D(QSGParentChange); - d->doChange(d->origParent, d->origStackBefore); -} - -QString QSGParentChange::typeName() const -{ - return QLatin1String("ParentChange"); -} - -bool QSGParentChange::override(QDeclarativeActionEvent*other) -{ - Q_D(QSGParentChange); - if (other->typeName() != QLatin1String("ParentChange")) - return false; - if (QSGParentChange *otherPC = static_cast(other)) - return (d->target == otherPC->object()); - return false; -} - -void QSGParentChange::saveCurrentValues() -{ - Q_D(QSGParentChange); - if (!d->target) { - d->rewindParent = 0; - d->rewindStackBefore = 0; - return; - } - - d->rewindParent = d->target->parentItem(); - d->rewindStackBefore = 0; - - if (!d->rewindParent) - return; - - QList children = d->rewindParent->childItems(); - for (int ii = 0; ii < children.count() - 1; ++ii) { - if (children.at(ii) == d->target) { - d->rewindStackBefore = children.at(ii + 1); - break; - } - } -} - -void QSGParentChange::rewind() -{ - Q_D(QSGParentChange); - d->doChange(d->rewindParent, d->rewindStackBefore); -} - -class QSGAnchorSetPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QSGAnchorSet) -public: - QSGAnchorSetPrivate() - : usedAnchors(0), resetAnchors(0), fill(0), - centerIn(0)/*, leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), - margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)*/ - { - } - - QSGAnchors::Anchors usedAnchors; - QSGAnchors::Anchors resetAnchors; - - QSGItem *fill; - QSGItem *centerIn; - - QDeclarativeScriptString leftScript; - QDeclarativeScriptString rightScript; - QDeclarativeScriptString topScript; - QDeclarativeScriptString bottomScript; - QDeclarativeScriptString hCenterScript; - QDeclarativeScriptString vCenterScript; - QDeclarativeScriptString baselineScript; - - /*qreal leftMargin; - qreal rightMargin; - qreal topMargin; - qreal bottomMargin; - qreal margins; - qreal vCenterOffset; - qreal hCenterOffset; - qreal baselineOffset;*/ -}; - -QSGAnchorSet::QSGAnchorSet(QObject *parent) - : QObject(*new QSGAnchorSetPrivate, parent) -{ -} - -QSGAnchorSet::~QSGAnchorSet() -{ -} - -QDeclarativeScriptString QSGAnchorSet::top() const -{ - Q_D(const QSGAnchorSet); - return d->topScript; -} - -void QSGAnchorSet::setTop(const QDeclarativeScriptString &edge) -{ - Q_D(QSGAnchorSet); - d->usedAnchors |= QSGAnchors::TopAnchor; - d->topScript = edge; - if (edge.script() == QLatin1String("undefined")) - resetTop(); -} - -void QSGAnchorSet::resetTop() -{ - Q_D(QSGAnchorSet); - d->usedAnchors &= ~QSGAnchors::TopAnchor; - d->topScript = QDeclarativeScriptString(); - d->resetAnchors |= QSGAnchors::TopAnchor; -} - -QDeclarativeScriptString QSGAnchorSet::bottom() const -{ - Q_D(const QSGAnchorSet); - return d->bottomScript; -} - -void QSGAnchorSet::setBottom(const QDeclarativeScriptString &edge) -{ - Q_D(QSGAnchorSet); - d->usedAnchors |= QSGAnchors::BottomAnchor; - d->bottomScript = edge; - if (edge.script() == QLatin1String("undefined")) - resetBottom(); -} - -void QSGAnchorSet::resetBottom() -{ - Q_D(QSGAnchorSet); - d->usedAnchors &= ~QSGAnchors::BottomAnchor; - d->bottomScript = QDeclarativeScriptString(); - d->resetAnchors |= QSGAnchors::BottomAnchor; -} - -QDeclarativeScriptString QSGAnchorSet::verticalCenter() const -{ - Q_D(const QSGAnchorSet); - return d->vCenterScript; -} - -void QSGAnchorSet::setVerticalCenter(const QDeclarativeScriptString &edge) -{ - Q_D(QSGAnchorSet); - d->usedAnchors |= QSGAnchors::VCenterAnchor; - d->vCenterScript = edge; - if (edge.script() == QLatin1String("undefined")) - resetVerticalCenter(); -} - -void QSGAnchorSet::resetVerticalCenter() -{ - Q_D(QSGAnchorSet); - d->usedAnchors &= ~QSGAnchors::VCenterAnchor; - d->vCenterScript = QDeclarativeScriptString(); - d->resetAnchors |= QSGAnchors::VCenterAnchor; -} - -QDeclarativeScriptString QSGAnchorSet::baseline() const -{ - Q_D(const QSGAnchorSet); - return d->baselineScript; -} - -void QSGAnchorSet::setBaseline(const QDeclarativeScriptString &edge) -{ - Q_D(QSGAnchorSet); - d->usedAnchors |= QSGAnchors::BaselineAnchor; - d->baselineScript = edge; - if (edge.script() == QLatin1String("undefined")) - resetBaseline(); -} - -void QSGAnchorSet::resetBaseline() -{ - Q_D(QSGAnchorSet); - d->usedAnchors &= ~QSGAnchors::BaselineAnchor; - d->baselineScript = QDeclarativeScriptString(); - d->resetAnchors |= QSGAnchors::BaselineAnchor; -} - -QDeclarativeScriptString QSGAnchorSet::left() const -{ - Q_D(const QSGAnchorSet); - return d->leftScript; -} - -void QSGAnchorSet::setLeft(const QDeclarativeScriptString &edge) -{ - Q_D(QSGAnchorSet); - d->usedAnchors |= QSGAnchors::LeftAnchor; - d->leftScript = edge; - if (edge.script() == QLatin1String("undefined")) - resetLeft(); -} - -void QSGAnchorSet::resetLeft() -{ - Q_D(QSGAnchorSet); - d->usedAnchors &= ~QSGAnchors::LeftAnchor; - d->leftScript = QDeclarativeScriptString(); - d->resetAnchors |= QSGAnchors::LeftAnchor; -} - -QDeclarativeScriptString QSGAnchorSet::right() const -{ - Q_D(const QSGAnchorSet); - return d->rightScript; -} - -void QSGAnchorSet::setRight(const QDeclarativeScriptString &edge) -{ - Q_D(QSGAnchorSet); - d->usedAnchors |= QSGAnchors::RightAnchor; - d->rightScript = edge; - if (edge.script() == QLatin1String("undefined")) - resetRight(); -} - -void QSGAnchorSet::resetRight() -{ - Q_D(QSGAnchorSet); - d->usedAnchors &= ~QSGAnchors::RightAnchor; - d->rightScript = QDeclarativeScriptString(); - d->resetAnchors |= QSGAnchors::RightAnchor; -} - -QDeclarativeScriptString QSGAnchorSet::horizontalCenter() const -{ - Q_D(const QSGAnchorSet); - return d->hCenterScript; -} - -void QSGAnchorSet::setHorizontalCenter(const QDeclarativeScriptString &edge) -{ - Q_D(QSGAnchorSet); - d->usedAnchors |= QSGAnchors::HCenterAnchor; - d->hCenterScript = edge; - if (edge.script() == QLatin1String("undefined")) - resetHorizontalCenter(); -} - -void QSGAnchorSet::resetHorizontalCenter() -{ - Q_D(QSGAnchorSet); - d->usedAnchors &= ~QSGAnchors::HCenterAnchor; - d->hCenterScript = QDeclarativeScriptString(); - d->resetAnchors |= QSGAnchors::HCenterAnchor; -} - -QSGItem *QSGAnchorSet::fill() const -{ - Q_D(const QSGAnchorSet); - return d->fill; -} - -void QSGAnchorSet::setFill(QSGItem *f) -{ - Q_D(QSGAnchorSet); - d->fill = f; -} - -void QSGAnchorSet::resetFill() -{ - setFill(0); -} - -QSGItem *QSGAnchorSet::centerIn() const -{ - Q_D(const QSGAnchorSet); - return d->centerIn; -} - -void QSGAnchorSet::setCenterIn(QSGItem* c) -{ - Q_D(QSGAnchorSet); - d->centerIn = c; -} - -void QSGAnchorSet::resetCenterIn() -{ - setCenterIn(0); -} - - -class QSGAnchorChangesPrivate : public QDeclarativeStateOperationPrivate -{ -public: - QSGAnchorChangesPrivate() - : target(0), anchorSet(new QSGAnchorSet), - leftBinding(0), rightBinding(0), hCenterBinding(0), - topBinding(0), bottomBinding(0), vCenterBinding(0), baselineBinding(0), - origLeftBinding(0), origRightBinding(0), origHCenterBinding(0), - origTopBinding(0), origBottomBinding(0), origVCenterBinding(0), - origBaselineBinding(0) - { - - } - ~QSGAnchorChangesPrivate() { delete anchorSet; } - - QSGItem *target; - QSGAnchorSet *anchorSet; - - QDeclarativeBinding *leftBinding; - QDeclarativeBinding *rightBinding; - QDeclarativeBinding *hCenterBinding; - QDeclarativeBinding *topBinding; - QDeclarativeBinding *bottomBinding; - QDeclarativeBinding *vCenterBinding; - QDeclarativeBinding *baselineBinding; - - QDeclarativeAbstractBinding *origLeftBinding; - QDeclarativeAbstractBinding *origRightBinding; - QDeclarativeAbstractBinding *origHCenterBinding; - QDeclarativeAbstractBinding *origTopBinding; - QDeclarativeAbstractBinding *origBottomBinding; - QDeclarativeAbstractBinding *origVCenterBinding; - QDeclarativeAbstractBinding *origBaselineBinding; - - QSGAnchorLine rewindLeft; - QSGAnchorLine rewindRight; - QSGAnchorLine rewindHCenter; - QSGAnchorLine rewindTop; - QSGAnchorLine rewindBottom; - QSGAnchorLine rewindVCenter; - QSGAnchorLine rewindBaseline; - - qreal fromX; - qreal fromY; - qreal fromWidth; - qreal fromHeight; - - qreal toX; - qreal toY; - qreal toWidth; - qreal toHeight; - - qreal rewindX; - qreal rewindY; - qreal rewindWidth; - qreal rewindHeight; - - bool applyOrigLeft; - bool applyOrigRight; - bool applyOrigHCenter; - bool applyOrigTop; - bool applyOrigBottom; - bool applyOrigVCenter; - bool applyOrigBaseline; - - QDeclarativeNullableValue origWidth; - QDeclarativeNullableValue origHeight; - qreal origX; - qreal origY; - - QList oldBindings; - - QDeclarativeProperty leftProp; - QDeclarativeProperty rightProp; - QDeclarativeProperty hCenterProp; - QDeclarativeProperty topProp; - QDeclarativeProperty bottomProp; - QDeclarativeProperty vCenterProp; - QDeclarativeProperty baselineProp; -}; - -QSGAnchorChanges::QSGAnchorChanges(QObject *parent) - : QDeclarativeStateOperation(*(new QSGAnchorChangesPrivate), parent) -{ -} - -QSGAnchorChanges::~QSGAnchorChanges() -{ -} - -QSGAnchorChanges::ActionList QSGAnchorChanges::actions() -{ - Q_D(QSGAnchorChanges); - d->leftBinding = d->rightBinding = d->hCenterBinding = d->topBinding - = d->bottomBinding = d->vCenterBinding = d->baselineBinding = 0; - - d->leftProp = QDeclarativeProperty(d->target, QLatin1String("anchors.left")); - d->rightProp = QDeclarativeProperty(d->target, QLatin1String("anchors.right")); - d->hCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.horizontalCenter")); - d->topProp = QDeclarativeProperty(d->target, QLatin1String("anchors.top")); - d->bottomProp = QDeclarativeProperty(d->target, QLatin1String("anchors.bottom")); - d->vCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.verticalCenter")); - d->baselineProp = QDeclarativeProperty(d->target, QLatin1String("anchors.baseline")); - - if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::LeftAnchor) { - d->leftBinding = new QDeclarativeBinding(d->anchorSet->d_func()->leftScript.script(), d->target, qmlContext(this)); - d->leftBinding->setTarget(d->leftProp); - } - if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::RightAnchor) { - d->rightBinding = new QDeclarativeBinding(d->anchorSet->d_func()->rightScript.script(), d->target, qmlContext(this)); - d->rightBinding->setTarget(d->rightProp); - } - if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::HCenterAnchor) { - d->hCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->hCenterScript.script(), d->target, qmlContext(this)); - d->hCenterBinding->setTarget(d->hCenterProp); - } - if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::TopAnchor) { - d->topBinding = new QDeclarativeBinding(d->anchorSet->d_func()->topScript.script(), d->target, qmlContext(this)); - d->topBinding->setTarget(d->topProp); - } - if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::BottomAnchor) { - d->bottomBinding = new QDeclarativeBinding(d->anchorSet->d_func()->bottomScript.script(), d->target, qmlContext(this)); - d->bottomBinding->setTarget(d->bottomProp); - } - if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::VCenterAnchor) { - d->vCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->vCenterScript.script(), d->target, qmlContext(this)); - d->vCenterBinding->setTarget(d->vCenterProp); - } - if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::BaselineAnchor) { - d->baselineBinding = new QDeclarativeBinding(d->anchorSet->d_func()->baselineScript.script(), d->target, qmlContext(this)); - d->baselineBinding->setTarget(d->baselineProp); - } - - QDeclarativeAction a; - a.event = this; - return ActionList() << a; -} - -QSGAnchorSet *QSGAnchorChanges::anchors() -{ - Q_D(QSGAnchorChanges); - return d->anchorSet; -} - -QSGItem *QSGAnchorChanges::object() const -{ - Q_D(const QSGAnchorChanges); - return d->target; -} - -void QSGAnchorChanges::setObject(QSGItem *target) -{ - Q_D(QSGAnchorChanges); - d->target = target; -} - -void QSGAnchorChanges::execute(Reason reason) -{ - Q_D(QSGAnchorChanges); - if (!d->target) - return; - - QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target); - //incorporate any needed "reverts" - if (d->applyOrigLeft) { - if (!d->origLeftBinding) - targetPrivate->anchors()->resetLeft(); - QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); - } - if (d->applyOrigRight) { - if (!d->origRightBinding) - targetPrivate->anchors()->resetRight(); - QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); - } - if (d->applyOrigHCenter) { - if (!d->origHCenterBinding) - targetPrivate->anchors()->resetHorizontalCenter(); - QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); - } - if (d->applyOrigTop) { - if (!d->origTopBinding) - targetPrivate->anchors()->resetTop(); - QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); - } - if (d->applyOrigBottom) { - if (!d->origBottomBinding) - targetPrivate->anchors()->resetBottom(); - QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); - } - if (d->applyOrigVCenter) { - if (!d->origVCenterBinding) - targetPrivate->anchors()->resetVerticalCenter(); - QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); - } - if (d->applyOrigBaseline) { - if (!d->origBaselineBinding) - targetPrivate->anchors()->resetBaseline(); - QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); - } - - //destroy old bindings - if (reason == ActualChange) { - for (int i = 0; i < d->oldBindings.size(); ++i) { - QDeclarativeAbstractBinding *binding = d->oldBindings.at(i); - if (binding) - binding->destroy(); - } - d->oldBindings.clear(); - } - - //reset any anchors that have been specified as "undefined" - if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::LeftAnchor) { - targetPrivate->anchors()->resetLeft(); - QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); - } - if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::RightAnchor) { - targetPrivate->anchors()->resetRight(); - QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); - } - if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::HCenterAnchor) { - targetPrivate->anchors()->resetHorizontalCenter(); - QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); - } - if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::TopAnchor) { - targetPrivate->anchors()->resetTop(); - QDeclarativePropertyPrivate::setBinding(d->topProp, 0); - } - if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::BottomAnchor) { - targetPrivate->anchors()->resetBottom(); - QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); - } - if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::VCenterAnchor) { - targetPrivate->anchors()->resetVerticalCenter(); - QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); - } - if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::BaselineAnchor) { - targetPrivate->anchors()->resetBaseline(); - QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); - } - - //set any anchors that have been specified - if (d->leftBinding) - QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), d->leftBinding); - if (d->rightBinding) - QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), d->rightBinding); - if (d->hCenterBinding) - QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), d->hCenterBinding); - if (d->topBinding) - QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), d->topBinding); - if (d->bottomBinding) - QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), d->bottomBinding); - if (d->vCenterBinding) - QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), d->vCenterBinding); - if (d->baselineBinding) - QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), d->baselineBinding); -} - -bool QSGAnchorChanges::isReversable() -{ - return true; -} - -void QSGAnchorChanges::reverse(Reason reason) -{ - Q_D(QSGAnchorChanges); - if (!d->target) - return; - - QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target); - //reset any anchors set by the state - if (d->leftBinding) { - targetPrivate->anchors()->resetLeft(); - QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), 0); - if (reason == ActualChange) { - d->leftBinding->destroy(); d->leftBinding = 0; - } - } - if (d->rightBinding) { - targetPrivate->anchors()->resetRight(); - QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), 0); - if (reason == ActualChange) { - d->rightBinding->destroy(); d->rightBinding = 0; - } - } - if (d->hCenterBinding) { - targetPrivate->anchors()->resetHorizontalCenter(); - QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), 0); - if (reason == ActualChange) { - d->hCenterBinding->destroy(); d->hCenterBinding = 0; - } - } - if (d->topBinding) { - targetPrivate->anchors()->resetTop(); - QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), 0); - if (reason == ActualChange) { - d->topBinding->destroy(); d->topBinding = 0; - } - } - if (d->bottomBinding) { - targetPrivate->anchors()->resetBottom(); - QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), 0); - if (reason == ActualChange) { - d->bottomBinding->destroy(); d->bottomBinding = 0; - } - } - if (d->vCenterBinding) { - targetPrivate->anchors()->resetVerticalCenter(); - QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), 0); - if (reason == ActualChange) { - d->vCenterBinding->destroy(); d->vCenterBinding = 0; - } - } - if (d->baselineBinding) { - targetPrivate->anchors()->resetBaseline(); - QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), 0); - if (reason == ActualChange) { - d->baselineBinding->destroy(); d->baselineBinding = 0; - } - } - - //restore previous anchors - if (d->origLeftBinding) - QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); - if (d->origRightBinding) - QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding); - if (d->origHCenterBinding) - QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); - if (d->origTopBinding) - QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding); - if (d->origBottomBinding) - QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); - if (d->origVCenterBinding) - QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); - if (d->origBaselineBinding) - QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); - - //restore any absolute geometry changed by the state's anchors - QSGAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QSGAnchors::Vertical_Mask; - QSGAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QSGAnchors::Vertical_Mask; - QSGAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QSGAnchors::Horizontal_Mask; - QSGAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QSGAnchors::Horizontal_Mask; - - bool stateSetWidth = (stateHAnchors && - stateHAnchors != QSGAnchors::LeftAnchor && - stateHAnchors != QSGAnchors::RightAnchor && - stateHAnchors != QSGAnchors::HCenterAnchor); - bool origSetWidth = (origHAnchors && - origHAnchors != QSGAnchors::LeftAnchor && - origHAnchors != QSGAnchors::RightAnchor && - origHAnchors != QSGAnchors::HCenterAnchor); - if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) - d->target->setWidth(d->origWidth.value); - - bool stateSetHeight = (stateVAnchors && - stateVAnchors != QSGAnchors::TopAnchor && - stateVAnchors != QSGAnchors::BottomAnchor && - stateVAnchors != QSGAnchors::VCenterAnchor && - stateVAnchors != QSGAnchors::BaselineAnchor); - bool origSetHeight = (origVAnchors && - origVAnchors != QSGAnchors::TopAnchor && - origVAnchors != QSGAnchors::BottomAnchor && - origVAnchors != QSGAnchors::VCenterAnchor && - origVAnchors != QSGAnchors::BaselineAnchor); - if (d->origHeight.isValid() && stateSetHeight && !origSetHeight) - d->target->setHeight(d->origHeight.value); - - if (stateHAnchors && !origHAnchors) - d->target->setX(d->origX); - - if (stateVAnchors && !origVAnchors) - d->target->setY(d->origY); -} - -QString QSGAnchorChanges::typeName() const -{ - return QLatin1String("AnchorChanges"); -} - -QList QSGAnchorChanges::additionalActions() -{ - Q_D(QSGAnchorChanges); - QList extra; - - QSGAnchors::Anchors combined = d->anchorSet->d_func()->usedAnchors | d->anchorSet->d_func()->resetAnchors; - bool hChange = combined & QSGAnchors::Horizontal_Mask; - bool vChange = combined & QSGAnchors::Vertical_Mask; - - if (d->target) { - QDeclarativeAction a; - if (hChange && d->fromX != d->toX) { - a.property = QDeclarativeProperty(d->target, QLatin1String("x")); - a.toValue = d->toX; - extra << a; - } - if (vChange && d->fromY != d->toY) { - a.property = QDeclarativeProperty(d->target, QLatin1String("y")); - a.toValue = d->toY; - extra << a; - } - if (hChange && d->fromWidth != d->toWidth) { - a.property = QDeclarativeProperty(d->target, QLatin1String("width")); - a.toValue = d->toWidth; - extra << a; - } - if (vChange && d->fromHeight != d->toHeight) { - a.property = QDeclarativeProperty(d->target, QLatin1String("height")); - a.toValue = d->toHeight; - extra << a; - } - } - - return extra; -} - -bool QSGAnchorChanges::changesBindings() -{ - return true; -} - -void QSGAnchorChanges::saveOriginals() -{ - Q_D(QSGAnchorChanges); - if (!d->target) - return; - - d->origLeftBinding = QDeclarativePropertyPrivate::binding(d->leftProp); - d->origRightBinding = QDeclarativePropertyPrivate::binding(d->rightProp); - d->origHCenterBinding = QDeclarativePropertyPrivate::binding(d->hCenterProp); - d->origTopBinding = QDeclarativePropertyPrivate::binding(d->topProp); - d->origBottomBinding = QDeclarativePropertyPrivate::binding(d->bottomProp); - d->origVCenterBinding = QDeclarativePropertyPrivate::binding(d->vCenterProp); - d->origBaselineBinding = QDeclarativePropertyPrivate::binding(d->baselineProp); - - QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target); - if (targetPrivate->widthValid) - d->origWidth = d->target->width(); - if (targetPrivate->heightValid) - d->origHeight = d->target->height(); - d->origX = d->target->x(); - d->origY = d->target->y(); - - d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop - = d->applyOrigBottom = d->applyOrigVCenter = d->applyOrigBaseline = false; - - saveCurrentValues(); -} - -void QSGAnchorChanges::copyOriginals(QDeclarativeActionEvent *other) -{ - Q_D(QSGAnchorChanges); - QSGAnchorChanges *ac = static_cast(other); - QSGAnchorChangesPrivate *acp = ac->d_func(); - - QSGAnchors::Anchors combined = acp->anchorSet->d_func()->usedAnchors | - acp->anchorSet->d_func()->resetAnchors; - - //probably also need to revert some things - d->applyOrigLeft = (combined & QSGAnchors::LeftAnchor); - d->applyOrigRight = (combined & QSGAnchors::RightAnchor); - d->applyOrigHCenter = (combined & QSGAnchors::HCenterAnchor); - d->applyOrigTop = (combined & QSGAnchors::TopAnchor); - d->applyOrigBottom = (combined & QSGAnchors::BottomAnchor); - d->applyOrigVCenter = (combined & QSGAnchors::VCenterAnchor); - d->applyOrigBaseline = (combined & QSGAnchors::BaselineAnchor); - - d->origLeftBinding = acp->origLeftBinding; - d->origRightBinding = acp->origRightBinding; - d->origHCenterBinding = acp->origHCenterBinding; - d->origTopBinding = acp->origTopBinding; - d->origBottomBinding = acp->origBottomBinding; - d->origVCenterBinding = acp->origVCenterBinding; - d->origBaselineBinding = acp->origBaselineBinding; - - d->origWidth = acp->origWidth; - d->origHeight = acp->origHeight; - d->origX = acp->origX; - d->origY = acp->origY; - - d->oldBindings.clear(); - d->oldBindings << acp->leftBinding << acp->rightBinding << acp->hCenterBinding - << acp->topBinding << acp->bottomBinding << acp->baselineBinding; - - saveCurrentValues(); -} - -void QSGAnchorChanges::clearBindings() -{ - Q_D(QSGAnchorChanges); - if (!d->target) - return; - - //### should this (saving "from" values) be moved to saveCurrentValues()? - d->fromX = d->target->x(); - d->fromY = d->target->y(); - d->fromWidth = d->target->width(); - d->fromHeight = d->target->height(); - - QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target); - //reset any anchors with corresponding reverts - //reset any anchors that have been specified as "undefined" - //reset any anchors that we'll be setting in the state - QSGAnchors::Anchors combined = d->anchorSet->d_func()->resetAnchors | - d->anchorSet->d_func()->usedAnchors; - if (d->applyOrigLeft || (combined & QSGAnchors::LeftAnchor)) { - targetPrivate->anchors()->resetLeft(); - QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); - } - if (d->applyOrigRight || (combined & QSGAnchors::RightAnchor)) { - targetPrivate->anchors()->resetRight(); - QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); - } - if (d->applyOrigHCenter || (combined & QSGAnchors::HCenterAnchor)) { - targetPrivate->anchors()->resetHorizontalCenter(); - QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); - } - if (d->applyOrigTop || (combined & QSGAnchors::TopAnchor)) { - targetPrivate->anchors()->resetTop(); - QDeclarativePropertyPrivate::setBinding(d->topProp, 0); - } - if (d->applyOrigBottom || (combined & QSGAnchors::BottomAnchor)) { - targetPrivate->anchors()->resetBottom(); - QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); - } - if (d->applyOrigVCenter || (combined & QSGAnchors::VCenterAnchor)) { - targetPrivate->anchors()->resetVerticalCenter(); - QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); - } - if (d->applyOrigBaseline || (combined & QSGAnchors::BaselineAnchor)) { - targetPrivate->anchors()->resetBaseline(); - QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); - } -} - -bool QSGAnchorChanges::override(QDeclarativeActionEvent*other) -{ - if (other->typeName() != QLatin1String("AnchorChanges")) - return false; - if (static_cast(this) == other) - return true; - if (static_cast(other)->object() == object()) - return true; - return false; -} - -void QSGAnchorChanges::rewind() -{ - Q_D(QSGAnchorChanges); - if (!d->target) - return; - - QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target); - - //restore previous values (but not previous bindings, i.e. anchors) - d->target->setX(d->rewindX); - d->target->setY(d->rewindY); - if (targetPrivate->widthValid) { - d->target->setWidth(d->rewindWidth); - } - if (targetPrivate->heightValid) { - d->target->setHeight(d->rewindHeight); - } -} - -void QSGAnchorChanges::saveCurrentValues() -{ - Q_D(QSGAnchorChanges); - if (!d->target) - return; - - QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target); - d->rewindLeft = targetPrivate->anchors()->left(); - d->rewindRight = targetPrivate->anchors()->right(); - d->rewindHCenter = targetPrivate->anchors()->horizontalCenter(); - d->rewindTop = targetPrivate->anchors()->top(); - d->rewindBottom = targetPrivate->anchors()->bottom(); - d->rewindVCenter = targetPrivate->anchors()->verticalCenter(); - d->rewindBaseline = targetPrivate->anchors()->baseline(); - - d->rewindX = d->target->x(); - d->rewindY = d->target->y(); - d->rewindWidth = d->target->width(); - d->rewindHeight = d->target->height(); -} - -void QSGAnchorChanges::saveTargetValues() -{ - Q_D(QSGAnchorChanges); - if (!d->target) - return; - - d->toX = d->target->x(); - d->toY = d->target->y(); - d->toWidth = d->target->width(); - d->toHeight = d->target->height(); -} - -#include - -QT_END_NAMESPACE - diff --git a/src/declarative/items/qsgstateoperations_p.h b/src/declarative/items/qsgstateoperations_p.h deleted file mode 100644 index 0a4a8f5e03..0000000000 --- a/src/declarative/items/qsgstateoperations_p.h +++ /dev/null @@ -1,275 +0,0 @@ -// Commit: 84c47bbb133304d7ef35642fa1fbb17619d4a43d -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGSTATEOPERATIONS_H -#define QSGSTATEOPERATIONS_H - -#include "qsgitem.h" -#include "qsganchors_p.h" - -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGParentChangePrivate; -class Q_AUTOTEST_EXPORT QSGParentChange : public QDeclarativeStateOperation, public QDeclarativeActionEvent -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGParentChange) - - Q_PROPERTY(QSGItem *target READ object WRITE setObject) - Q_PROPERTY(QSGItem *parent READ parent WRITE setParent) - Q_PROPERTY(QDeclarativeScriptString x READ x WRITE setX) - Q_PROPERTY(QDeclarativeScriptString y READ y WRITE setY) - Q_PROPERTY(QDeclarativeScriptString width READ width WRITE setWidth) - Q_PROPERTY(QDeclarativeScriptString height READ height WRITE setHeight) - Q_PROPERTY(QDeclarativeScriptString scale READ scale WRITE setScale) - Q_PROPERTY(QDeclarativeScriptString rotation READ rotation WRITE setRotation) -public: - QSGParentChange(QObject *parent=0); - ~QSGParentChange(); - - QSGItem *object() const; - void setObject(QSGItem *); - - QSGItem *parent() const; - void setParent(QSGItem *); - - QSGItem *originalParent() const; - - QDeclarativeScriptString x() const; - void setX(QDeclarativeScriptString x); - bool xIsSet() const; - - QDeclarativeScriptString y() const; - void setY(QDeclarativeScriptString y); - bool yIsSet() const; - - QDeclarativeScriptString width() const; - void setWidth(QDeclarativeScriptString width); - bool widthIsSet() const; - - QDeclarativeScriptString height() const; - void setHeight(QDeclarativeScriptString height); - bool heightIsSet() const; - - QDeclarativeScriptString scale() const; - void setScale(QDeclarativeScriptString scale); - bool scaleIsSet() const; - - QDeclarativeScriptString rotation() const; - void setRotation(QDeclarativeScriptString rotation); - bool rotationIsSet() const; - - virtual ActionList actions(); - - virtual void saveOriginals(); - //virtual void copyOriginals(QDeclarativeActionEvent*); - virtual void execute(Reason reason = ActualChange); - virtual bool isReversable(); - virtual void reverse(Reason reason = ActualChange); - virtual QString typeName() const; - virtual bool override(QDeclarativeActionEvent*other); - virtual void rewind(); - virtual void saveCurrentValues(); -}; - -class QSGAnchorChanges; -class QSGAnchorSetPrivate; -class Q_AUTOTEST_EXPORT QSGAnchorSet : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QDeclarativeScriptString left READ left WRITE setLeft RESET resetLeft) - Q_PROPERTY(QDeclarativeScriptString right READ right WRITE setRight RESET resetRight) - Q_PROPERTY(QDeclarativeScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter) - Q_PROPERTY(QDeclarativeScriptString top READ top WRITE setTop RESET resetTop) - Q_PROPERTY(QDeclarativeScriptString bottom READ bottom WRITE setBottom RESET resetBottom) - Q_PROPERTY(QDeclarativeScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter) - Q_PROPERTY(QDeclarativeScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline) - //Q_PROPERTY(QSGItem *fill READ fill WRITE setFill RESET resetFill) - //Q_PROPERTY(QSGItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn) - - /*Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) - Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged) - Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged) - Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged()) - Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged) - Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged) - Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged()) - Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged())*/ - -public: - QSGAnchorSet(QObject *parent=0); - virtual ~QSGAnchorSet(); - - QDeclarativeScriptString left() const; - void setLeft(const QDeclarativeScriptString &edge); - void resetLeft(); - - QDeclarativeScriptString right() const; - void setRight(const QDeclarativeScriptString &edge); - void resetRight(); - - QDeclarativeScriptString horizontalCenter() const; - void setHorizontalCenter(const QDeclarativeScriptString &edge); - void resetHorizontalCenter(); - - QDeclarativeScriptString top() const; - void setTop(const QDeclarativeScriptString &edge); - void resetTop(); - - QDeclarativeScriptString bottom() const; - void setBottom(const QDeclarativeScriptString &edge); - void resetBottom(); - - QDeclarativeScriptString verticalCenter() const; - void setVerticalCenter(const QDeclarativeScriptString &edge); - void resetVerticalCenter(); - - QDeclarativeScriptString baseline() const; - void setBaseline(const QDeclarativeScriptString &edge); - void resetBaseline(); - - QSGItem *fill() const; - void setFill(QSGItem *); - void resetFill(); - - QSGItem *centerIn() const; - void setCenterIn(QSGItem *); - void resetCenterIn(); - - /*qreal leftMargin() const; - void setLeftMargin(qreal); - - qreal rightMargin() const; - void setRightMargin(qreal); - - qreal horizontalCenterOffset() const; - void setHorizontalCenterOffset(qreal); - - qreal topMargin() const; - void setTopMargin(qreal); - - qreal bottomMargin() const; - void setBottomMargin(qreal); - - qreal margins() const; - void setMargins(qreal); - - qreal verticalCenterOffset() const; - void setVerticalCenterOffset(qreal); - - qreal baselineOffset() const; - void setBaselineOffset(qreal);*/ - - QSGAnchors::Anchors usedAnchors() const; - -/*Q_SIGNALS: - void leftMarginChanged(); - void rightMarginChanged(); - void topMarginChanged(); - void bottomMarginChanged(); - void marginsChanged(); - void verticalCenterOffsetChanged(); - void horizontalCenterOffsetChanged(); - void baselineOffsetChanged();*/ - -private: - friend class QSGAnchorChanges; - Q_DISABLE_COPY(QSGAnchorSet) - Q_DECLARE_PRIVATE(QSGAnchorSet) -}; - -class QSGAnchorChangesPrivate; -class Q_AUTOTEST_EXPORT QSGAnchorChanges : public QDeclarativeStateOperation, public QDeclarativeActionEvent -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGAnchorChanges) - - Q_PROPERTY(QSGItem *target READ object WRITE setObject) - Q_PROPERTY(QSGAnchorSet *anchors READ anchors CONSTANT) - -public: - QSGAnchorChanges(QObject *parent=0); - ~QSGAnchorChanges(); - - virtual ActionList actions(); - - QSGAnchorSet *anchors(); - - QSGItem *object() const; - void setObject(QSGItem *); - - virtual void execute(Reason reason = ActualChange); - virtual bool isReversable(); - virtual void reverse(Reason reason = ActualChange); - virtual QString typeName() const; - virtual bool override(QDeclarativeActionEvent*other); - virtual bool changesBindings(); - virtual void saveOriginals(); - virtual bool needsCopy() { return true; } - virtual void copyOriginals(QDeclarativeActionEvent*); - virtual void clearBindings(); - virtual void rewind(); - virtual void saveCurrentValues(); - - QList additionalActions(); - virtual void saveTargetValues(); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGParentChange) -QML_DECLARE_TYPE(QSGAnchorSet) -QML_DECLARE_TYPE(QSGAnchorChanges) - -QT_END_HEADER - -#endif // QSGSTATEOPERATIONS_H - diff --git a/src/declarative/items/qsgtext.cpp b/src/declarative/items/qsgtext.cpp deleted file mode 100644 index 6c55e82802..0000000000 --- a/src/declarative/items/qsgtext.cpp +++ /dev/null @@ -1,1938 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgtext_p.h" -#include "qsgtext_p_p.h" - -#include -#include -#include -#include "qsgtextnode_p.h" -#include "qsgimage_p_p.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; - -class QSGTextDocumentWithImageResources : public QTextDocument { - Q_OBJECT - -public: - QSGTextDocumentWithImageResources(QSGText *parent); - virtual ~QSGTextDocumentWithImageResources(); - - void setText(const QString &); - int resourcesLoading() const { return outstanding; } - -protected: - QVariant loadResource(int type, const QUrl &name); - -private slots: - void requestFinished(); - -private: - QHash m_resources; - - int outstanding; - static QSet errors; -}; - -DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) -DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE); - -QString QSGTextPrivate::elideChar = QString(0x2026); - -QSGTextPrivate::QSGTextPrivate() -: color((QRgb)0), style(QSGText::Normal), hAlign(QSGText::AlignLeft), - vAlign(QSGText::AlignTop), elideMode(QSGText::ElideNone), - format(QSGText::AutoText), wrapMode(QSGText::NoWrap), lineHeight(1), - lineHeightMode(QSGText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX), - maximumLineCountValid(false), - texture(0), - imageCacheDirty(false), updateOnComponentComplete(true), - richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false), - requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false), - layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true), - naturalWidth(0), doc(0), textLine(0), nodeType(NodeIsNull) - -#if defined(Q_OS_MAC) -, layoutThread(0), paintingThread(0) -#endif - -{ - cacheAllTextAsImage = enableImageCache(); -} - -void QSGTextPrivate::init() -{ - Q_Q(QSGText); - q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFlag(QSGItem::ItemHasContents); -} - -QSGTextDocumentWithImageResources::QSGTextDocumentWithImageResources(QSGText *parent) -: QTextDocument(parent), outstanding(0) -{ - setUndoRedoEnabled(false); -} - -QSGTextDocumentWithImageResources::~QSGTextDocumentWithImageResources() -{ - if (!m_resources.isEmpty()) - qDeleteAll(m_resources); -} - -QVariant QSGTextDocumentWithImageResources::loadResource(int type, const QUrl &name) -{ - QDeclarativeContext *context = qmlContext(parent()); - QUrl url = context->resolvedUrl(name); - - if (type == QTextDocument::ImageResource) { - QHash::Iterator iter = m_resources.find(url); - - if (iter == m_resources.end()) { - QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url); - iter = m_resources.insert(name, p); - - if (p->isLoading()) { - p->connectFinished(this, SLOT(requestFinished())); - outstanding++; - } - } - - QDeclarativePixmap *p = *iter; - if (p->isReady()) { - return p->pixmap(); - } else if (p->isError()) { - if (!errors.contains(url)) { - errors.insert(url); - qmlInfo(parent()) << p->error(); - } - } - } - - return QTextDocument::loadResource(type,url); // The *resolved* URL -} - -void QSGTextDocumentWithImageResources::requestFinished() -{ - outstanding--; - if (outstanding == 0) { - QSGText *textItem = static_cast(parent()); - QString text = textItem->text(); -#ifndef QT_NO_TEXTHTMLPARSER - setHtml(text); -#else - setPlainText(text); -#endif - QSGTextPrivate *d = QSGTextPrivate::get(textItem); - d->updateLayout(); - } -} - -void QSGTextDocumentWithImageResources::setText(const QString &text) -{ - if (!m_resources.isEmpty()) { - qDeleteAll(m_resources); - m_resources.clear(); - outstanding = 0; - } - -#ifndef QT_NO_TEXTHTMLPARSER - setHtml(text); -#else - setPlainText(text); -#endif -} - -QSet QSGTextDocumentWithImageResources::errors; - -QSGTextPrivate::~QSGTextPrivate() -{ - delete textLine; textLine = 0; -} - -qreal QSGTextPrivate::getImplicitWidth() const -{ - if (!requireImplicitWidth) { - // We don't calculate implicitWidth unless it is required. - // We need to force a size update now to ensure implicitWidth is calculated - QSGTextPrivate *me = const_cast(this); - me->requireImplicitWidth = true; - me->updateSize(); - } - return implicitWidth; -} - -void QSGTextPrivate::updateLayout() -{ - Q_Q(QSGText); - if (!q->isComponentComplete()) { - updateOnComponentComplete = true; - return; - } - - layoutTextElided = false; - // Setup instance of QTextLayout for all cases other than richtext - if (!richText) { - layout.clearLayout(); - layout.setFont(font); - if (!styledText) { - QString tmp = text; - tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); - singleline = !tmp.contains(QChar::LineSeparator); - if (singleline && !maximumLineCountValid && elideMode != QSGText::ElideNone && q->widthValid()) { - QFontMetrics fm(font); - tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); - if (tmp != text) { - layoutTextElided = true; - if (!truncated) { - truncated = true; - emit q->truncatedChanged(); - } - } - } - layout.setText(tmp); - } else { - singleline = false; - if (textHasChanged) { - QDeclarativeStyledText::parse(text, layout); - textHasChanged = false; - } - } - } else { - ensureDoc(); - QTextBlockFormat::LineHeightTypes type; - type = lineHeightMode == QSGText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight; - QTextBlockFormat blockFormat; - blockFormat.setLineHeight((lineHeightMode == QSGText::FixedHeight ? lineHeight : lineHeight * 100), type); - for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) { - QTextCursor cursor(it); - cursor.mergeBlockFormat(blockFormat); - } - } - - updateSize(); -} - -void QSGTextPrivate::updateSize() -{ - Q_Q(QSGText); - - if (!q->isComponentComplete()) { - updateOnComponentComplete = true; - return; - } - - if (!requireImplicitWidth) { - emit q->implicitWidthChanged(); - // if the implicitWidth is used, then updateSize() has already been called (recursively) - if (requireImplicitWidth) - return; - } - - invalidateImageCache(); - - QFontMetrics fm(font); - if (text.isEmpty()) { - q->setImplicitWidth(0); - q->setImplicitHeight(fm.height()); - paintedSize = QSize(0, fm.height()); - emit q->paintedSizeChanged(); - q->update(); - return; - } - - int dy = q->height(); - QSize size(0, 0); - -#if defined(Q_OS_MAC) - layoutThread = QThread::currentThread(); -#endif - - //setup instance of QTextLayout for all cases other than richtext - if (!richText) { - QRect textRect = setupTextLayout(); - layedOutTextRect = textRect; - size = textRect.size(); - dy -= size.height(); - } else { - singleline = false; // richtext can't elide or be optimized for single-line case - ensureDoc(); - doc->setDefaultFont(font); - QSGText::HAlignment horizontalAlignment = q->effectiveHAlign(); - if (rightToLeftText) { - if (horizontalAlignment == QSGText::AlignLeft) - horizontalAlignment = QSGText::AlignRight; - else if (horizontalAlignment == QSGText::AlignRight) - horizontalAlignment = QSGText::AlignLeft; - } - QTextOption option; - option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign)); - option.setWrapMode(QTextOption::WrapMode(wrapMode)); - if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField()) - option.setUseDesignMetrics(true); - doc->setDefaultTextOption(option); - if (requireImplicitWidth && q->widthValid()) { - doc->setTextWidth(-1); - naturalWidth = doc->idealWidth(); - } - if (wrapMode != QSGText::NoWrap && q->widthValid()) - doc->setTextWidth(q->width()); - else - doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug) - dy -= (int)doc->size().height(); - QSize dsize = doc->size().toSize(); - layedOutTextRect = QRect(QPoint(0,0), dsize); - size = QSize(int(doc->idealWidth()),dsize.height()); - } - int yoff = 0; - - if (q->heightValid()) { - if (vAlign == QSGText::AlignBottom) - yoff = dy; - else if (vAlign == QSGText::AlignVCenter) - yoff = dy/2; - } - q->setBaselineOffset(fm.ascent() + yoff); - - //### need to comfirm cost of always setting these for richText - internalWidthUpdate = true; - if (!q->widthValid()) - q->setImplicitWidth(size.width()); - else if (requireImplicitWidth) - q->setImplicitWidth(naturalWidth); - internalWidthUpdate = false; - - q->setImplicitHeight(size.height()); - if (paintedSize != size) { - paintedSize = size; - emit q->paintedSizeChanged(); - } - q->update(); -} - -QSGTextLine::QSGTextLine() - : QObject(), m_line(0), m_height(0) -{ -} - -void QSGTextLine::setLine(QTextLine *line) -{ - m_line = line; -} - -int QSGTextLine::number() const -{ - if (m_line) - return m_line->lineNumber(); - return 0; -} - -qreal QSGTextLine::width() const -{ - if (m_line) - return m_line->width(); - return 0; -} - -void QSGTextLine::setWidth(qreal width) -{ - if (m_line) - m_line->setLineWidth(width); -} - -qreal QSGTextLine::height() const -{ - if (m_height) - return m_height; - if (m_line) - return m_line->height(); - return 0; -} - -void QSGTextLine::setHeight(qreal height) -{ - if (m_line) - m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height)); - m_height = height; -} - -qreal QSGTextLine::x() const -{ - if (m_line) - return m_line->x(); - return 0; -} - -void QSGTextLine::setX(qreal x) -{ - if (m_line) - m_line->setPosition(QPointF(x, m_line->y())); -} - -qreal QSGTextLine::y() const -{ - if (m_line) - return m_line->y(); - return 0; -} - -void QSGTextLine::setY(qreal y) -{ - if (m_line) - m_line->setPosition(QPointF(m_line->x(), y)); -} - -void QSGText::doLayout() -{ - Q_D(QSGText); - d->updateSize(); -} - -/*! - \qmlsignal QtQuick2::Text::onLineLaidOut(line) - - This handler is called for every line during the layout process. - This gives the opportunity to position and resize a line as it is being laid out. - It can for example be used to create columns or lay out text around objects. - - The properties of a line are: - \list - \o number (read-only) - \o x - \o y - \o width - \o height - \endlist - - For example, this will move the first 5 lines of a text element by 100 pixels to the right: - \code - onLineLaidOut: { - if (line.number < 5) { - line.x = line.x + 100 - line.width = line.width - 100 - } - } - \endcode -*/ - -bool QSGTextPrivate::isLineLaidOutConnected() -{ - static int idx = this->signalIndex("lineLaidOut(QSGTextLine*)"); - return this->isSignalConnected(idx); -} - -void QSGTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, qreal elideWidth = 0) -{ - Q_Q(QSGText); - -#if defined(Q_OS_MAC) - if (QThread::currentThread() != paintingThread) { -#endif - if (!line.lineNumber()) - linesRects.clear(); - - if (!textLine) - textLine = new QSGTextLine; - textLine->setLine(&line); - textLine->setY(height); - textLine->setHeight(0); - - // use the text item's width by default if it has one and wrap is on - if (q->widthValid() && q->wrapMode() != QSGText::NoWrap) - textLine->setWidth(q->width() - elideWidth); - else - textLine->setWidth(INT_MAX); - if (lineHeight != 1.0) - textLine->setHeight((lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight); - - emit q->lineLaidOut(textLine); - - linesRects << QRectF(textLine->x(), textLine->y(), textLine->width(), textLine->height()); - height += textLine->height(); - -#if defined(Q_OS_MAC) - } else { - if (line.lineNumber() < linesRects.count()) { - QRectF r = linesRects.at(line.lineNumber()); - line.setLineWidth(r.width()); - line.setPosition(r.topLeft()); - } - } -#endif -} - -/*! - Lays out the QSGTextPrivate::layout QTextLayout in the constraints of the QSGText. - - Returns the size of the final text. This can be used to position the text vertically (the text is - already absolutely positioned horizontally). -*/ -QRect QSGTextPrivate::setupTextLayout() -{ - // ### text layout handling should be profiled and optimized as needed - // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine); - Q_Q(QSGText); - layout.setCacheEnabled(true); - - qreal lineWidth = 0; - int visibleCount = 0; - - //set manual width - if (q->widthValid()) - lineWidth = q->width(); - - QTextOption textOption = layout.textOption(); - textOption.setAlignment(Qt::Alignment(q->effectiveHAlign())); - textOption.setWrapMode(QTextOption::WrapMode(wrapMode)); - if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField()) - textOption.setUseDesignMetrics(true); - layout.setTextOption(textOption); - - bool elideText = false; - bool truncate = false; - - QFontMetrics fm(layout.font()); - elidePos = QPointF(); - - if (requireImplicitWidth && q->widthValid()) { - // requires an extra layout - QString elidedText; - if (layoutTextElided) { - // We have provided elided text to the layout, but we must calculate unelided width. - elidedText = layout.text(); - layout.setText(text); - } - layout.beginLayout(); - forever { - QTextLine line = layout.createLine(); - if (!line.isValid()) - break; - } - layout.endLayout(); - QRectF br; - for (int i = 0; i < layout.lineCount(); ++i) { - QTextLine line = layout.lineAt(i); - br = br.united(line.naturalTextRect()); - } - naturalWidth = br.width(); - if (layoutTextElided) - layout.setText(elidedText); - } - - qreal height = 0; - bool customLayout = isLineLaidOutConnected(); - - if (maximumLineCountValid) { - layout.beginLayout(); - if (!lineWidth) - lineWidth = INT_MAX; - int linesLeft = maximumLineCount; - int visibleTextLength = 0; - while (linesLeft > 0) { - QTextLine line = layout.createLine(); - if (!line.isValid()) - break; - - visibleCount++; - - if (customLayout) - setupCustomLineGeometry(line, height); - else if (lineWidth) - line.setLineWidth(lineWidth); - visibleTextLength += line.textLength(); - - if (--linesLeft == 0) { - if (visibleTextLength < text.length()) { - truncate = true; - if (elideMode == QSGText::ElideRight && q->widthValid()) { - qreal elideWidth = fm.width(elideChar); - // Need to correct for alignment - if (customLayout) - setupCustomLineGeometry(line, height, elideWidth); - else - line.setLineWidth(lineWidth - elideWidth); - if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) { - line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y())); - elidePos.setX(line.naturalTextRect().left() - elideWidth); - } else { - elidePos.setX(line.naturalTextRect().right()); - } - elideText = true; - } - } - } - } - layout.endLayout(); - - //Update truncated - if (truncated != truncate) { - truncated = truncate; - emit q->truncatedChanged(); - } - } else { - layout.beginLayout(); - forever { - QTextLine line = layout.createLine(); - if (!line.isValid()) - break; - visibleCount++; - if (customLayout) - setupCustomLineGeometry(line, height); - else { - if (lineWidth) - line.setLineWidth(lineWidth); - } - } - layout.endLayout(); - } - - height = 0; - QRectF br; - for (int i = 0; i < layout.lineCount(); ++i) { - QTextLine line = layout.lineAt(i); - // set line spacing - if (!customLayout) - line.setPosition(QPointF(line.position().x(), height)); - if (elideText && i == layout.lineCount()-1) { - elidePos.setY(height + fm.ascent()); - br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent()))); - } - br = br.united(line.naturalTextRect()); - height += (lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight; - } - if (!customLayout) - br.setHeight(height); - - if (!q->widthValid()) - naturalWidth = br.width(); - - //Update the number of visible lines - if (lineCount != visibleCount) { - lineCount = visibleCount; - emit q->lineCountChanged(); - } - - return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height())); -} - -/*! - Returns a painted version of the QSGTextPrivate::layout QTextLayout. - If \a drawStyle is true, the style color overrides all colors in the document. -*/ -QPixmap QSGTextPrivate::textLayoutImage(bool drawStyle) -{ - QSize size = layedOutTextRect.size(); - - //paint text - QPixmap img(size); - if (!size.isEmpty()) { - img.fill(Qt::transparent); -#ifdef Q_WS_MAC - bool oldSmooth = qt_applefontsmoothing_enabled; - qt_applefontsmoothing_enabled = false; -#endif - QPainter p(&img); -#ifdef Q_WS_MAC - qt_applefontsmoothing_enabled = oldSmooth; -#endif - drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle); - } - return img; -} - -/*! - Paints the QSGTextPrivate::layout QTextLayout into \a painter at \a pos. If - \a drawStyle is true, the style color overrides all colors in the document. -*/ -void QSGTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle) -{ - if (drawStyle) - painter->setPen(styleColor); - else - painter->setPen(color); - painter->setFont(font); - layout.draw(painter, pos); - if (!elidePos.isNull()) - painter->drawText(pos + elidePos, elideChar); -} - -/*! - Returns a painted version of the QSGTextPrivate::doc QTextDocument. - If \a drawStyle is true, the style color overrides all colors in the document. -*/ -QPixmap QSGTextPrivate::textDocumentImage(bool drawStyle) -{ - QSize size = doc->size().toSize(); - - //paint text - QPixmap img(size); - img.fill(Qt::transparent); -#ifdef Q_WS_MAC - bool oldSmooth = qt_applefontsmoothing_enabled; - qt_applefontsmoothing_enabled = false; -#endif - QPainter p(&img); -#ifdef Q_WS_MAC - qt_applefontsmoothing_enabled = oldSmooth; -#endif - - QAbstractTextDocumentLayout::PaintContext context; - - QTextOption oldOption(doc->defaultTextOption()); - if (drawStyle) { - context.palette.setColor(QPalette::Text, styleColor); - QTextOption colorOption(doc->defaultTextOption()); - colorOption.setFlags(QTextOption::SuppressColors); - doc->setDefaultTextOption(colorOption); - } else { - context.palette.setColor(QPalette::Text, color); - } - doc->documentLayout()->draw(&p, context); - if (drawStyle) - doc->setDefaultTextOption(oldOption); - return img; -} - -/*! - Mark the image cache as dirty. -*/ -void QSGTextPrivate::invalidateImageCache() -{ - Q_Q(QSGText); - - if (richTextAsImage || cacheAllTextAsImage || (qmlDisableDistanceField() && style != QSGText::Normal)) { // If actually using the image cache - if (imageCacheDirty) - return; - - imageCacheDirty = true; - - if (q->isComponentComplete()) - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - } else if (q->isComponentComplete()) - q->update(); -} - -/*! - Tests if the image cache is dirty, and repaints it if it is. -*/ -void QSGTextPrivate::checkImageCache() -{ - Q_Q(QSGText); - - if (!imageCacheDirty) - return; - - if (text.isEmpty()) { - - imageCache = QPixmap(); - - } else { - - QPixmap textImage; - QPixmap styledImage; - - if (richText) { - textImage = textDocumentImage(false); - if (style != QSGText::Normal) - styledImage = textDocumentImage(true); //### should use styleColor - } else { - textImage = textLayoutImage(false); - if (style != QSGText::Normal) - styledImage = textLayoutImage(true); //### should use styleColor - } - - switch (style) { - case QSGText::Outline: - imageCache = drawOutline(textImage, styledImage); - break; - case QSGText::Sunken: - imageCache = drawOutline(textImage, styledImage, -1); - break; - case QSGText::Raised: - imageCache = drawOutline(textImage, styledImage, 1); - break; - default: - imageCache = textImage; - break; - } - - } - - imageCacheDirty = false; - textureImageCacheDirty = true; - q->update(); -} - -/*! - Ensures the QSGTextPrivate::doc variable is set to a valid text document -*/ -void QSGTextPrivate::ensureDoc() -{ - if (!doc) { - Q_Q(QSGText); - doc = new QSGTextDocumentWithImageResources(q); - doc->setDocumentMargin(0); - } -} - -/*! - Draw \a styleSource as an outline around \a source and return the new image. -*/ -QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource) -{ - QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); - img.fill(Qt::transparent); - - QPainter ppm(&img); - - QPoint pos(0, 0); - pos += QPoint(-1, 0); - ppm.drawPixmap(pos, styleSource); - pos += QPoint(2, 0); - ppm.drawPixmap(pos, styleSource); - pos += QPoint(-1, -1); - ppm.drawPixmap(pos, styleSource); - pos += QPoint(0, 2); - ppm.drawPixmap(pos, styleSource); - - pos += QPoint(0, -1); - ppm.drawPixmap(pos, source); - ppm.end(); - - return img; -} - -/*! - Draw \a styleSource below \a source at \a yOffset and return the new image. -*/ -QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset) -{ - QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); - img.fill(Qt::transparent); - - QPainter ppm(&img); - - ppm.drawPixmap(QPoint(0, yOffset), styleSource); - ppm.drawPixmap(0, 0, source); - - ppm.end(); - - return img; -} - -/*! - \qmlclass Text QSGText - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The Text item allows you to add formatted text to a scene. - \inherits Item - - Text items can display both plain and rich text. For example, red text with - a specific font and size can be defined like this: - - \qml - Text { - text: "Hello World!" - font.family: "Helvetica" - font.pointSize: 24 - color: "red" - } - \endqml - - Rich text is defined using HTML-style markup: - - \qml - Text { - text: "Hello World!" - } - \endqml - - \image declarative-text.png - - If height and width are not explicitly set, Text will attempt to determine how - much room is needed and set it accordingly. Unless \l wrapMode is set, it will always - prefer width to height (all text will be placed on a single line). - - The \l elide property can alternatively be used to fit a single line of - plain text to a set width. - - Note that the \l{Supported HTML Subset} is limited. Also, if the text contains - HTML img tags that load remote images, the text is reloaded. - - Text provides read-only text. For editable text, see \l TextEdit. - - \sa {declarative/text/fonts}{Fonts example} -*/ -QSGText::QSGText(QSGItem *parent) -: QSGImplicitSizeItem(*(new QSGTextPrivate), parent) -{ - Q_D(QSGText); - d->init(); -} - -QSGText::~QSGText() -{ -} - -/*! - \qmlproperty bool QtQuick2::Text::clip - This property holds whether the text is clipped. - - Note that if the text does not fit in the bounding rectangle it will be abruptly chopped. - - If you want to display potentially long text in a limited space, you probably want to use \c elide instead. -*/ - -/*! - \qmlproperty bool QtQuick2::Text::smooth - - This property holds whether the text is smoothly scaled or transformed. - - Smooth filtering gives better visual quality, but is slower. If - the item is displayed at its natural size, this property has no visual or - performance effect. - - \note Generally scaling artifacts are only visible if the item is stationary on - the screen. A common pattern when animating an item is to disable smooth - filtering at the beginning of the animation and reenable it at the conclusion. -*/ - -/*! - \qmlsignal QtQuick2::Text::onLinkActivated(string link) - - This handler is called when the user clicks on a link embedded in the text. - The link must be in rich text or HTML format and the - \a link string provides access to the particular link. - - \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0 - - The example code will display the text - "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}." - - Clicking on the highlighted link will output - \tt{http://qt.nokia.com link activated} to the console. -*/ - -/*! - \qmlproperty string QtQuick2::Text::font.family - - Sets the family name of the font. - - The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". - If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. - If the family isn't available a family will be set using the font matching algorithm. -*/ - -/*! - \qmlproperty bool QtQuick2::Text::font.bold - - Sets whether the font weight is bold. -*/ - -/*! - \qmlproperty enumeration QtQuick2::Text::font.weight - - Sets the font's weight. - - The weight can be one of: - \list - \o Font.Light - \o Font.Normal - the default - \o Font.DemiBold - \o Font.Bold - \o Font.Black - \endlist - - \qml - Text { text: "Hello"; font.weight: Font.DemiBold } - \endqml -*/ - -/*! - \qmlproperty bool QtQuick2::Text::font.italic - - Sets whether the font has an italic style. -*/ - -/*! - \qmlproperty bool QtQuick2::Text::font.underline - - Sets whether the text is underlined. -*/ - -/*! - \qmlproperty bool QtQuick2::Text::font.strikeout - - Sets whether the font has a strikeout style. -*/ - -/*! - \qmlproperty real QtQuick2::Text::font.pointSize - - Sets the font size in points. The point size must be greater than zero. -*/ - -/*! - \qmlproperty int QtQuick2::Text::font.pixelSize - - Sets the font size in pixels. - - Using this function makes the font device dependent. - Use \c pointSize to set the size of the font in a device independent manner. -*/ - -/*! - \qmlproperty real QtQuick2::Text::font.letterSpacing - - Sets the letter spacing for the font. - - Letter spacing changes the default spacing between individual letters in the font. - A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. -*/ - -/*! - \qmlproperty real QtQuick2::Text::font.wordSpacing - - Sets the word spacing for the font. - - Word spacing changes the default spacing between individual words. - A positive value increases the word spacing by a corresponding amount of pixels, - while a negative value decreases the inter-word spacing accordingly. -*/ - -/*! - \qmlproperty enumeration QtQuick2::Text::font.capitalization - - Sets the capitalization for the text. - - \list - \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. - \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. - \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. - \o Font.SmallCaps - This alters the text to be rendered in small-caps type. - \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. - \endlist - - \qml - Text { text: "Hello"; font.capitalization: Font.AllLowercase } - \endqml -*/ -QFont QSGText::font() const -{ - Q_D(const QSGText); - return d->sourceFont; -} - -void QSGText::setFont(const QFont &font) -{ - Q_D(QSGText); - if (d->sourceFont == font) - return; - - d->sourceFont = font; - QFont oldFont = d->font; - d->font = font; - - if (d->font.pointSizeF() != -1) { - // 0.5pt resolution - qreal size = qRound(d->font.pointSizeF()*2.0); - d->font.setPointSizeF(size/2.0); - } - - if (oldFont != d->font) - d->updateLayout(); - - emit fontChanged(d->sourceFont); -} - -/*! - \qmlproperty string QtQuick2::Text::text - - The text to display. Text supports both plain and rich text strings. - - The item will try to automatically determine whether the text should - be treated as styled text. This determination is made using Qt::mightBeRichText(). -*/ -QString QSGText::text() const -{ - Q_D(const QSGText); - return d->text; -} - -void QSGText::setText(const QString &n) -{ - Q_D(QSGText); - if (d->text == n) - return; - - d->richText = d->format == RichText; - d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n)); - d->text = n; - if (isComponentComplete()) { - if (d->richText) { - d->ensureDoc(); - d->doc->setText(n); - d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); - d->richTextAsImage = enableImageCache(); - } else { - d->rightToLeftText = d->text.isRightToLeft(); - } - d->determineHorizontalAlignment(); - } - d->textHasChanged = true; - d->updateLayout(); - emit textChanged(d->text); -} - -/*! - \qmlproperty color QtQuick2::Text::color - - The text color. - - An example of green text defined using hexadecimal notation: - \qml - Text { - color: "#00FF00" - text: "green text" - } - \endqml - - An example of steel blue text defined using an SVG color name: - \qml - Text { - color: "steelblue" - text: "blue text" - } - \endqml -*/ -QColor QSGText::color() const -{ - Q_D(const QSGText); - return d->color; -} - -void QSGText::setColor(const QColor &color) -{ - Q_D(QSGText); - if (d->color == color) - return; - - d->color = color; - d->invalidateImageCache(); - emit colorChanged(d->color); -} -/*! - \qmlproperty enumeration QtQuick2::Text::style - - Set an additional text style. - - Supported text styles are: - \list - \o Text.Normal - the default - \o Text.Outline - \o Text.Raised - \o Text.Sunken - \endlist - - \qml - Row { - Text { font.pointSize: 24; text: "Normal" } - Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" } - Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" } - Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" } - } - \endqml - - \image declarative-textstyle.png -*/ -QSGText::TextStyle QSGText::style() const -{ - Q_D(const QSGText); - return d->style; -} - -void QSGText::setStyle(QSGText::TextStyle style) -{ - Q_D(QSGText); - if (d->style == style) - return; - - // changing to/from Normal requires the boundingRect() to change - if (isComponentComplete() && (d->style == Normal || style == Normal)) - update(); - d->style = style; - d->invalidateImageCache(); - emit styleChanged(d->style); -} - -/*! - \qmlproperty color QtQuick2::Text::styleColor - - Defines the secondary color used by text styles. - - \c styleColor is used as the outline color for outlined text, and as the - shadow color for raised or sunken text. If no style has been set, it is not - used at all. - - \qml - Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" } - \endqml - - \sa style - */ -QColor QSGText::styleColor() const -{ - Q_D(const QSGText); - return d->styleColor; -} - -void QSGText::setStyleColor(const QColor &color) -{ - Q_D(QSGText); - if (d->styleColor == color) - return; - - d->styleColor = color; - d->invalidateImageCache(); - emit styleColorChanged(d->styleColor); -} - -/*! - \qmlproperty enumeration QtQuick2::Text::horizontalAlignment - \qmlproperty enumeration QtQuick2::Text::verticalAlignment - \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment - - Sets the horizontal and vertical alignment of the text within the Text items - width and height. By default, the text is vertically aligned to the top. Horizontal - alignment follows the natural alignment of the text, for example text that is read - from left to right will be aligned to the left. - - The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and - \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom - and \c Text.AlignVCenter. - - Note that for a single line of text, the size of the text is the area of the text. In this common case, - all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will - need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to - that of the parent. - - When using the attached property LayoutMirroring::enabled to mirror application - layouts, the horizontal alignment of text will also be mirrored. However, the property - \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment - of Text, use the read-only property \c effectiveHorizontalAlignment. -*/ -QSGText::HAlignment QSGText::hAlign() const -{ - Q_D(const QSGText); - return d->hAlign; -} - -void QSGText::setHAlign(HAlignment align) -{ - Q_D(QSGText); - bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; - d->hAlignImplicit = false; - if (d->setHAlign(align, forceAlign) && isComponentComplete()) - d->updateLayout(); -} - -void QSGText::resetHAlign() -{ - Q_D(QSGText); - d->hAlignImplicit = true; - if (d->determineHorizontalAlignment() && isComponentComplete()) - d->updateLayout(); -} - -QSGText::HAlignment QSGText::effectiveHAlign() const -{ - Q_D(const QSGText); - QSGText::HAlignment effectiveAlignment = d->hAlign; - if (!d->hAlignImplicit && d->effectiveLayoutMirror) { - switch (d->hAlign) { - case QSGText::AlignLeft: - effectiveAlignment = QSGText::AlignRight; - break; - case QSGText::AlignRight: - effectiveAlignment = QSGText::AlignLeft; - break; - default: - break; - } - } - return effectiveAlignment; -} - -bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign) -{ - Q_Q(QSGText); - if (hAlign != alignment || forceAlign) { - QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); - hAlign = alignment; - - emit q->horizontalAlignmentChanged(hAlign); - if (oldEffectiveHAlign != q->effectiveHAlign()) - emit q->effectiveHorizontalAlignmentChanged(); - return true; - } - return false; -} - -bool QSGTextPrivate::determineHorizontalAlignment() -{ - Q_Q(QSGText); - if (hAlignImplicit && q->isComponentComplete()) { - bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; - return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft); - } - return false; -} - -void QSGTextPrivate::mirrorChange() -{ - Q_Q(QSGText); - if (q->isComponentComplete()) { - if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) { - updateLayout(); - emit q->effectiveHorizontalAlignmentChanged(); - } - } -} - -QTextDocument *QSGTextPrivate::textDocument() -{ - return doc; -} - -QSGText::VAlignment QSGText::vAlign() const -{ - Q_D(const QSGText); - return d->vAlign; -} - -void QSGText::setVAlign(VAlignment align) -{ - Q_D(QSGText); - if (d->vAlign == align) - return; - - d->vAlign = align; - emit verticalAlignmentChanged(align); -} - -/*! - \qmlproperty enumeration QtQuick2::Text::wrapMode - - Set this property to wrap the text to the Text item's width. The text will only - wrap if an explicit width has been set. wrapMode can be one of: - - \list - \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width. - \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width. - \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. - \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. - \endlist -*/ -QSGText::WrapMode QSGText::wrapMode() const -{ - Q_D(const QSGText); - return d->wrapMode; -} - -void QSGText::setWrapMode(WrapMode mode) -{ - Q_D(QSGText); - if (mode == d->wrapMode) - return; - - d->wrapMode = mode; - d->updateLayout(); - - emit wrapModeChanged(); -} - -/*! - \qmlproperty int QtQuick2::Text::lineCount - - Returns the number of lines visible in the text item. - - This property is not supported for rich text. - - \sa maximumLineCount -*/ -int QSGText::lineCount() const -{ - Q_D(const QSGText); - return d->lineCount; -} - -/*! - \qmlproperty bool QtQuick2::Text::truncated - - Returns true if the text has been truncated due to \l maximumLineCount - or \l elide. - - This property is not supported for rich text. - - \sa maximumLineCount, elide -*/ -bool QSGText::truncated() const -{ - Q_D(const QSGText); - return d->truncated; -} - -/*! - \qmlproperty int QtQuick2::Text::maximumLineCount - - Set this property to limit the number of lines that the text item will show. - If elide is set to Text.ElideRight, the text will be elided appropriately. - By default, this is the value of the largest possible integer. - - This property is not supported for rich text. - - \sa lineCount, elide -*/ -int QSGText::maximumLineCount() const -{ - Q_D(const QSGText); - return d->maximumLineCount; -} - -void QSGText::setMaximumLineCount(int lines) -{ - Q_D(QSGText); - - d->maximumLineCountValid = lines==INT_MAX ? false : true; - if (d->maximumLineCount != lines) { - d->maximumLineCount = lines; - d->updateLayout(); - emit maximumLineCountChanged(); - } -} - -void QSGText::resetMaximumLineCount() -{ - Q_D(QSGText); - setMaximumLineCount(INT_MAX); - d->elidePos = QPointF(); - if (d->truncated != false) { - d->truncated = false; - emit truncatedChanged(); - } -} - -/*! - \qmlproperty enumeration QtQuick2::Text::textFormat - - The way the text property should be displayed. - - Supported text formats are: - - \list - \o Text.AutoText (default) - \o Text.PlainText - \o Text.StyledText - \o Text.RichText - \endlist - - If the text format is \c Text.AutoText the text element - will automatically determine whether the text should be treated as - styled text. This determination is made using Qt::mightBeRichText(). - - Text.StyledText is an optimized format supporting some basic text - styling markup, in the style of html 3.2: - - \code - - bold - - italic -
      - new line -

      - paragraph - - underlined text - -

      to
      - headers - - anchor -
        ,
          and
        • - ordered and unordered lists - > < & - \endcode - - \c Text.StyledText parser is strict, requiring tags to be correctly nested. - - \table - \row - \o - \qml -Column { - Text { - font.pointSize: 24 - text: "Hello World!" - } - Text { - font.pointSize: 24 - textFormat: Text.RichText - text: "Hello World!" - } - Text { - font.pointSize: 24 - textFormat: Text.PlainText - text: "Hello World!" - } -} - \endqml - \o \image declarative-textformat.png - \endtable -*/ -QSGText::TextFormat QSGText::textFormat() const -{ - Q_D(const QSGText); - return d->format; -} - -void QSGText::setTextFormat(TextFormat format) -{ - Q_D(QSGText); - if (format == d->format) - return; - d->format = format; - bool wasRich = d->richText; - d->richText = format == RichText; - d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text)); - - if (!wasRich && d->richText && isComponentComplete()) { - d->ensureDoc(); - d->doc->setText(d->text); - d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); - d->richTextAsImage = enableImageCache(); - } else { - d->rightToLeftText = d->text.isRightToLeft(); - } - d->determineHorizontalAlignment(); - d->updateLayout(); - - emit textFormatChanged(d->format); -} - -/*! - \qmlproperty enumeration QtQuick2::Text::elide - - Set this property to elide parts of the text fit to the Text item's width. - The text will only elide if an explicit width has been set. - - This property cannot be used with rich text. - - Eliding can be: - \list - \o Text.ElideNone - the default - \o Text.ElideLeft - \o Text.ElideMiddle - \o Text.ElideRight - \endlist - - If this property is set to Text.ElideRight, it can be used with multiline - text. The text will only elide if maximumLineCount has been set. - - If the text is a multi-length string, and the mode is not \c Text.ElideNone, - the first string that fits will be used, otherwise the last will be elided. - - Multi-length strings are ordered from longest to shortest, separated by the - Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}). -*/ -QSGText::TextElideMode QSGText::elideMode() const -{ - Q_D(const QSGText); - return d->elideMode; -} - -void QSGText::setElideMode(QSGText::TextElideMode mode) -{ - Q_D(QSGText); - if (mode == d->elideMode) - return; - - d->elideMode = mode; - d->updateLayout(); - - emit elideModeChanged(d->elideMode); -} - -/*! \internal */ -QRectF QSGText::boundingRect() const -{ - Q_D(const QSGText); - - QRect rect = d->layedOutTextRect; - if (d->style != Normal) - rect.adjust(-1, 0, 1, 2); - - // Could include font max left/right bearings to either side of rectangle. - - int h = height(); - switch (d->vAlign) { - case AlignTop: - break; - case AlignBottom: - rect.moveTop(h - rect.height()); - break; - case AlignVCenter: - rect.moveTop((h - rect.height()) / 2); - break; - } - - return QRectF(rect); -} - -/*! \internal */ -void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_D(QSGText); - if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width()) - && (d->wrapMode != QSGText::NoWrap - || d->elideMode != QSGText::ElideNone - || d->hAlign != QSGText::AlignLeft)) { - if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) { - // We need to re-elide - d->updateLayout(); - } else { - // We just need to re-layout - d->updateSize(); - } - } - - QSGItem::geometryChanged(newGeometry, oldGeometry); -} - -QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) -{ - Q_UNUSED(data); - Q_D(QSGText); - - if (d->text.isEmpty()) { - delete oldNode; - return 0; - } - - QRectF bounds = boundingRect(); - - // We need to make sure the layout is done in the current thread -#if defined(Q_OS_MAC) - d->paintingThread = QThread::currentThread(); - if (d->layoutThread != d->paintingThread) - d->updateLayout(); -#endif - - // XXX todo - some styled text can be done by the QSGTextNode - if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) { - bool wasDirty = d->textureImageCacheDirty; - d->textureImageCacheDirty = false; - - if (d->imageCache.isNull()) { - delete oldNode; - return 0; - } - - QSGImageNode *node = 0; - if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) { - delete oldNode; - node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode(); - d->texture = new QSGPlainTexture(); - wasDirty = true; - d->nodeType = QSGTextPrivate::NodeIsTexture; - } else { - node = static_cast(oldNode); - Q_ASSERT(d->texture); - } - - if (wasDirty) { - qobject_cast(d->texture)->setImage(d->imageCache.toImage()); - node->setTexture(0); - node->setTexture(d->texture); - } - - node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height())); - node->setSourceRect(QRectF(0, 0, 1, 1)); - node->setHorizontalWrapMode(QSGTexture::ClampToEdge); - node->setVerticalWrapMode(QSGTexture::ClampToEdge); - node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that.. - node->update(); - - return node; - - } else { - QSGTextNode *node = 0; - if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) { - delete oldNode; - node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext()); - d->nodeType = QSGTextPrivate::NodeIsText; - } else { - node = static_cast(oldNode); - } - - node->deleteContent(); - node->setMatrix(QMatrix4x4()); - - if (d->richText) { - d->ensureDoc(); - node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor); - - } else { - node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor); - } - - return node; - } -} - -bool QSGText::event(QEvent *e) -{ - Q_D(QSGText); - if (e->type() == QEvent::User) { - d->checkImageCache(); - return true; - } else { - return QSGImplicitSizeItem::event(e); - } -} - -/*! - \qmlproperty real QtQuick2::Text::paintedWidth - - Returns the width of the text, including width past the width - which is covered due to insufficient wrapping if WrapMode is set. -*/ -qreal QSGText::paintedWidth() const -{ - Q_D(const QSGText); - return d->paintedSize.width(); -} - -/*! - \qmlproperty real QtQuick2::Text::paintedHeight - - Returns the height of the text, including height past the height - which is covered due to there being more text than fits in the set height. -*/ -qreal QSGText::paintedHeight() const -{ - Q_D(const QSGText); - return d->paintedSize.height(); -} - -/*! - \qmlproperty real QtQuick2::Text::lineHeight - - Sets the line height for the text. - The value can be in pixels or a multiplier depending on lineHeightMode. - - The default value is a multiplier of 1.0. - The line height must be a positive value. -*/ -qreal QSGText::lineHeight() const -{ - Q_D(const QSGText); - return d->lineHeight; -} - -void QSGText::setLineHeight(qreal lineHeight) -{ - Q_D(QSGText); - - if ((d->lineHeight == lineHeight) || (lineHeight < 0.0)) - return; - - d->lineHeight = lineHeight; - d->updateLayout(); - emit lineHeightChanged(lineHeight); -} - -/*! - \qmlproperty enumeration QtQuick2::Text::lineHeightMode - - This property determines how the line height is specified. - The possible values are: - - \list - \o Text.ProportionalHeight (default) - this sets the spacing proportional to the - line (as a multiplier). For example, set to 2 for double spacing. - \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels). - \endlist -*/ -QSGText::LineHeightMode QSGText::lineHeightMode() const -{ - Q_D(const QSGText); - return d->lineHeightMode; -} - -void QSGText::setLineHeightMode(LineHeightMode mode) -{ - Q_D(QSGText); - if (mode == d->lineHeightMode) - return; - - d->lineHeightMode = mode; - d->updateLayout(); - - emit lineHeightModeChanged(mode); -} - -/*! - Returns the number of resources (images) that are being loaded asynchronously. -*/ -int QSGText::resourcesLoading() const -{ - Q_D(const QSGText); - return d->doc ? d->doc->resourcesLoading() : 0; -} - -/*! \internal */ -void QSGText::componentComplete() -{ - Q_D(QSGText); - QSGItem::componentComplete(); - if (d->updateOnComponentComplete) { - d->updateOnComponentComplete = false; - if (d->richText) { - d->ensureDoc(); - d->doc->setText(d->text); - d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); - d->richTextAsImage = enableImageCache(); - } else { - d->rightToLeftText = d->text.isRightToLeft(); - } - d->determineHorizontalAlignment(); - d->updateLayout(); - } -} - - -QString QSGTextPrivate::anchorAt(const QPointF &mousePos) -{ - if (styledText) { - for (int i = 0; i < layout.lineCount(); ++i) { - QTextLine line = layout.lineAt(i); - if (line.naturalTextRect().contains(mousePos)) { - int charPos = line.xToCursor(mousePos.x()); - foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) { - if (formatRange.format.isAnchor() - && charPos >= formatRange.start - && charPos <= formatRange.start + formatRange.length) { - return formatRange.format.anchorHref(); - } - } - break; - } - } - } - return QString(); -} - -bool QSGTextPrivate::isLinkActivatedConnected() -{ - static int idx = this->signalIndex("linkActivated(QString)"); - return this->isSignalConnected(idx); -} - -/*! \internal */ -void QSGText::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGText); - - if (d->isLinkActivatedConnected()) { - if (d->styledText) - d->activeLink = d->anchorAt(event->localPos()); - else if (d->richText && d->doc) - d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos()); - } - - if (d->activeLink.isEmpty()) - event->setAccepted(false); - - // ### may malfunction if two of the same links are clicked & dragged onto each other) - - if (!event->isAccepted()) - QSGItem::mousePressEvent(event); - -} - -/*! \internal */ -void QSGText::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGText); - - // ### confirm the link, and send a signal out - - QString link; - if (d->isLinkActivatedConnected()) { - if (d->styledText) - link = d->anchorAt(event->localPos()); - else if (d->richText && d->doc) - link = d->doc->documentLayout()->anchorAt(event->localPos()); - } - - if (!link.isEmpty() && d->activeLink == link) - emit linkActivated(d->activeLink); - else - event->setAccepted(false); - - if (!event->isAccepted()) - QSGItem::mouseReleaseEvent(event); -} - -QT_END_NAMESPACE - -#include "qsgtext.moc" diff --git a/src/declarative/items/qsgtext_p.h b/src/declarative/items/qsgtext_p.h deleted file mode 100644 index aa07c8a25e..0000000000 --- a/src/declarative/items/qsgtext_p.h +++ /dev/null @@ -1,252 +0,0 @@ -// Commit: 27e4302b7f45f22180693d26747f419177c81e27 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTEXT_P_H -#define QSGTEXT_P_H - -#include "qsgimplicitsizeitem_p.h" - -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) -class QSGTextPrivate; -class QSGTextLine; -class Q_DECLARATIVE_PRIVATE_EXPORT QSGText : public QSGImplicitSizeItem -{ - Q_OBJECT - Q_ENUMS(HAlignment) - Q_ENUMS(VAlignment) - Q_ENUMS(TextStyle) - Q_ENUMS(TextFormat) - Q_ENUMS(TextElideMode) - Q_ENUMS(WrapMode) - Q_ENUMS(LineHeightMode) - - Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) - Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(TextStyle style READ style WRITE setStyle NOTIFY styleChanged) - Q_PROPERTY(QColor styleColor READ styleColor WRITE setStyleColor NOTIFY styleColorChanged) - Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) - Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged) - Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged) - Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) - Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged) - Q_PROPERTY(bool truncated READ truncated NOTIFY truncatedChanged) - Q_PROPERTY(int maximumLineCount READ maximumLineCount WRITE setMaximumLineCount NOTIFY maximumLineCountChanged RESET resetMaximumLineCount) - - Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged) - Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode? - Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) - Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) - Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged) - Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged) - -public: - QSGText(QSGItem *parent=0); - ~QSGText(); - - enum HAlignment { AlignLeft = Qt::AlignLeft, - AlignRight = Qt::AlignRight, - AlignHCenter = Qt::AlignHCenter, - AlignJustify = Qt::AlignJustify }; - enum VAlignment { AlignTop = Qt::AlignTop, - AlignBottom = Qt::AlignBottom, - AlignVCenter = Qt::AlignVCenter }; - enum TextStyle { Normal, - Outline, - Raised, - Sunken }; - enum TextFormat { PlainText = Qt::PlainText, - RichText = Qt::RichText, - AutoText = Qt::AutoText, - StyledText = 4 }; - enum TextElideMode { ElideLeft = Qt::ElideLeft, - ElideRight = Qt::ElideRight, - ElideMiddle = Qt::ElideMiddle, - ElideNone = Qt::ElideNone }; - - enum WrapMode { NoWrap = QTextOption::NoWrap, - WordWrap = QTextOption::WordWrap, - WrapAnywhere = QTextOption::WrapAnywhere, - WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT - Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere - }; - - enum LineHeightMode { ProportionalHeight, FixedHeight }; - - QString text() const; - void setText(const QString &); - - QFont font() const; - void setFont(const QFont &font); - - QColor color() const; - void setColor(const QColor &c); - - TextStyle style() const; - void setStyle(TextStyle style); - - QColor styleColor() const; - void setStyleColor(const QColor &c); - - HAlignment hAlign() const; - void setHAlign(HAlignment align); - void resetHAlign(); - HAlignment effectiveHAlign() const; - - VAlignment vAlign() const; - void setVAlign(VAlignment align); - - WrapMode wrapMode() const; - void setWrapMode(WrapMode w); - - int lineCount() const; - bool truncated() const; - - int maximumLineCount() const; - void setMaximumLineCount(int lines); - void resetMaximumLineCount(); - - TextFormat textFormat() const; - void setTextFormat(TextFormat format); - - TextElideMode elideMode() const; - void setElideMode(TextElideMode); - - qreal lineHeight() const; - void setLineHeight(qreal lineHeight); - - LineHeightMode lineHeightMode() const; - void setLineHeightMode(LineHeightMode); - - virtual void componentComplete(); - - int resourcesLoading() const; // mainly for testing - - qreal paintedWidth() const; - qreal paintedHeight() const; - - QRectF boundingRect() const; - Q_INVOKABLE void doLayout(); - -Q_SIGNALS: - void textChanged(const QString &text); - void linkActivated(const QString &link); - void fontChanged(const QFont &font); - void colorChanged(const QColor &color); - void styleChanged(TextStyle style); - void styleColorChanged(const QColor &color); - void horizontalAlignmentChanged(HAlignment alignment); - void verticalAlignmentChanged(VAlignment alignment); - void wrapModeChanged(); - void lineCountChanged(); - void truncatedChanged(); - void maximumLineCountChanged(); - void textFormatChanged(TextFormat textFormat); - void elideModeChanged(TextElideMode mode); - void paintedSizeChanged(); - void lineHeightChanged(qreal lineHeight); - void lineHeightModeChanged(LineHeightMode mode); - void effectiveHorizontalAlignmentChanged(); - void lineLaidOut(QSGTextLine *line); - -protected: - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - virtual bool event(QEvent *); - -private: - Q_DISABLE_COPY(QSGText) - Q_DECLARE_PRIVATE(QSGText) -}; - -class QTextLine; -class Q_AUTOTEST_EXPORT QSGTextLine : public QObject -{ - Q_OBJECT - Q_PROPERTY(int number READ number) - Q_PROPERTY(qreal width READ width WRITE setWidth) - Q_PROPERTY(qreal height READ height WRITE setHeight) - Q_PROPERTY(qreal x READ x WRITE setX) - Q_PROPERTY(qreal y READ y WRITE setY) - -public: - QSGTextLine(); - - void setLine(QTextLine* line); - int number() const; - - qreal width() const; - void setWidth(qreal width); - - qreal height() const; - void setHeight(qreal height); - - qreal x() const; - void setX(qreal x); - - qreal y() const; - void setY(qreal y); - -private: - QTextLine *m_line; - qreal m_height; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGText) -QML_DECLARE_TYPE(QSGTextLine) - -QT_END_HEADER - -#endif // QSGTEXT_P_H diff --git a/src/declarative/items/qsgtext_p_p.h b/src/declarative/items/qsgtext_p_p.h deleted file mode 100644 index 8b83263f6f..0000000000 --- a/src/declarative/items/qsgtext_p_p.h +++ /dev/null @@ -1,168 +0,0 @@ -// Commit: 6e5a642c9484536fc173714f560f739944368cf5 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTEXT_P_P_H -#define QSGTEXT_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgitem.h" -#include "qsgimplicitsizeitem_p_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -class QTextLayout; -class QSGTextDocumentWithImageResources; -class QSGPlainTexture; - -class Q_AUTOTEST_EXPORT QSGTextPrivate : public QSGImplicitSizeItemPrivate -{ - Q_DECLARE_PUBLIC(QSGText) -public: - QSGTextPrivate(); - ~QSGTextPrivate(); - void init(); - - void updateSize(); - void updateLayout(); - bool determineHorizontalAlignment(); - bool setHAlign(QSGText::HAlignment, bool forceAlign = false); - void mirrorChange(); - QTextDocument *textDocument(); - bool isLineLaidOutConnected(); - - QString text; - QFont font; - QFont sourceFont; - QColor color; - QSGText::TextStyle style; - QColor styleColor; - QString activeLink; - QSGText::HAlignment hAlign; - QSGText::VAlignment vAlign; - QSGText::TextElideMode elideMode; - QSGText::TextFormat format; - QSGText::WrapMode wrapMode; - qreal lineHeight; - QSGText::LineHeightMode lineHeightMode; - int lineCount; - int maximumLineCount; - int maximumLineCountValid; - QPointF elidePos; - - static QString elideChar; - - void invalidateImageCache(); - void checkImageCache(); - QPixmap imageCache; - QSGTexture *texture; - - bool imageCacheDirty:1; - bool updateOnComponentComplete:1; - bool richText:1; - bool styledText:1; - bool singleline:1; - bool cacheAllTextAsImage:1; - bool internalWidthUpdate:1; - bool requireImplicitWidth:1; - bool truncated:1; - bool hAlignImplicit:1; - bool rightToLeftText:1; - bool layoutTextElided:1; - bool richTextAsImage:1; - bool textureImageCacheDirty:1; - bool textHasChanged:1; - - QRect layedOutTextRect; - QSize paintedSize; - qreal naturalWidth; - virtual qreal getImplicitWidth() const; - - void ensureDoc(); - QPixmap textDocumentImage(bool drawStyle); - QSGTextDocumentWithImageResources *doc; - - QRect setupTextLayout(); - void setupCustomLineGeometry(QTextLine &line, qreal &height, qreal elideWidth); - QPixmap textLayoutImage(bool drawStyle); - void drawTextLayout(QPainter *p, const QPointF &pos, bool drawStyle); - bool isLinkActivatedConnected(); - QString anchorAt(const QPointF &pos); - QTextLayout layout; - QList linesRects; - QSGTextLine *textLine; - - static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource); - static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset); - - static inline QSGTextPrivate *get(QSGText *t) { - return t->d_func(); - } - - enum NodeType { - NodeIsNull, - NodeIsTexture, - NodeIsText - }; - NodeType nodeType; - -#if defined(Q_OS_MAC) - QThread *layoutThread; - QThread *paintingThread; -#endif -}; - -QT_END_NAMESPACE - -#endif // QSGTEXT_P_P_H diff --git a/src/declarative/items/qsgtextedit.cpp b/src/declarative/items/qsgtextedit.cpp deleted file mode 100644 index bbc751f99c..0000000000 --- a/src/declarative/items/qsgtextedit.cpp +++ /dev/null @@ -1,1976 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgtextedit_p.h" -#include "qsgtextedit_p_p.h" -#include "qsgevents_p_p.h" -#include "qsgcanvas.h" -#include "qsgtextnode_p.h" -#include "qsgsimplerectnode.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) -DEFINE_BOOL_CONFIG_OPTION(qmlEnableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE) - -/*! - \qmlclass TextEdit QSGTextEdit - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The TextEdit item displays multiple lines of editable formatted text. - \inherits Item - - The TextEdit item displays a block of editable, formatted text. - - It can display both plain and rich text. For example: - - \qml -TextEdit { - width: 240 - text: "Hello World!" - font.family: "Helvetica" - font.pointSize: 20 - color: "blue" - focus: true -} - \endqml - - \image declarative-textedit.gif - - Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus. - - Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific - to a look-and-feel. For example, to add flickable scrolling that follows the cursor: - - \snippet snippets/declarative/texteditor.qml 0 - - A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible - scrollbar, or a scrollbar that fades in to show location, etc. - - Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can - be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely - from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord(). - - You can translate between cursor positions (characters from the start of the document) and pixel - points using positionAt() and positionToRectangle(). - - \sa Text, TextInput, {declarative/text/textselection}{Text Selection example} -*/ - -/*! - \qmlsignal QtQuick2::TextEdit::onLinkActivated(string link) - - This handler is called when the user clicks on a link embedded in the text. - The link must be in rich text or HTML format and the - \a link string provides access to the particular link. -*/ -QSGTextEdit::QSGTextEdit(QSGItem *parent) -: QSGImplicitSizeItem(*(new QSGTextEditPrivate), parent) -{ - Q_D(QSGTextEdit); - d->init(); -} - -QString QSGTextEdit::text() const -{ - Q_D(const QSGTextEdit); - -#ifndef QT_NO_TEXTHTMLPARSER - if (d->richText) - return d->document->toHtml(); - else -#endif - return d->document->toPlainText(); -} - -/*! - \qmlproperty string QtQuick2::TextEdit::font.family - - Sets the family name of the font. - - The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". - If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. - If the family isn't available a family will be set using the font matching algorithm. -*/ - -/*! - \qmlproperty bool QtQuick2::TextEdit::font.bold - - Sets whether the font weight is bold. -*/ - -/*! - \qmlproperty enumeration QtQuick2::TextEdit::font.weight - - Sets the font's weight. - - The weight can be one of: - \list - \o Font.Light - \o Font.Normal - the default - \o Font.DemiBold - \o Font.Bold - \o Font.Black - \endlist - - \qml - TextEdit { text: "Hello"; font.weight: Font.DemiBold } - \endqml -*/ - -/*! - \qmlproperty bool QtQuick2::TextEdit::font.italic - - Sets whether the font has an italic style. -*/ - -/*! - \qmlproperty bool QtQuick2::TextEdit::font.underline - - Sets whether the text is underlined. -*/ - -/*! - \qmlproperty bool QtQuick2::TextEdit::font.strikeout - - Sets whether the font has a strikeout style. -*/ - -/*! - \qmlproperty real QtQuick2::TextEdit::font.pointSize - - Sets the font size in points. The point size must be greater than zero. -*/ - -/*! - \qmlproperty int QtQuick2::TextEdit::font.pixelSize - - Sets the font size in pixels. - - Using this function makes the font device dependent. Use - \l{TextEdit::font.pointSize} to set the size of the font in a - device independent manner. -*/ - -/*! - \qmlproperty real QtQuick2::TextEdit::font.letterSpacing - - Sets the letter spacing for the font. - - Letter spacing changes the default spacing between individual letters in the font. - A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. -*/ - -/*! - \qmlproperty real QtQuick2::TextEdit::font.wordSpacing - - Sets the word spacing for the font. - - Word spacing changes the default spacing between individual words. - A positive value increases the word spacing by a corresponding amount of pixels, - while a negative value decreases the inter-word spacing accordingly. -*/ - -/*! - \qmlproperty enumeration QtQuick2::TextEdit::font.capitalization - - Sets the capitalization for the text. - - \list - \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. - \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. - \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. - \o Font.SmallCaps - This alters the text to be rendered in small-caps type. - \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. - \endlist - - \qml - TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase } - \endqml -*/ - -/*! - \qmlproperty string QtQuick2::TextEdit::text - - The text to display. If the text format is AutoText the text edit will - automatically determine whether the text should be treated as - rich text. This determination is made using Qt::mightBeRichText(). -*/ -void QSGTextEdit::setText(const QString &text) -{ - Q_D(QSGTextEdit); - if (QSGTextEdit::text() == text) - return; - - d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text)); - if (d->richText) { -#ifndef QT_NO_TEXTHTMLPARSER - d->control->setHtml(text); -#else - d->control->setPlainText(text); -#endif - d->useImageFallback = qmlEnableImageCache(); - } else { - d->control->setPlainText(text); - } - q_textChanged(); -} - -/*! - \qmlproperty enumeration QtQuick2::TextEdit::textFormat - - The way the text property should be displayed. - - \list - \o TextEdit.AutoText - \o TextEdit.PlainText - \o TextEdit.RichText - \endlist - - The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit - will automatically determine whether the text should be treated as - rich text. This determination is made using Qt::mightBeRichText(). - - \table - \row - \o - \qml -Column { - TextEdit { - font.pointSize: 24 - text: "Hello World!" - } - TextEdit { - font.pointSize: 24 - textFormat: TextEdit.RichText - text: "Hello World!" - } - TextEdit { - font.pointSize: 24 - textFormat: TextEdit.PlainText - text: "Hello World!" - } -} - \endqml - \o \image declarative-textformat.png - \endtable -*/ -QSGTextEdit::TextFormat QSGTextEdit::textFormat() const -{ - Q_D(const QSGTextEdit); - return d->format; -} - -void QSGTextEdit::setTextFormat(TextFormat format) -{ - Q_D(QSGTextEdit); - if (format == d->format) - return; - bool wasRich = d->richText; - d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); - - if (wasRich && !d->richText) { - d->control->setPlainText(d->text); - updateSize(); - } else if (!wasRich && d->richText) { -#ifndef QT_NO_TEXTHTMLPARSER - d->control->setHtml(d->text); -#else - d->control->setPlainText(d->text); -#endif - updateSize(); - d->useImageFallback = qmlEnableImageCache(); - } - d->format = format; - d->control->setAcceptRichText(d->format != PlainText); - emit textFormatChanged(d->format); -} - -QFont QSGTextEdit::font() const -{ - Q_D(const QSGTextEdit); - return d->sourceFont; -} - -void QSGTextEdit::setFont(const QFont &font) -{ - Q_D(QSGTextEdit); - if (d->sourceFont == font) - return; - - d->sourceFont = font; - QFont oldFont = d->font; - d->font = font; - if (d->font.pointSizeF() != -1) { - // 0.5pt resolution - qreal size = qRound(d->font.pointSizeF()*2.0); - d->font.setPointSizeF(size/2.0); - } - - if (oldFont != d->font) { - d->document->setDefaultFont(d->font); - if (d->cursor) { - d->cursor->setHeight(QFontMetrics(d->font).height()); - moveCursorDelegate(); - } - updateSize(); - updateDocument(); - } - emit fontChanged(d->sourceFont); -} - -/*! - \qmlproperty color QtQuick2::TextEdit::color - - The text color. - - \qml - // green text using hexadecimal notation - TextEdit { color: "#00FF00" } - \endqml - - \qml - // steelblue text using SVG color name - TextEdit { color: "steelblue" } - \endqml -*/ -QColor QSGTextEdit::color() const -{ - Q_D(const QSGTextEdit); - return d->color; -} - -void QSGTextEdit::setColor(const QColor &color) -{ - Q_D(QSGTextEdit); - if (d->color == color) - return; - - d->color = color; - QPalette pal = d->control->palette(); - pal.setColor(QPalette::Text, color); - d->control->setPalette(pal); - updateDocument(); - emit colorChanged(d->color); -} - -/*! - \qmlproperty color QtQuick2::TextEdit::selectionColor - - The text highlight color, used behind selections. -*/ -QColor QSGTextEdit::selectionColor() const -{ - Q_D(const QSGTextEdit); - return d->selectionColor; -} - -void QSGTextEdit::setSelectionColor(const QColor &color) -{ - Q_D(QSGTextEdit); - if (d->selectionColor == color) - return; - - d->selectionColor = color; - QPalette pal = d->control->palette(); - pal.setColor(QPalette::Highlight, color); - d->control->setPalette(pal); - updateDocument(); - emit selectionColorChanged(d->selectionColor); -} - -/*! - \qmlproperty color QtQuick2::TextEdit::selectedTextColor - - The selected text color, used in selections. -*/ -QColor QSGTextEdit::selectedTextColor() const -{ - Q_D(const QSGTextEdit); - return d->selectedTextColor; -} - -void QSGTextEdit::setSelectedTextColor(const QColor &color) -{ - Q_D(QSGTextEdit); - if (d->selectedTextColor == color) - return; - - d->selectedTextColor = color; - QPalette pal = d->control->palette(); - pal.setColor(QPalette::HighlightedText, color); - d->control->setPalette(pal); - updateDocument(); - emit selectedTextColorChanged(d->selectedTextColor); -} - -/*! - \qmlproperty enumeration QtQuick2::TextEdit::horizontalAlignment - \qmlproperty enumeration QtQuick2::TextEdit::verticalAlignment - \qmlproperty enumeration QtQuick2::TextEdit::effectiveHorizontalAlignment - - Sets the horizontal and vertical alignment of the text within the TextEdit item's - width and height. By default, the text alignment follows the natural alignment - of the text, for example text that is read from left to right will be aligned to - the left. - - Valid values for \c horizontalAlignment are: - \list - \o TextEdit.AlignLeft (default) - \o TextEdit.AlignRight - \o TextEdit.AlignHCenter - \o TextEdit.AlignJustify - \endlist - - Valid values for \c verticalAlignment are: - \list - \o TextEdit.AlignTop (default) - \o TextEdit.AlignBottom - \o TextEdit.AlignVCenter - \endlist - - When using the attached property LayoutMirroring::enabled to mirror application - layouts, the horizontal alignment of text will also be mirrored. However, the property - \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment - of TextEdit, use the read-only property \c effectiveHorizontalAlignment. -*/ -QSGTextEdit::HAlignment QSGTextEdit::hAlign() const -{ - Q_D(const QSGTextEdit); - return d->hAlign; -} - -void QSGTextEdit::setHAlign(HAlignment align) -{ - Q_D(QSGTextEdit); - bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; - d->hAlignImplicit = false; - if (d->setHAlign(align, forceAlign) && isComponentComplete()) { - d->updateDefaultTextOption(); - updateSize(); - } -} - -void QSGTextEdit::resetHAlign() -{ - Q_D(QSGTextEdit); - d->hAlignImplicit = true; - if (d->determineHorizontalAlignment() && isComponentComplete()) { - d->updateDefaultTextOption(); - updateSize(); - } -} - -QSGTextEdit::HAlignment QSGTextEdit::effectiveHAlign() const -{ - Q_D(const QSGTextEdit); - QSGTextEdit::HAlignment effectiveAlignment = d->hAlign; - if (!d->hAlignImplicit && d->effectiveLayoutMirror) { - switch (d->hAlign) { - case QSGTextEdit::AlignLeft: - effectiveAlignment = QSGTextEdit::AlignRight; - break; - case QSGTextEdit::AlignRight: - effectiveAlignment = QSGTextEdit::AlignLeft; - break; - default: - break; - } - } - return effectiveAlignment; -} - -bool QSGTextEditPrivate::setHAlign(QSGTextEdit::HAlignment alignment, bool forceAlign) -{ - Q_Q(QSGTextEdit); - if (hAlign != alignment || forceAlign) { - QSGTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); - hAlign = alignment; - emit q->horizontalAlignmentChanged(alignment); - if (oldEffectiveHAlign != q->effectiveHAlign()) - emit q->effectiveHorizontalAlignmentChanged(); - return true; - } - return false; -} - -bool QSGTextEditPrivate::determineHorizontalAlignment() -{ - Q_Q(QSGTextEdit); - if (hAlignImplicit && q->isComponentComplete()) { - bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; - return setHAlign(alignToRight ? QSGTextEdit::AlignRight : QSGTextEdit::AlignLeft); - } - return false; -} - -void QSGTextEditPrivate::mirrorChange() -{ - Q_Q(QSGTextEdit); - if (q->isComponentComplete()) { - if (!hAlignImplicit && (hAlign == QSGTextEdit::AlignRight || hAlign == QSGTextEdit::AlignLeft)) { - updateDefaultTextOption(); - q->updateSize(); - emit q->effectiveHorizontalAlignmentChanged(); - } - } -} - -QSGTextEdit::VAlignment QSGTextEdit::vAlign() const -{ - Q_D(const QSGTextEdit); - return d->vAlign; -} - -void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment) -{ - Q_D(QSGTextEdit); - if (alignment == d->vAlign) - return; - d->vAlign = alignment; - d->updateDefaultTextOption(); - updateSize(); - moveCursorDelegate(); - emit verticalAlignmentChanged(d->vAlign); -} -/*! - \qmlproperty enumeration QtQuick2::TextEdit::wrapMode - - Set this property to wrap the text to the TextEdit item's width. - The text will only wrap if an explicit width has been set. - - \list - \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width. - \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width. - \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. - \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. - \endlist - - The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap. -*/ -QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const -{ - Q_D(const QSGTextEdit); - return d->wrapMode; -} - -void QSGTextEdit::setWrapMode(WrapMode mode) -{ - Q_D(QSGTextEdit); - if (mode == d->wrapMode) - return; - d->wrapMode = mode; - d->updateDefaultTextOption(); - updateSize(); - emit wrapModeChanged(); -} - -/*! - \qmlproperty int QtQuick2::TextEdit::lineCount - - Returns the total number of lines in the textEdit item. -*/ -int QSGTextEdit::lineCount() const -{ - Q_D(const QSGTextEdit); - return d->lineCount; -} - -/*! - \qmlproperty real QtQuick2::TextEdit::paintedWidth - - Returns the width of the text, including the width past the width - which is covered due to insufficient wrapping if \l wrapMode is set. -*/ -qreal QSGTextEdit::paintedWidth() const -{ - Q_D(const QSGTextEdit); - return d->paintedSize.width(); -} - -/*! - \qmlproperty real QtQuick2::TextEdit::paintedHeight - - Returns the height of the text, including the height past the height - that is covered if the text does not fit within the set height. -*/ -qreal QSGTextEdit::paintedHeight() const -{ - Q_D(const QSGTextEdit); - return d->paintedSize.height(); -} - -/*! - \qmlmethod rectangle QtQuick2::TextEdit::positionToRectangle(position) - - Returns the rectangle at the given \a position in the text. The x, y, - and height properties correspond to the cursor that would describe - that position. -*/ -QRectF QSGTextEdit::positionToRectangle(int pos) const -{ - Q_D(const QSGTextEdit); - QTextCursor c(d->document); - c.setPosition(pos); - return d->control->cursorRect(c); - -} - -/*! - \qmlmethod int QtQuick2::TextEdit::positionAt(int x, int y) - - Returns the text position closest to pixel position (\a x, \a y). - - Position 0 is before the first character, position 1 is after the first character - but before the second, and so on until position \l {text}.length, which is after all characters. -*/ -int QSGTextEdit::positionAt(int x, int y) const -{ - Q_D(const QSGTextEdit); - int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit); - QTextCursor cursor = d->control->textCursor(); - if (r > cursor.position()) { - // The cursor position includes positions within the preedit text, but only positions in the - // same text block are offset so it is possible to get a position that is either part of the - // preedit or the next text block. - QTextLayout *layout = cursor.block().layout(); - const int preeditLength = layout - ? layout->preeditAreaText().length() - : 0; - if (preeditLength > 0 - && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) { - r = r > cursor.position() + preeditLength - ? r - preeditLength - : cursor.position(); - } - } - return r; -} - -/*! - \qmlmethod void QtQuick2::TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters) - - Moves the cursor to \a position and updates the selection according to the optional \a mode - parameter. (To only move the cursor, set the \l cursorPosition property.) - - When this method is called it additionally sets either the - selectionStart or the selectionEnd (whichever was at the previous cursor position) - to the specified position. This allows you to easily extend and contract the selected - text range. - - The selection mode specifies whether the selection is updated on a per character or a per word - basis. If not specified the selection mode will default to TextEdit.SelectCharacters. - - \list - \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at - the previous cursor position) to the specified position. - \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all - words between the specified postion and the previous cursor position. Words partially in the - range are included. - \endlist - - For example, take this sequence of calls: - - \code - cursorPosition = 5 - moveCursorSelection(9, TextEdit.SelectCharacters) - moveCursorSelection(7, TextEdit.SelectCharacters) - \endcode - - This moves the cursor to position 5, extend the selection end from 5 to 9 - and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 - selected (the 6th and 7th characters). - - The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary - before or on position 5 and extend the selection end to a word boundary on or past position 9. -*/ -void QSGTextEdit::moveCursorSelection(int pos) -{ - //Note that this is the same as setCursorPosition but with the KeepAnchor flag set - Q_D(QSGTextEdit); - QTextCursor cursor = d->control->textCursor(); - if (cursor.position() == pos) - return; - cursor.setPosition(pos, QTextCursor::KeepAnchor); - d->control->setTextCursor(cursor); -} - -void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode) -{ - Q_D(QSGTextEdit); - QTextCursor cursor = d->control->textCursor(); - if (cursor.position() == pos) - return; - if (mode == SelectCharacters) { - cursor.setPosition(pos, QTextCursor::KeepAnchor); - } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) { - if (cursor.anchor() > cursor.position()) { - cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); - if (cursor.position() == cursor.anchor()) - cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor); - else - cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor); - } else { - cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); - } - - cursor.setPosition(pos, QTextCursor::KeepAnchor); - cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); - if (cursor.position() != pos) - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); - } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) { - if (cursor.anchor() < cursor.position()) { - cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); - } else { - cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); - if (cursor.position() != cursor.anchor()) { - cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); - } - } - - cursor.setPosition(pos, QTextCursor::KeepAnchor); - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); - if (cursor.position() != pos) { - cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); - cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); - } - } - d->control->setTextCursor(cursor); -} - -/*! - \qmlproperty bool QtQuick2::TextEdit::cursorVisible - If true the text edit shows a cursor. - - This property is set and unset when the text edit gets active focus, but it can also - be set directly (useful, for example, if a KeyProxy might forward keys to it). -*/ -bool QSGTextEdit::isCursorVisible() const -{ - Q_D(const QSGTextEdit); - return d->cursorVisible; -} - -void QSGTextEdit::setCursorVisible(bool on) -{ - Q_D(QSGTextEdit); - if (d->cursorVisible == on) - return; - d->cursorVisible = on; - QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut); - if (!on && !d->persistentSelection) - d->control->setCursorIsFocusIndicator(true); - d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); - emit cursorVisibleChanged(d->cursorVisible); -} - -/*! - \qmlproperty int QtQuick2::TextEdit::cursorPosition - The position of the cursor in the TextEdit. -*/ -int QSGTextEdit::cursorPosition() const -{ - Q_D(const QSGTextEdit); - return d->control->textCursor().position(); -} - -void QSGTextEdit::setCursorPosition(int pos) -{ - Q_D(QSGTextEdit); - if (pos < 0 || pos > d->text.length()) - return; - QTextCursor cursor = d->control->textCursor(); - if (cursor.position() == pos && cursor.anchor() == pos) - return; - cursor.setPosition(pos); - d->control->setTextCursor(cursor); -} - -/*! - \qmlproperty Component QtQuick2::TextEdit::cursorDelegate - The delegate for the cursor in the TextEdit. - - If you set a cursorDelegate for a TextEdit, this delegate will be used for - drawing the cursor instead of the standard cursor. An instance of the - delegate will be created and managed by the text edit when a cursor is - needed, and the x and y properties of delegate instance will be set so as - to be one pixel before the top left of the current character. - - Note that the root item of the delegate component must be a QDeclarativeItem or - QDeclarativeItem derived item. -*/ -QDeclarativeComponent* QSGTextEdit::cursorDelegate() const -{ - Q_D(const QSGTextEdit); - return d->cursorComponent; -} - -void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c) -{ - Q_D(QSGTextEdit); - if (d->cursorComponent) { - if (d->cursor) { - d->control->setCursorWidth(-1); - updateCursor(); - delete d->cursor; - d->cursor = 0; - } - } - d->cursorComponent = c; - if (c && c->isReady()) { - loadCursorDelegate(); - } else { - if (c) - connect(c, SIGNAL(statusChanged()), - this, SLOT(loadCursorDelegate())); - } - - emit cursorDelegateChanged(); -} - -void QSGTextEdit::loadCursorDelegate() -{ - Q_D(QSGTextEdit); - if (d->cursorComponent->isLoading()) - return; - QDeclarativeContext *creationContext = d->cursorComponent->creationContext(); - QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this)); - d->cursor = qobject_cast(object); - if (d->cursor) { - d->control->setCursorWidth(0); - updateCursor(); - QDeclarative_setParent_noEvent(d->cursor, this); - d->cursor->setParentItem(this); - d->cursor->setHeight(QFontMetrics(d->font).height()); - moveCursorDelegate(); - }else{ - delete object; - qmlInfo(this) << "Error loading cursor delegate."; - } -} - -/*! - \qmlproperty int QtQuick2::TextEdit::selectionStart - - The cursor position before the first character in the current selection. - - This property is read-only. To change the selection, use select(start,end), - selectAll(), or selectWord(). - - \sa selectionEnd, cursorPosition, selectedText -*/ -int QSGTextEdit::selectionStart() const -{ - Q_D(const QSGTextEdit); - return d->control->textCursor().selectionStart(); -} - -/*! - \qmlproperty int QtQuick2::TextEdit::selectionEnd - - The cursor position after the last character in the current selection. - - This property is read-only. To change the selection, use select(start,end), - selectAll(), or selectWord(). - - \sa selectionStart, cursorPosition, selectedText -*/ -int QSGTextEdit::selectionEnd() const -{ - Q_D(const QSGTextEdit); - return d->control->textCursor().selectionEnd(); -} - -/*! - \qmlproperty string QtQuick2::TextEdit::selectedText - - This read-only property provides the text currently selected in the - text edit. - - It is equivalent to the following snippet, but is faster and easier - to use. - \code - //myTextEdit is the id of the TextEdit - myTextEdit.text.toString().substring(myTextEdit.selectionStart, - myTextEdit.selectionEnd); - \endcode -*/ -QString QSGTextEdit::selectedText() const -{ - Q_D(const QSGTextEdit); - return d->control->textCursor().selectedText(); -} - -/*! - \qmlproperty bool QtQuick2::TextEdit::activeFocusOnPress - - Whether the TextEdit should gain active focus on a mouse press. By default this is - set to true. -*/ -bool QSGTextEdit::focusOnPress() const -{ - Q_D(const QSGTextEdit); - return d->focusOnPress; -} - -void QSGTextEdit::setFocusOnPress(bool on) -{ - Q_D(QSGTextEdit); - if (d->focusOnPress == on) - return; - d->focusOnPress = on; - emit activeFocusOnPressChanged(d->focusOnPress); -} - -/*! - \qmlproperty bool QtQuick2::TextEdit::persistentSelection - - Whether the TextEdit should keep the selection visible when it loses active focus to another - item in the scene. By default this is set to true; -*/ -bool QSGTextEdit::persistentSelection() const -{ - Q_D(const QSGTextEdit); - return d->persistentSelection; -} - -void QSGTextEdit::setPersistentSelection(bool on) -{ - Q_D(QSGTextEdit); - if (d->persistentSelection == on) - return; - d->persistentSelection = on; - emit persistentSelectionChanged(d->persistentSelection); -} - -/* - \qmlproperty real QtQuick2::TextEdit::textMargin - - The margin, in pixels, around the text in the TextEdit. -*/ -qreal QSGTextEdit::textMargin() const -{ - Q_D(const QSGTextEdit); - return d->textMargin; -} - -void QSGTextEdit::setTextMargin(qreal margin) -{ - Q_D(QSGTextEdit); - if (d->textMargin == margin) - return; - d->textMargin = margin; - d->document->setDocumentMargin(d->textMargin); - emit textMarginChanged(d->textMargin); -} - -void QSGTextEdit::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) -{ - if (newGeometry.width() != oldGeometry.width()) - updateSize(); - QSGImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); -} - -/*! - Ensures any delayed caching or data loading the class - needs to performed is complete. -*/ -void QSGTextEdit::componentComplete() -{ - Q_D(QSGTextEdit); - QSGImplicitSizeItem::componentComplete(); - - if (d->richText) - d->useImageFallback = qmlEnableImageCache(); - - if (d->dirty) { - d->determineHorizontalAlignment(); - d->updateDefaultTextOption(); - updateSize(); - d->dirty = false; - } - -} -/*! - \qmlproperty bool QtQuick2::TextEdit::selectByMouse - - Defaults to false. - - If true, the user can use the mouse to select text in some - platform-specific way. Note that for some platforms this may - not be an appropriate interaction (eg. may conflict with how - the text needs to behave inside a Flickable. -*/ -bool QSGTextEdit::selectByMouse() const -{ - Q_D(const QSGTextEdit); - return d->selectByMouse; -} - -void QSGTextEdit::setSelectByMouse(bool on) -{ - Q_D(QSGTextEdit); - if (d->selectByMouse != on) { - d->selectByMouse = on; - setKeepMouseGrab(on); - if (on) - setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse); - else - setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse); - emit selectByMouseChanged(on); - } -} - -/*! - \qmlproperty enum QtQuick2::TextEdit::mouseSelectionMode - - Specifies how text should be selected using a mouse. - - \list - \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default) - \o TextEdit.SelectWords - The selection is updated with whole words. - \endlist - - This property only applies when \l selectByMouse is true. -*/ -QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const -{ - Q_D(const QSGTextEdit); - return d->mouseSelectionMode; -} - -void QSGTextEdit::setMouseSelectionMode(SelectionMode mode) -{ - Q_D(QSGTextEdit); - if (d->mouseSelectionMode != mode) { - d->mouseSelectionMode = mode; - d->control->setWordSelectionEnabled(mode == SelectWords); - emit mouseSelectionModeChanged(mode); - } -} - -/*! - \qmlproperty bool QtQuick2::TextEdit::readOnly - - Whether the user can interact with the TextEdit item. If this - property is set to true the text cannot be edited by user interaction. - - By default this property is false. -*/ -void QSGTextEdit::setReadOnly(bool r) -{ - Q_D(QSGTextEdit); - if (r == isReadOnly()) - return; - - setFlag(QSGItem::ItemAcceptsInputMethod, !r); - Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse; - if (d->selectByMouse) - flags = flags | Qt::TextSelectableByMouse; - if (!r) - flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable; - d->control->setTextInteractionFlags(flags); - if (!r) - d->control->moveCursor(QTextCursor::End); - - emit readOnlyChanged(r); -} - -bool QSGTextEdit::isReadOnly() const -{ - Q_D(const QSGTextEdit); - return !(d->control->textInteractionFlags() & Qt::TextEditable); -} - -/*! - Sets how the text edit should interact with user input to the given - \a flags. -*/ -void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags) -{ - Q_D(QSGTextEdit); - d->control->setTextInteractionFlags(flags); -} - -/*! - Returns the flags specifying how the text edit should interact - with user input. -*/ -Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const -{ - Q_D(const QSGTextEdit); - return d->control->textInteractionFlags(); -} - -/*! - \qmlproperty rectangle QtQuick2::TextEdit::cursorRectangle - - The rectangle where the text cursor is rendered - within the text edit. Read-only. -*/ -QRect QSGTextEdit::cursorRectangle() const -{ - Q_D(const QSGTextEdit); - return d->control->cursorRect().toRect().translated(0,d->yoff); -} - -bool QSGTextEdit::event(QEvent *event) -{ - Q_D(QSGTextEdit); - if (event->type() == QEvent::ShortcutOverride) { - d->control->processEvent(event, QPointF(0, -d->yoff)); - return event->isAccepted(); - } - return QSGImplicitSizeItem::event(event); -} - -/*! -\overload -Handles the given key \a event. -*/ -void QSGTextEdit::keyPressEvent(QKeyEvent *event) -{ - Q_D(QSGTextEdit); - d->control->processEvent(event, QPointF(0, -d->yoff)); - if (!event->isAccepted()) - QSGImplicitSizeItem::keyPressEvent(event); -} - -/*! -\overload -Handles the given key \a event. -*/ -void QSGTextEdit::keyReleaseEvent(QKeyEvent *event) -{ - Q_D(QSGTextEdit); - d->control->processEvent(event, QPointF(0, -d->yoff)); - if (!event->isAccepted()) - QSGImplicitSizeItem::keyReleaseEvent(event); -} - -/*! - \qmlmethod void QtQuick2::TextEdit::deselect() - - Removes active text selection. -*/ -void QSGTextEdit::deselect() -{ - Q_D(QSGTextEdit); - QTextCursor c = d->control->textCursor(); - c.clearSelection(); - d->control->setTextCursor(c); -} - -/*! - \qmlmethod void QtQuick2::TextEdit::selectAll() - - Causes all text to be selected. -*/ -void QSGTextEdit::selectAll() -{ - Q_D(QSGTextEdit); - d->control->selectAll(); -} - -/*! - \qmlmethod void QtQuick2::TextEdit::selectWord() - - Causes the word closest to the current cursor position to be selected. -*/ -void QSGTextEdit::selectWord() -{ - Q_D(QSGTextEdit); - QTextCursor c = d->control->textCursor(); - c.select(QTextCursor::WordUnderCursor); - d->control->setTextCursor(c); -} - -/*! - \qmlmethod void QtQuick2::TextEdit::select(int start, int end) - - Causes the text from \a start to \a end to be selected. - - If either start or end is out of range, the selection is not changed. - - After calling this, selectionStart will become the lesser - and selectionEnd will become the greater (regardless of the order passed - to this method). - - \sa selectionStart, selectionEnd -*/ -void QSGTextEdit::select(int start, int end) -{ - Q_D(QSGTextEdit); - if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length()) - return; - QTextCursor cursor = d->control->textCursor(); - cursor.beginEditBlock(); - cursor.setPosition(start, QTextCursor::MoveAnchor); - cursor.setPosition(end, QTextCursor::KeepAnchor); - cursor.endEditBlock(); - d->control->setTextCursor(cursor); - - // QTBUG-11100 - updateSelectionMarkers(); -} - -/*! - \qmlmethod void QtQuick2::TextEdit::isRightToLeft(int start, int end) - - Returns true if the natural reading direction of the editor text - found between positions \a start and \a end is right to left. -*/ -bool QSGTextEdit::isRightToLeft(int start, int end) -{ - Q_D(QSGTextEdit); - if (start > end) { - qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start."; - return false; - } else { - return d->text.mid(start, end - start).isRightToLeft(); - } -} - -#ifndef QT_NO_CLIPBOARD -/*! - \qmlmethod QtQuick2::TextEdit::cut() - - Moves the currently selected text to the system clipboard. -*/ -void QSGTextEdit::cut() -{ - Q_D(QSGTextEdit); - d->control->cut(); -} - -/*! - \qmlmethod QtQuick2::TextEdit::copy() - - Copies the currently selected text to the system clipboard. -*/ -void QSGTextEdit::copy() -{ - Q_D(QSGTextEdit); - d->control->copy(); -} - -/*! - \qmlmethod QtQuick2::TextEdit::paste() - - Replaces the currently selected text by the contents of the system clipboard. -*/ -void QSGTextEdit::paste() -{ - Q_D(QSGTextEdit); - d->control->paste(); -} -#endif // QT_NO_CLIPBOARD - -/*! -\overload -Handles the given mouse \a event. -*/ -void QSGTextEdit::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGTextEdit); - if (d->focusOnPress){ - bool hadActiveFocus = hasActiveFocus(); - forceActiveFocus(); - // re-open input panel on press if already focused - if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) - openSoftwareInputPanel(); - } - d->control->processEvent(event, QPointF(0, -d->yoff)); - if (!event->isAccepted()) - QSGImplicitSizeItem::mousePressEvent(event); -} - -/*! -\overload -Handles the given mouse \a event. -*/ -void QSGTextEdit::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGTextEdit); - d->control->processEvent(event, QPointF(0, -d->yoff)); - - if (!event->isAccepted()) - QSGImplicitSizeItem::mouseReleaseEvent(event); -} - -/*! -\overload -Handles the given mouse \a event. -*/ -void QSGTextEdit::mouseDoubleClickEvent(QMouseEvent *event) -{ - Q_D(QSGTextEdit); - d->control->processEvent(event, QPointF(0, -d->yoff)); - if (!event->isAccepted()) - QSGImplicitSizeItem::mouseDoubleClickEvent(event); -} - -/*! -\overload -Handles the given mouse \a event. -*/ -void QSGTextEdit::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QSGTextEdit); - d->control->processEvent(event, QPointF(0, -d->yoff)); - if (!event->isAccepted()) - QSGImplicitSizeItem::mouseMoveEvent(event); -} - -/*! -\overload -Handles the given input method \a event. -*/ -void QSGTextEdit::inputMethodEvent(QInputMethodEvent *event) -{ - Q_D(QSGTextEdit); - const bool wasComposing = isInputMethodComposing(); - d->control->processEvent(event, QPointF(0, -d->yoff)); - if (wasComposing != isInputMethodComposing()) - emit inputMethodComposingChanged(); -} - -void QSGTextEdit::itemChange(ItemChange change, const ItemChangeData &value) -{ - Q_D(QSGTextEdit); - if (change == ItemActiveFocusHasChanged) { - setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus()); - } - QSGItem::itemChange(change, value); -} - -/*! -\overload -Returns the value of the given \a property. -*/ -QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const -{ - Q_D(const QSGTextEdit); - - QVariant v; - switch (property) { - case Qt::ImEnabled: - v = (bool)(flags() & ItemAcceptsInputMethod); - break; - case Qt::ImHints: - v = (int)inputMethodHints(); - break; - default: - v = d->control->inputMethodQuery(property); - break; - } - return v; - -} - -void QSGTextEdit::updateImageCache(const QRectF &) -{ - Q_D(QSGTextEdit); - - // Do we really need the image cache? - if (!d->richText || !d->useImageFallback) { - if (!d->pixmapCache.isNull()) - d->pixmapCache = QPixmap(); - return; - } - - if (width() != d->pixmapCache.width() || height() != d->pixmapCache.height()) - d->pixmapCache = QPixmap(width(), height()); - - if (d->pixmapCache.isNull()) - return; - - // ### Use supplied rect, clear area and update only this part (for cursor updates) - QRectF bounds = QRectF(0, 0, width(), height()); - d->pixmapCache.fill(Qt::transparent); - { - QPainter painter(&d->pixmapCache); - - painter.setRenderHint(QPainter::TextAntialiasing); - painter.translate(0, d->yoff); - - d->control->drawContents(&painter, bounds); - } - -} - -QSGNode *QSGTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) -{ - Q_UNUSED(updatePaintNodeData); - Q_D(QSGTextEdit); - - QSGNode *currentNode = oldNode; - if (d->richText && d->useImageFallback) { - QSGImageNode *node = 0; - if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsTexture) { - delete oldNode; - node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode(); - d->texture = new QSGPlainTexture(); - d->nodeType = QSGTextEditPrivate::NodeIsTexture; - currentNode = node; - } else { - node = static_cast(oldNode); - } - - qobject_cast(d->texture)->setImage(d->pixmapCache.toImage()); - node->setTexture(0); - node->setTexture(d->texture); - - node->setTargetRect(QRectF(0, 0, d->pixmapCache.width(), d->pixmapCache.height())); - node->setSourceRect(QRectF(0, 0, 1, 1)); - node->setHorizontalWrapMode(QSGTexture::ClampToEdge); - node->setVerticalWrapMode(QSGTexture::ClampToEdge); - node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that.. - node->update(); - - } else if (oldNode == 0 || d->documentDirty) { - d->documentDirty = false; - -#if defined(Q_WS_MAC) - // Make sure document is relayouted in the paint node on Mac - // to avoid crashes due to the font engines created in the - // shaping process - d->document->markContentsDirty(0, d->document->characterCount()); -#endif - - QSGTextNode *node = 0; - if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsText) { - delete oldNode; - node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext()); - d->nodeType = QSGTextEditPrivate::NodeIsText; - currentNode = node; - } else { - node = static_cast(oldNode); - } - - node->deleteContent(); - node->setMatrix(QMatrix4x4()); - - QRectF bounds = boundingRect(); - - QColor selectionColor = d->control->palette().color(QPalette::Highlight); - QColor selectedTextColor = d->control->palette().color(QPalette::HighlightedText); - node->addTextDocument(bounds.topLeft(), d->document, d->color, QSGText::Normal, QColor(), - selectionColor, selectedTextColor, selectionStart(), - selectionEnd() - 1); // selectionEnd() returns first char after - // selection - -#if defined(Q_WS_MAC) - // We also need to make sure the document layout is redone when - // control is returned to the main thread, as all the font engines - // are now owned by the rendering thread - d->document->markContentsDirty(0, d->document->characterCount()); -#endif - } - - if (d->nodeType == QSGTextEditPrivate::NodeIsText && d->cursorComponent == 0 && !isReadOnly()) { - QSGTextNode *node = static_cast(currentNode); - - QColor color = (!d->cursorVisible || !d->control->cursorOn()) - ? QColor(0, 0, 0, 0) - : d->color; - - if (node->cursorNode() == 0) { - node->setCursor(cursorRectangle(), color); - } else { - node->cursorNode()->setRect(cursorRectangle()); - node->cursorNode()->setColor(color); - } - - } - - return currentNode; -} - -/*! - \qmlproperty bool QtQuick2::TextEdit::smooth - - This property holds whether the text is smoothly scaled or transformed. - - Smooth filtering gives better visual quality, but is slower. If - the item is displayed at its natural size, this property has no visual or - performance effect. - - \note Generally scaling artifacts are only visible if the item is stationary on - the screen. A common pattern when animating an item is to disable smooth - filtering at the beginning of the animation and reenable it at the conclusion. -*/ - -/*! - \qmlproperty bool QtQuick2::TextEdit::canPaste - - Returns true if the TextEdit is writable and the content of the clipboard is - suitable for pasting into the TextEdit. -*/ -bool QSGTextEdit::canPaste() const -{ - Q_D(const QSGTextEdit); - return d->canPaste; -} - -/*! - \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing - - - This property holds whether the TextEdit has partial text input from an - input method. - - While it is composing an input method may rely on mouse or key events from - the TextEdit to edit or commit the partial text. This property can be used - to determine when to disable events handlers that may interfere with the - correct operation of an input method. -*/ -bool QSGTextEdit::isInputMethodComposing() const -{ - Q_D(const QSGTextEdit); - if (QTextLayout *layout = d->control->textCursor().block().layout()) - return layout->preeditAreaText().length() > 0; - return false; -} - -void QSGTextEditPrivate::init() -{ - Q_Q(QSGTextEdit); - - q->setSmooth(smooth); - q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFlag(QSGItem::ItemAcceptsInputMethod); - q->setFlag(QSGItem::ItemHasContents); - - control = new QTextControl(q); - control->setIgnoreUnusedNavigationEvents(true); - control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable); - control->setDragEnabled(false); - - // By default, QTextControl will issue both a updateCursorRequest() and an updateRequest() - // when the cursor needs to be repainted. We need the signals to be separate to be able to - // distinguish the cursor updates so that we can avoid updating the whole subtree when the - // cursor blinks. - if (!QObject::disconnect(control, SIGNAL(updateCursorRequest(QRectF)), - control, SIGNAL(updateRequest(QRectF)))) { - qWarning("QSGTextEditPrivate::init: Failed to disconnect updateCursorRequest and updateRequest"); - } - - // QTextControl follows the default text color - // defined by the platform, declarative text - // should be black by default - QPalette pal = control->palette(); - if (pal.color(QPalette::Text) != color) { - pal.setColor(QPalette::Text, color); - control->setPalette(pal); - } - - QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument())); - QObject::connect(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor())); - QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged())); - QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged())); - QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers())); - QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers())); - QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged())); - QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate())); - QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString))); -#ifndef QT_NO_CLIPBOARD - QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged())); - QObject::connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged())); - canPaste = control->canPaste(); -#endif - - document = control->document(); - document->setDefaultFont(font); - document->setDocumentMargin(textMargin); - document->setUndoRedoEnabled(false); // flush undo buffer. - document->setUndoRedoEnabled(true); - updateDefaultTextOption(); -} - -void QSGTextEdit::q_textChanged() -{ - Q_D(QSGTextEdit); - d->text = text(); - d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft(); - d->determineHorizontalAlignment(); - d->updateDefaultTextOption(); - updateSize(); - updateTotalLines(); - emit textChanged(d->text); -} - -void QSGTextEdit::moveCursorDelegate() -{ - Q_D(QSGTextEdit); - d->determineHorizontalAlignment(); - updateMicroFocus(); - emit cursorRectangleChanged(); - if (!d->cursor) - return; - QRectF cursorRect = cursorRectangle(); - d->cursor->setX(cursorRect.x()); - d->cursor->setY(cursorRect.y()); -} - -void QSGTextEditPrivate::updateSelection() -{ - Q_Q(QSGTextEdit); - QTextCursor cursor = control->textCursor(); - bool startChange = (lastSelectionStart != cursor.selectionStart()); - bool endChange = (lastSelectionEnd != cursor.selectionEnd()); - cursor.beginEditBlock(); - cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor); - cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor); - cursor.endEditBlock(); - control->setTextCursor(cursor); - if (startChange) - q->selectionStartChanged(); - if (endChange) - q->selectionEndChanged(); -} - -void QSGTextEdit::updateSelectionMarkers() -{ - Q_D(QSGTextEdit); - if (d->lastSelectionStart != d->control->textCursor().selectionStart()) { - d->lastSelectionStart = d->control->textCursor().selectionStart(); - emit selectionStartChanged(); - } - if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) { - d->lastSelectionEnd = d->control->textCursor().selectionEnd(); - emit selectionEndChanged(); - } -} - -QRectF QSGTextEdit::boundingRect() const -{ - Q_D(const QSGTextEdit); - QRectF r = QSGImplicitSizeItem::boundingRect(); - int cursorWidth = 1; - if (d->cursor) - cursorWidth = d->cursor->width(); - if (!d->document->isEmpty()) - cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor - - // Could include font max left/right bearings to either side of rectangle. - - r.setRight(r.right() + cursorWidth); - return r.translated(0,d->yoff); -} - -qreal QSGTextEditPrivate::getImplicitWidth() const -{ - Q_Q(const QSGTextEdit); - if (!requireImplicitWidth) { - // We don't calculate implicitWidth unless it is required. - // We need to force a size update now to ensure implicitWidth is calculated - const_cast(this)->requireImplicitWidth = true; - const_cast(q)->updateSize(); - } - return implicitWidth; -} - -//### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't -// need to do all the calculations each time -void QSGTextEdit::updateSize() -{ - Q_D(QSGTextEdit); - if (isComponentComplete()) { - qreal naturalWidth = d->implicitWidth; - // ### assumes that if the width is set, the text will fill to edges - // ### (unless wrap is false, then clipping will occur) - if (widthValid()) { - if (!d->requireImplicitWidth) { - emit implicitWidthChanged(); - // if the implicitWidth is used, then updateSize() has already been called (recursively) - if (d->requireImplicitWidth) - return; - } - if (d->requireImplicitWidth) { - d->document->setTextWidth(-1); - naturalWidth = d->document->idealWidth(); - } - if (d->document->textWidth() != width()) - d->document->setTextWidth(width()); - } else { - d->document->setTextWidth(-1); - } - QFontMetrics fm = QFontMetrics(d->font); - int dy = height(); - dy -= (int)d->document->size().height(); - - int nyoff; - if (heightValid()) { - if (d->vAlign == AlignBottom) - nyoff = dy; - else if (d->vAlign == AlignVCenter) - nyoff = dy/2; - else - nyoff = 0; - } else { - nyoff = 0; - } - if (nyoff != d->yoff) - d->yoff = nyoff; - setBaselineOffset(fm.ascent() + d->yoff + d->textMargin); - - //### need to comfirm cost of always setting these - int newWidth = qCeil(d->document->idealWidth()); - if (!widthValid() && d->document->textWidth() != newWidth) - d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug) - // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. - if (!widthValid()) - setImplicitWidth(newWidth); - else if (d->requireImplicitWidth) - setImplicitWidth(naturalWidth); - qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height(); - setImplicitHeight(newHeight); - - d->paintedSize = QSize(newWidth, newHeight); - emit paintedSizeChanged(); - } else { - d->dirty = true; - } - updateDocument(); -} - -void QSGTextEdit::updateDocument() -{ - Q_D(QSGTextEdit); - d->documentDirty = true; - - if (isComponentComplete()) { - updateImageCache(); - update(); - } -} - -void QSGTextEdit::updateCursor() -{ - Q_D(QSGTextEdit); - if (isComponentComplete()) { - updateImageCache(d->control->cursorRect()); - update(); - } -} - -void QSGTextEdit::updateTotalLines() -{ - Q_D(QSGTextEdit); - - int subLines = 0; - - for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) { - QTextLayout *layout = it.layout(); - if (!layout) - continue; - subLines += layout->lineCount()-1; - } - - int newTotalLines = d->document->lineCount() + subLines; - if (d->lineCount != newTotalLines) { - d->lineCount = newTotalLines; - emit lineCountChanged(); - } -} - -void QSGTextEditPrivate::updateDefaultTextOption() -{ - Q_Q(QSGTextEdit); - QTextOption opt = document->defaultTextOption(); - int oldAlignment = opt.alignment(); - - QSGTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign(); - if (rightToLeftText) { - if (horizontalAlignment == QSGTextEdit::AlignLeft) - horizontalAlignment = QSGTextEdit::AlignRight; - else if (horizontalAlignment == QSGTextEdit::AlignRight) - horizontalAlignment = QSGTextEdit::AlignLeft; - } - opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign)); - - QTextOption::WrapMode oldWrapMode = opt.wrapMode(); - opt.setWrapMode(QTextOption::WrapMode(wrapMode)); - - bool oldUseDesignMetrics = opt.useDesignMetrics(); - bool useDesignMetrics = !qmlDisableDistanceField(); - opt.setUseDesignMetrics(useDesignMetrics); - - if (oldWrapMode == opt.wrapMode() - && oldAlignment == opt.alignment() - && oldUseDesignMetrics == useDesignMetrics) { - return; - } - document->setDefaultTextOption(opt); -} - - - -/*! - \qmlmethod void QtQuick2::TextEdit::openSoftwareInputPanel() - - Opens software input panels like virtual keyboards for typing, useful for - customizing when you want the input keyboard to be shown and hidden in - your application. - - By default the opening of input panels follows the platform style. On Symbian^1 and - Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms - the panels are automatically opened when TextEdit element gains active focus. Input panels are - always closed if no editor has active focus. - - You can disable the automatic behavior by setting the property \c activeFocusOnPress to false - and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement - the behavior you want. - - Only relevant on platforms, which provide virtual keyboards. - - \code - import QtQuick 1.0 - TextEdit { - id: textEdit - text: "Hello world!" - activeFocusOnPress: false - MouseArea { - anchors.fill: parent - onClicked: { - if (!textEdit.activeFocus) { - textEdit.forceActiveFocus(); - textEdit.openSoftwareInputPanel(); - } else { - textEdit.focus = false; - } - } - onPressAndHold: textEdit.closeSoftwareInputPanel(); - } - } - \endcode -*/ -void QSGTextEdit::openSoftwareInputPanel() -{ - if (qGuiApp) - qGuiApp->inputPanel()->show(); -} - -/*! - \qmlmethod void QtQuick2::TextEdit::closeSoftwareInputPanel() - - Closes a software input panel like a virtual keyboard shown on the screen, useful - for customizing when you want the input keyboard to be shown and hidden in - your application. - - By default the opening of input panels follows the platform style. On Symbian^1 and - Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms - the panels are automatically opened when TextEdit element gains active focus. Input panels are - always closed if no editor has active focus. - - You can disable the automatic behavior by setting the property \c activeFocusOnPress to false - and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement - the behavior you want. - - Only relevant on platforms, which provide virtual keyboards. - - \code - import QtQuick 1.0 - TextEdit { - id: textEdit - text: "Hello world!" - activeFocusOnPress: false - MouseArea { - anchors.fill: parent - onClicked: { - if (!textEdit.activeFocus) { - textEdit.forceActiveFocus(); - textEdit.openSoftwareInputPanel(); - } else { - textEdit.focus = false; - } - } - onPressAndHold: textEdit.closeSoftwareInputPanel(); - } - } - \endcode -*/ -void QSGTextEdit::closeSoftwareInputPanel() -{ - if (qGuiApp) - qGuiApp->inputPanel()->hide(); -} - -void QSGTextEdit::focusInEvent(QFocusEvent *event) -{ - Q_D(const QSGTextEdit); - if (d->focusOnPress && !isReadOnly()) - openSoftwareInputPanel(); - QSGImplicitSizeItem::focusInEvent(event); -} - -void QSGTextEdit::q_canPasteChanged() -{ - Q_D(QSGTextEdit); - bool old = d->canPaste; - d->canPaste = d->control->canPaste(); - if (old!=d->canPaste) - emit canPasteChanged(); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgtextedit_p.h b/src/declarative/items/qsgtextedit_p.h deleted file mode 100644 index fced11808f..0000000000 --- a/src/declarative/items/qsgtextedit_p.h +++ /dev/null @@ -1,306 +0,0 @@ -// Commit: 27e4302b7f45f22180693d26747f419177c81e27 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTEXTEDIT_P_H -#define QSGTEXTEDIT_P_H - -#include "qsgimplicitsizeitem_p.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGTextEditPrivate; -class Q_AUTOTEST_EXPORT QSGTextEdit : public QSGImplicitSizeItem -{ - Q_OBJECT - Q_ENUMS(VAlignment) - Q_ENUMS(HAlignment) - Q_ENUMS(TextFormat) - Q_ENUMS(WrapMode) - Q_ENUMS(SelectionMode) - - Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged) - Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged) - Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) - Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) - Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged) - Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged) - Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged) - Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged) - Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) - Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) - Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged) - Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged) - Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged) - Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) - Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) - Q_PROPERTY(QDeclarativeComponent* cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) - Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) - Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) - Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectionChanged) - Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged) - Q_PROPERTY(bool persistentSelection READ persistentSelection WRITE setPersistentSelection NOTIFY persistentSelectionChanged) - Q_PROPERTY(qreal textMargin READ textMargin WRITE setTextMargin NOTIFY textMarginChanged) - Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints) - Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) - Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged) - Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged) - Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged) - -public: - QSGTextEdit(QSGItem *parent=0); - - enum HAlignment { - AlignLeft = Qt::AlignLeft, - AlignRight = Qt::AlignRight, - AlignHCenter = Qt::AlignHCenter, - AlignJustify = Qt::AlignJustify - }; - - enum VAlignment { - AlignTop = Qt::AlignTop, - AlignBottom = Qt::AlignBottom, - AlignVCenter = Qt::AlignVCenter - }; - - enum TextFormat { - PlainText = Qt::PlainText, - RichText = Qt::RichText, - AutoText = Qt::AutoText - }; - - enum WrapMode { NoWrap = QTextOption::NoWrap, - WordWrap = QTextOption::WordWrap, - WrapAnywhere = QTextOption::WrapAnywhere, - WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT - Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere - }; - - enum SelectionMode { - SelectCharacters, - SelectWords - }; - - Q_INVOKABLE void openSoftwareInputPanel(); - Q_INVOKABLE void closeSoftwareInputPanel(); - - QString text() const; - void setText(const QString &); - - TextFormat textFormat() const; - void setTextFormat(TextFormat format); - - QFont font() const; - void setFont(const QFont &font); - - QColor color() const; - void setColor(const QColor &c); - - QColor selectionColor() const; - void setSelectionColor(const QColor &c); - - QColor selectedTextColor() const; - void setSelectedTextColor(const QColor &c); - - HAlignment hAlign() const; - void setHAlign(HAlignment align); - void resetHAlign(); - HAlignment effectiveHAlign() const; - - VAlignment vAlign() const; - void setVAlign(VAlignment align); - - WrapMode wrapMode() const; - void setWrapMode(WrapMode w); - - int lineCount() const; - - bool isCursorVisible() const; - void setCursorVisible(bool on); - - int cursorPosition() const; - void setCursorPosition(int pos); - - QDeclarativeComponent* cursorDelegate() const; - void setCursorDelegate(QDeclarativeComponent*); - - int selectionStart() const; - int selectionEnd() const; - - QString selectedText() const; - - bool focusOnPress() const; - void setFocusOnPress(bool on); - - bool persistentSelection() const; - void setPersistentSelection(bool on); - - qreal textMargin() const; - void setTextMargin(qreal margin); - - bool selectByMouse() const; - void setSelectByMouse(bool); - - SelectionMode mouseSelectionMode() const; - void setMouseSelectionMode(SelectionMode mode); - - bool canPaste() const; - - virtual void componentComplete(); - - /* FROM EDIT */ - void setReadOnly(bool); - bool isReadOnly() const; - - void setTextInteractionFlags(Qt::TextInteractionFlags flags); - Qt::TextInteractionFlags textInteractionFlags() const; - - QRect cursorRectangle() const; - - QVariant inputMethodQuery(Qt::InputMethodQuery property) const; - - qreal paintedWidth() const; - qreal paintedHeight() const; - - Q_INVOKABLE QRectF positionToRectangle(int) const; - Q_INVOKABLE int positionAt(int x, int y) const; - Q_INVOKABLE void moveCursorSelection(int pos); - Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode); - - QRectF boundingRect() const; - - bool isInputMethodComposing() const; - -Q_SIGNALS: - void textChanged(const QString &); - void paintedSizeChanged(); - void cursorPositionChanged(); - void cursorRectangleChanged(); - void selectionStartChanged(); - void selectionEndChanged(); - void selectionChanged(); - void colorChanged(const QColor &color); - void selectionColorChanged(const QColor &color); - void selectedTextColorChanged(const QColor &color); - void fontChanged(const QFont &font); - void horizontalAlignmentChanged(HAlignment alignment); - void verticalAlignmentChanged(VAlignment alignment); - void wrapModeChanged(); - void lineCountChanged(); - void textFormatChanged(TextFormat textFormat); - void readOnlyChanged(bool isReadOnly); - void cursorVisibleChanged(bool isCursorVisible); - void cursorDelegateChanged(); - void activeFocusOnPressChanged(bool activeFocusOnPressed); - void persistentSelectionChanged(bool isPersistentSelection); - void textMarginChanged(qreal textMargin); - void selectByMouseChanged(bool selectByMouse); - void mouseSelectionModeChanged(SelectionMode mode); - void linkActivated(const QString &link); - void canPasteChanged(); - void inputMethodComposingChanged(); - void effectiveHorizontalAlignmentChanged(); - -public Q_SLOTS: - void selectAll(); - void selectWord(); - void select(int start, int end); - void deselect(); - bool isRightToLeft(int start, int end); -#ifndef QT_NO_CLIPBOARD - void cut(); - void copy(); - void paste(); -#endif - -private Q_SLOTS: - void q_textChanged(); - void updateSelectionMarkers(); - void moveCursorDelegate(); - void loadCursorDelegate(); - void q_canPasteChanged(); - void updateDocument(); - void updateCursor(); - -private: - void updateSize(); - void updateTotalLines(); - void updateImageCache(const QRectF &rect = QRectF()); - -protected: - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - - bool event(QEvent *); - void keyPressEvent(QKeyEvent *); - void keyReleaseEvent(QKeyEvent *); - void focusInEvent(QFocusEvent *event); - - // mouse filter? - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void mouseDoubleClickEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void inputMethodEvent(QInputMethodEvent *e); - virtual void itemChange(ItemChange, const ItemChangeData &); - - QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData); - -private: - Q_DISABLE_COPY(QSGTextEdit) - Q_DECLARE_PRIVATE(QSGTextEdit) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGTextEdit) - -QT_END_HEADER - -#endif // QSGTEXTEDIT_P_H diff --git a/src/declarative/items/qsgtextedit_p_p.h b/src/declarative/items/qsgtextedit_p_p.h deleted file mode 100644 index 51904d1cde..0000000000 --- a/src/declarative/items/qsgtextedit_p_p.h +++ /dev/null @@ -1,144 +0,0 @@ -// Commit: 27e4302b7f45f22180693d26747f419177c81e27 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTEXTEDIT_P_P_H -#define QSGTEXTEDIT_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qsgtextedit_p.h" -#include "qsgimplicitsizeitem_p_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE -class QTextLayout; -class QTextDocument; -class QTextControl; -class QSGTextEditPrivate : public QSGImplicitSizeItemPrivate -{ - Q_DECLARE_PUBLIC(QSGTextEdit) - -public: - QSGTextEditPrivate() - : color("black"), hAlign(QSGTextEdit::AlignLeft), vAlign(QSGTextEdit::AlignTop), - documentDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true), - persistentSelection(true), requireImplicitWidth(false), selectByMouse(false), canPaste(false), - hAlignImplicit(true), rightToLeftText(false), useImageFallback(false), - textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), cursorComponent(0), cursor(0), - format(QSGTextEdit::AutoText), document(0), wrapMode(QSGTextEdit::NoWrap), - mouseSelectionMode(QSGTextEdit::SelectCharacters), - lineCount(0), yoff(0), nodeType(NodeIsNull), texture(0) - { - } - - void init(); - - void updateDefaultTextOption(); - void relayoutDocument(); - void updateSelection(); - bool determineHorizontalAlignment(); - bool setHAlign(QSGTextEdit::HAlignment, bool forceAlign = false); - void mirrorChange(); - qreal getImplicitWidth() const; - - QString text; - QFont font; - QFont sourceFont; - QColor color; - QColor selectionColor; - QColor selectedTextColor; - QString style; - QColor styleColor; - QSGTextEdit::HAlignment hAlign; - QSGTextEdit::VAlignment vAlign; - - bool documentDirty : 1; - bool dirty : 1; - bool richText : 1; - bool cursorVisible : 1; - bool focusOnPress : 1; - bool persistentSelection : 1; - bool requireImplicitWidth:1; - bool selectByMouse:1; - bool canPaste:1; - bool hAlignImplicit:1; - bool rightToLeftText:1; - bool useImageFallback:1; - - qreal textMargin; - int lastSelectionStart; - int lastSelectionEnd; - QDeclarativeComponent* cursorComponent; - QSGItem* cursor; - QSGTextEdit::TextFormat format; - QTextDocument *document; - QTextControl *control; - QSGTextEdit::WrapMode wrapMode; - QSGTextEdit::SelectionMode mouseSelectionMode; - int lineCount; - int yoff; - QSize paintedSize; - - enum NodeType { - NodeIsNull, - NodeIsTexture, - NodeIsText - }; - NodeType nodeType; - QSGTexture *texture; - QPixmap pixmapCache; -}; - -QT_END_NAMESPACE - -#endif // QSGTEXTEDIT_P_P_H diff --git a/src/declarative/items/qsgtextinput.cpp b/src/declarative/items/qsgtextinput.cpp deleted file mode 100644 index 6d0fa47d4f..0000000000 --- a/src/declarative/items/qsgtextinput.cpp +++ /dev/null @@ -1,2007 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgtextinput_p.h" -#include "qsgtextinput_p_p.h" -#include "qsgcanvas.h" - -#include -#include - -#include -#include -#include -#include "qsgtextnode_p.h" -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) - -/*! - \qmlclass TextInput QSGTextInput - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - \brief The TextInput item displays an editable line of text. - \inherits Item - - The TextInput element displays a single line of editable plain text. - - TextInput is used to accept a line of text input. Input constraints - can be placed on a TextInput item (for example, through a \l validator or \l inputMask), - and setting \l echoMode to an appropriate value enables TextInput to be used for - a password input field. - - On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled. - If you want such bindings (on any platform), you will need to construct them in QML. - - \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example} -*/ -QSGTextInput::QSGTextInput(QSGItem* parent) -: QSGImplicitSizeItem(*(new QSGTextInputPrivate), parent) -{ - Q_D(QSGTextInput); - d->init(); -} - -QSGTextInput::~QSGTextInput() -{ -} - -/*! - \qmlproperty string QtQuick2::TextInput::text - - The text in the TextInput. -*/ -QString QSGTextInput::text() const -{ - Q_D(const QSGTextInput); - return d->control->text(); -} - -void QSGTextInput::setText(const QString &s) -{ - Q_D(QSGTextInput); - if (s == text()) - return; - d->control->setText(s); -} - -/*! - \qmlproperty string QtQuick2::TextInput::font.family - - Sets the family name of the font. - - The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". - If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. - If the family isn't available a family will be set using the font matching algorithm. -*/ - -/*! - \qmlproperty bool QtQuick2::TextInput::font.bold - - Sets whether the font weight is bold. -*/ - -/*! - \qmlproperty enumeration QtQuick2::TextInput::font.weight - - Sets the font's weight. - - The weight can be one of: - \list - \o Font.Light - \o Font.Normal - the default - \o Font.DemiBold - \o Font.Bold - \o Font.Black - \endlist - - \qml - TextInput { text: "Hello"; font.weight: Font.DemiBold } - \endqml -*/ - -/*! - \qmlproperty bool QtQuick2::TextInput::font.italic - - Sets whether the font has an italic style. -*/ - -/*! - \qmlproperty bool QtQuick2::TextInput::font.underline - - Sets whether the text is underlined. -*/ - -/*! - \qmlproperty bool QtQuick2::TextInput::font.strikeout - - Sets whether the font has a strikeout style. -*/ - -/*! - \qmlproperty real QtQuick2::TextInput::font.pointSize - - Sets the font size in points. The point size must be greater than zero. -*/ - -/*! - \qmlproperty int QtQuick2::TextInput::font.pixelSize - - Sets the font size in pixels. - - Using this function makes the font device dependent. - Use \c pointSize to set the size of the font in a device independent manner. -*/ - -/*! - \qmlproperty real QtQuick2::TextInput::font.letterSpacing - - Sets the letter spacing for the font. - - Letter spacing changes the default spacing between individual letters in the font. - A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. -*/ - -/*! - \qmlproperty real QtQuick2::TextInput::font.wordSpacing - - Sets the word spacing for the font. - - Word spacing changes the default spacing between individual words. - A positive value increases the word spacing by a corresponding amount of pixels, - while a negative value decreases the inter-word spacing accordingly. -*/ - -/*! - \qmlproperty enumeration QtQuick2::TextInput::font.capitalization - - Sets the capitalization for the text. - - \list - \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. - \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. - \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. - \o Font.SmallCaps - This alters the text to be rendered in small-caps type. - \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. - \endlist - - \qml - TextInput { text: "Hello"; font.capitalization: Font.AllLowercase } - \endqml -*/ - -QFont QSGTextInput::font() const -{ - Q_D(const QSGTextInput); - return d->sourceFont; -} - -void QSGTextInput::setFont(const QFont &font) -{ - Q_D(QSGTextInput); - if (d->sourceFont == font) - return; - - d->sourceFont = font; - QFont oldFont = d->font; - d->font = font; - if (d->font.pointSizeF() != -1) { - // 0.5pt resolution - qreal size = qRound(d->font.pointSizeF()*2.0); - d->font.setPointSizeF(size/2.0); - } - if (oldFont != d->font) { - d->control->setFont(d->font); - updateSize(); - updateCursorRectangle(); - if (d->cursorItem) { - d->cursorItem->setHeight(QFontMetrics(d->font).height()); - } - } - emit fontChanged(d->sourceFont); -} - -/*! - \qmlproperty color QtQuick2::TextInput::color - - The text color. -*/ -QColor QSGTextInput::color() const -{ - Q_D(const QSGTextInput); - return d->color; -} - -void QSGTextInput::setColor(const QColor &c) -{ - Q_D(QSGTextInput); - if (c != d->color) { - d->color = c; - update(); - emit colorChanged(c); - } -} - - -/*! - \qmlproperty color QtQuick2::TextInput::selectionColor - - The text highlight color, used behind selections. -*/ -QColor QSGTextInput::selectionColor() const -{ - Q_D(const QSGTextInput); - return d->selectionColor; -} - -void QSGTextInput::setSelectionColor(const QColor &color) -{ - Q_D(QSGTextInput); - if (d->selectionColor == color) - return; - - d->selectionColor = color; - QPalette p = d->control->palette(); - p.setColor(QPalette::Highlight, d->selectionColor); - d->control->setPalette(p); - if (d->control->hasSelectedText()) - update(); - emit selectionColorChanged(color); -} -/*! - \qmlproperty color QtQuick2::TextInput::selectedTextColor - - The highlighted text color, used in selections. -*/ -QColor QSGTextInput::selectedTextColor() const -{ - Q_D(const QSGTextInput); - return d->selectedTextColor; -} - -void QSGTextInput::setSelectedTextColor(const QColor &color) -{ - Q_D(QSGTextInput); - if (d->selectedTextColor == color) - return; - - d->selectedTextColor = color; - QPalette p = d->control->palette(); - p.setColor(QPalette::HighlightedText, d->selectedTextColor); - d->control->setPalette(p); - if (d->control->hasSelectedText()) - update(); - emit selectedTextColorChanged(color); -} - -/*! - \qmlproperty enumeration QtQuick2::TextInput::horizontalAlignment - \qmlproperty enumeration QtQuick2::TextInput::effectiveHorizontalAlignment - - Sets the horizontal alignment of the text within the TextInput item's - width and height. By default, the text alignment follows the natural alignment - of the text, for example text that is read from left to right will be aligned to - the left. - - TextInput does not have vertical alignment, as the natural height is - exactly the height of the single line of text. If you set the height - manually to something larger, TextInput will always be top aligned - vertically. You can use anchors to align it however you want within - another item. - - The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and - \c TextInput.AlignHCenter. - - When using the attached property LayoutMirroring::enabled to mirror application - layouts, the horizontal alignment of text will also be mirrored. However, the property - \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment - of TextInput, use the read-only property \c effectiveHorizontalAlignment. -*/ -QSGTextInput::HAlignment QSGTextInput::hAlign() const -{ - Q_D(const QSGTextInput); - return d->hAlign; -} - -void QSGTextInput::setHAlign(HAlignment align) -{ - Q_D(QSGTextInput); - bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; - d->hAlignImplicit = false; - if (d->setHAlign(align, forceAlign) && isComponentComplete()) { - updateCursorRectangle(); - } -} - -void QSGTextInput::resetHAlign() -{ - Q_D(QSGTextInput); - d->hAlignImplicit = true; - if (d->determineHorizontalAlignment() && isComponentComplete()) { - updateCursorRectangle(); - } -} - -QSGTextInput::HAlignment QSGTextInput::effectiveHAlign() const -{ - Q_D(const QSGTextInput); - QSGTextInput::HAlignment effectiveAlignment = d->hAlign; - if (!d->hAlignImplicit && d->effectiveLayoutMirror) { - switch (d->hAlign) { - case QSGTextInput::AlignLeft: - effectiveAlignment = QSGTextInput::AlignRight; - break; - case QSGTextInput::AlignRight: - effectiveAlignment = QSGTextInput::AlignLeft; - break; - default: - break; - } - } - return effectiveAlignment; -} - -bool QSGTextInputPrivate::setHAlign(QSGTextInput::HAlignment alignment, bool forceAlign) -{ - Q_Q(QSGTextInput); - if ((hAlign != alignment || forceAlign) && alignment <= QSGTextInput::AlignHCenter) { // justify not supported - QSGTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); - hAlign = alignment; - emit q->horizontalAlignmentChanged(alignment); - if (oldEffectiveHAlign != q->effectiveHAlign()) - emit q->effectiveHorizontalAlignmentChanged(); - return true; - } - return false; -} - -bool QSGTextInputPrivate::determineHorizontalAlignment() -{ - if (hAlignImplicit) { - // if no explicit alignment has been set, follow the natural layout direction of the text - QString text = control->text(); - if (text.isEmpty()) - text = control->preeditAreaText(); - bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft(); - return setHAlign(isRightToLeft ? QSGTextInput::AlignRight : QSGTextInput::AlignLeft); - } - return false; -} - -void QSGTextInputPrivate::mirrorChange() -{ - Q_Q(QSGTextInput); - if (q->isComponentComplete()) { - if (!hAlignImplicit && (hAlign == QSGTextInput::AlignRight || hAlign == QSGTextInput::AlignLeft)) { - q->updateCursorRectangle(); - emit q->effectiveHorizontalAlignmentChanged(); - } - } -} - -/*! - \qmlproperty bool QtQuick2::TextInput::readOnly - - Sets whether user input can modify the contents of the TextInput. - - If readOnly is set to true, then user input will not affect the text - property. Any bindings or attempts to set the text property will still - work. -*/ -bool QSGTextInput::isReadOnly() const -{ - Q_D(const QSGTextInput); - return d->control->isReadOnly(); -} - -void QSGTextInput::setReadOnly(bool ro) -{ - Q_D(QSGTextInput); - if (d->control->isReadOnly() == ro) - return; - - setFlag(QSGItem::ItemAcceptsInputMethod, !ro); - d->control->setReadOnly(ro); - if (!ro) - d->control->setCursorPosition(d->control->end()); - - emit readOnlyChanged(ro); -} - -/*! - \qmlproperty int QtQuick2::TextInput::maximumLength - The maximum permitted length of the text in the TextInput. - - If the text is too long, it is truncated at the limit. - - By default, this property contains a value of 32767. -*/ -int QSGTextInput::maxLength() const -{ - Q_D(const QSGTextInput); - return d->control->maxLength(); -} - -void QSGTextInput::setMaxLength(int ml) -{ - Q_D(QSGTextInput); - if (d->control->maxLength() == ml) - return; - - d->control->setMaxLength(ml); - - emit maximumLengthChanged(ml); -} - -/*! - \qmlproperty bool QtQuick2::TextInput::cursorVisible - Set to true when the TextInput shows a cursor. - - This property is set and unset when the TextInput gets active focus, so that other - properties can be bound to whether the cursor is currently showing. As it - gets set and unset automatically, when you set the value yourself you must - keep in mind that your value may be overwritten. - - It can be set directly in script, for example if a KeyProxy might - forward keys to it and you desire it to look active when this happens - (but without actually giving it active focus). - - It should not be set directly on the element, like in the below QML, - as the specified value will be overridden an lost on focus changes. - - \code - TextInput { - text: "Text" - cursorVisible: false - } - \endcode - - In the above snippet the cursor will still become visible when the - TextInput gains active focus. -*/ -bool QSGTextInput::isCursorVisible() const -{ - Q_D(const QSGTextInput); - return d->cursorVisible; -} - -void QSGTextInput::setCursorVisible(bool on) -{ - Q_D(QSGTextInput); - if (d->cursorVisible == on) - return; - d->cursorVisible = on; - d->control->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0); - QRect r = d->control->cursorRect(); - if (d->control->inputMask().isEmpty()) - updateRect(r); - else - updateRect(); - emit cursorVisibleChanged(d->cursorVisible); -} - -/*! - \qmlproperty int QtQuick2::TextInput::cursorPosition - The position of the cursor in the TextInput. -*/ -int QSGTextInput::cursorPosition() const -{ - Q_D(const QSGTextInput); - return d->control->cursor(); -} -void QSGTextInput::setCursorPosition(int cp) -{ - Q_D(QSGTextInput); - if (cp < 0 || cp > d->control->text().length()) - return; - d->control->moveCursor(cp); -} - -/*! - Returns a Rect which encompasses the cursor, but which may be larger than is - required. Ignores custom cursor delegates. -*/ -QRect QSGTextInput::cursorRectangle() const -{ - Q_D(const QSGTextInput); - QRect r = d->control->cursorRect(); - // Scroll and make consistent with TextEdit - // QLineControl inexplicably adds 1 to the height and horizontal padding - // for unicode direction markers. - r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1); - return r; -} -/*! - \qmlproperty int QtQuick2::TextInput::selectionStart - - The cursor position before the first character in the current selection. - - This property is read-only. To change the selection, use select(start,end), - selectAll(), or selectWord(). - - \sa selectionEnd, cursorPosition, selectedText -*/ -int QSGTextInput::selectionStart() const -{ - Q_D(const QSGTextInput); - return d->lastSelectionStart; -} -/*! - \qmlproperty int QtQuick2::TextInput::selectionEnd - - The cursor position after the last character in the current selection. - - This property is read-only. To change the selection, use select(start,end), - selectAll(), or selectWord(). - - \sa selectionStart, cursorPosition, selectedText -*/ -int QSGTextInput::selectionEnd() const -{ - Q_D(const QSGTextInput); - return d->lastSelectionEnd; -} -/*! - \qmlmethod void QtQuick2::TextInput::select(int start, int end) - - Causes the text from \a start to \a end to be selected. - - If either start or end is out of range, the selection is not changed. - - After calling this, selectionStart will become the lesser - and selectionEnd will become the greater (regardless of the order passed - to this method). - - \sa selectionStart, selectionEnd -*/ -void QSGTextInput::select(int start, int end) -{ - Q_D(QSGTextInput); - if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length()) - return; - d->control->setSelection(start, end-start); -} - -/*! - \qmlproperty string QtQuick2::TextInput::selectedText - - This read-only property provides the text currently selected in the - text input. - - It is equivalent to the following snippet, but is faster and easier - to use. - - \js - myTextInput.text.toString().substring(myTextInput.selectionStart, - myTextInput.selectionEnd); - \endjs -*/ -QString QSGTextInput::selectedText() const -{ - Q_D(const QSGTextInput); - return d->control->selectedText(); -} - -/*! - \qmlproperty bool QtQuick2::TextInput::activeFocusOnPress - - Whether the TextInput should gain active focus on a mouse press. By default this is - set to true. -*/ -bool QSGTextInput::focusOnPress() const -{ - Q_D(const QSGTextInput); - return d->focusOnPress; -} - -void QSGTextInput::setFocusOnPress(bool b) -{ - Q_D(QSGTextInput); - if (d->focusOnPress == b) - return; - - d->focusOnPress = b; - - emit activeFocusOnPressChanged(d->focusOnPress); -} -/*! - \qmlproperty bool QtQuick2::TextInput::autoScroll - - Whether the TextInput should scroll when the text is longer than the width. By default this is - set to true. -*/ -bool QSGTextInput::autoScroll() const -{ - Q_D(const QSGTextInput); - return d->autoScroll; -} - -void QSGTextInput::setAutoScroll(bool b) -{ - Q_D(QSGTextInput); - if (d->autoScroll == b) - return; - - d->autoScroll = b; - //We need to repaint so that the scrolling is taking into account. - updateSize(true); - updateCursorRectangle(); - emit autoScrollChanged(d->autoScroll); -} - -#ifndef QT_NO_VALIDATOR - -/*! - \qmlclass IntValidator QIntValidator - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - - This element provides a validator for integer values. - - IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and - will accept locale specific digits, group separators, and positive and negative signs. In - addition, IntValidator is always guaranteed to accept a number formatted according to the "C" - locale. -*/ -/*! - \qmlproperty int QtQuick2::IntValidator::top - - This property holds the validator's highest acceptable value. - By default, this property's value is derived from the highest signed integer available (typically 2147483647). -*/ -/*! - \qmlproperty int QtQuick2::IntValidator::bottom - - This property holds the validator's lowest acceptable value. - By default, this property's value is derived from the lowest signed integer available (typically -2147483647). -*/ - -/*! - \qmlclass DoubleValidator QDoubleValidator - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - - This element provides a validator for non-integer numbers. -*/ - -/*! - \qmlproperty real QtQuick2::DoubleValidator::top - - This property holds the validator's maximum acceptable value. - By default, this property contains a value of infinity. -*/ -/*! - \qmlproperty real QtQuick2::DoubleValidator::bottom - - This property holds the validator's minimum acceptable value. - By default, this property contains a value of -infinity. -*/ -/*! - \qmlproperty int QtQuick2::DoubleValidator::decimals - - This property holds the validator's maximum number of digits after the decimal point. - By default, this property contains a value of 1000. -*/ -/*! - \qmlproperty enumeration QtQuick2::DoubleValidator::notation - This property holds the notation of how a string can describe a number. - - The possible values for this property are: - - \list - \o DoubleValidator.StandardNotation - \o DoubleValidator.ScientificNotation (default) - \endlist - - If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2). -*/ - -/*! - \qmlclass RegExpValidator QRegExpValidator - \inqmlmodule QtQuick 2 - \ingroup qml-basic-visual-elements - - This element provides a validator, which counts as valid any string which - matches a specified regular expression. -*/ -/*! - \qmlproperty regExp QtQuick2::RegExpValidator::regExp - - This property holds the regular expression used for validation. - - Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression - matching "a". - - By default, this property contains a regular expression with the pattern .* that matches any string. -*/ - -/*! - \qmlproperty Validator QtQuick2::TextInput::validator - - Allows you to set a validator on the TextInput. When a validator is set - the TextInput will only accept input which leaves the text property in - an acceptable or intermediate state. The accepted signal will only be sent - if the text is in an acceptable state when enter is pressed. - - Currently supported validators are IntValidator, DoubleValidator and - RegExpValidator. An example of using validators is shown below, which allows - input of integers between 11 and 31 into the text input: - - \code - import QtQuick 1.0 - TextInput{ - validator: IntValidator{bottom: 11; top: 31;} - focus: true - } - \endcode - - \sa acceptableInput, inputMask -*/ - -QValidator* QSGTextInput::validator() const -{ - Q_D(const QSGTextInput); - //###const cast isn't good, but needed for property system? - return const_cast(d->control->validator()); -} - -void QSGTextInput::setValidator(QValidator* v) -{ - Q_D(QSGTextInput); - if (d->control->validator() == v) - return; - - d->control->setValidator(v); - if (!d->control->hasAcceptableInput()) { - d->oldValidity = false; - emit acceptableInputChanged(); - } - - emit validatorChanged(); -} -#endif // QT_NO_VALIDATOR - -/*! - \qmlproperty string QtQuick2::TextInput::inputMask - - Allows you to set an input mask on the TextInput, restricting the allowable - text inputs. See QLineEdit::inputMask for further details, as the exact - same mask strings are used by TextInput. - - \sa acceptableInput, validator -*/ -QString QSGTextInput::inputMask() const -{ - Q_D(const QSGTextInput); - return d->control->inputMask(); -} - -void QSGTextInput::setInputMask(const QString &im) -{ - Q_D(QSGTextInput); - if (d->control->inputMask() == im) - return; - - d->control->setInputMask(im); - emit inputMaskChanged(d->control->inputMask()); -} - -/*! - \qmlproperty bool QtQuick2::TextInput::acceptableInput - - This property is always true unless a validator or input mask has been set. - If a validator or input mask has been set, this property will only be true - if the current text is acceptable to the validator or input mask as a final - string (not as an intermediate string). -*/ -bool QSGTextInput::hasAcceptableInput() const -{ - Q_D(const QSGTextInput); - return d->control->hasAcceptableInput(); -} - -/*! - \qmlsignal QtQuick2::TextInput::onAccepted() - - This handler is called when the Return or Enter key is pressed. - Note that if there is a \l validator or \l inputMask set on the text - input, the handler will only be emitted if the input is in an acceptable - state. -*/ - -void QSGTextInputPrivate::updateInputMethodHints() -{ - Q_Q(QSGTextInput); - Qt::InputMethodHints hints = inputMethodHints; - uint echo = control->echoMode(); - if (echo == QSGTextInput::Password || echo == QSGTextInput::NoEcho) - hints |= Qt::ImhHiddenText; - else if (echo == QSGTextInput::PasswordEchoOnEdit) - hints &= ~Qt::ImhHiddenText; - if (echo != QSGTextInput::Normal) - hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText); - q->setInputMethodHints(hints); -} -/*! - \qmlproperty enumeration QtQuick2::TextInput::echoMode - - Specifies how the text should be displayed in the TextInput. - \list - \o TextInput.Normal - Displays the text as it is. (Default) - \o TextInput.Password - Displays asterixes instead of characters. - \o TextInput.NoEcho - Displays nothing. - \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered - while editing, otherwise displays asterisks. - \endlist -*/ -QSGTextInput::EchoMode QSGTextInput::echoMode() const -{ - Q_D(const QSGTextInput); - return (QSGTextInput::EchoMode)d->control->echoMode(); -} - -void QSGTextInput::setEchoMode(QSGTextInput::EchoMode echo) -{ - Q_D(QSGTextInput); - if (echoMode() == echo) - return; - d->control->setEchoMode((QLineControl::EchoMode)echo); - d->updateInputMethodHints(); - q_textChanged(); - emit echoModeChanged(echoMode()); -} - -Qt::InputMethodHints QSGTextInput::imHints() const -{ - Q_D(const QSGTextInput); - return d->inputMethodHints; -} - -void QSGTextInput::setIMHints(Qt::InputMethodHints hints) -{ - Q_D(QSGTextInput); - if (d->inputMethodHints == hints) - return; - d->inputMethodHints = hints; - d->updateInputMethodHints(); -} - -/*! - \qmlproperty Component QtQuick2::TextInput::cursorDelegate - The delegate for the cursor in the TextInput. - - If you set a cursorDelegate for a TextInput, this delegate will be used for - drawing the cursor instead of the standard cursor. An instance of the - delegate will be created and managed by the TextInput when a cursor is - needed, and the x property of delegate instance will be set so as - to be one pixel before the top left of the current character. - - Note that the root item of the delegate component must be a QDeclarativeItem or - QDeclarativeItem derived item. -*/ -QDeclarativeComponent* QSGTextInput::cursorDelegate() const -{ - Q_D(const QSGTextInput); - return d->cursorComponent; -} - -void QSGTextInput::setCursorDelegate(QDeclarativeComponent* c) -{ - Q_D(QSGTextInput); - if (d->cursorComponent == c) - return; - - d->cursorComponent = c; - if (!c) { - //note that the components are owned by something else - delete d->cursorItem; - } else { - d->startCreatingCursor(); - } - - emit cursorDelegateChanged(); -} - -void QSGTextInputPrivate::startCreatingCursor() -{ - Q_Q(QSGTextInput); - if (cursorComponent->isReady()) { - q->createCursor(); - } else if (cursorComponent->isLoading()) { - q->connect(cursorComponent, SIGNAL(statusChanged(int)), - q, SLOT(createCursor())); - } else { // isError - qmlInfo(q, cursorComponent->errors()) << QSGTextInput::tr("Could not load cursor delegate"); - } -} - -void QSGTextInput::createCursor() -{ - Q_D(QSGTextInput); - if (d->cursorComponent->isError()) { - qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate"); - return; - } - - if (!d->cursorComponent->isReady()) - return; - - if (d->cursorItem) - delete d->cursorItem; - QDeclarativeContext *creationContext = d->cursorComponent->creationContext(); - QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this)); - d->cursorItem = qobject_cast(object); - if (!d->cursorItem) { - delete object; - qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate"); - return; - } - - QDeclarative_setParent_noEvent(d->cursorItem, this); - d->cursorItem->setParentItem(this); - d->cursorItem->setX(d->control->cursorToX()); - d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. -} - -/*! - \qmlmethod rect QtQuick2::TextInput::positionToRectangle(int pos) - - This function takes a character position and returns the rectangle that the - cursor would occupy, if it was placed at that character position. - - This is similar to setting the cursorPosition, and then querying the cursor - rectangle, but the cursorPosition is not changed. -*/ -QRectF QSGTextInput::positionToRectangle(int pos) const -{ - Q_D(const QSGTextInput); - if (pos > d->control->cursorPosition()) - pos += d->control->preeditAreaText().length(); - return QRectF(d->control->cursorToX(pos)-d->hscroll, - 0.0, - d->control->cursorWidth(), - cursorRectangle().height()); -} - -/*! - \qmlmethod int QtQuick2::TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters) - - This function returns the character position at - x pixels from the left of the textInput. Position 0 is before the - first character, position 1 is after the first character but before the second, - and so on until position text.length, which is after all characters. - - This means that for all x values before the first character this function returns 0, - and for all x values after the last character this function returns text.length. - - The cursor position type specifies how the cursor position should be resolved. - - \list - \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x. - \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x. - \endlist -*/ -int QSGTextInput::positionAt(int x) const -{ - return positionAt(x, CursorBetweenCharacters); -} - -int QSGTextInput::positionAt(int x, CursorPosition position) const -{ - Q_D(const QSGTextInput); - int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); - const int cursor = d->control->cursor(); - if (pos > cursor) { - const int preeditLength = d->control->preeditAreaText().length(); - pos = pos > cursor + preeditLength - ? pos - preeditLength - : cursor; - } - return pos; -} - -void QSGTextInput::keyPressEvent(QKeyEvent* ev) -{ - Q_D(QSGTextInput); - // Don't allow MacOSX up/down support, and we don't allow a completer. - bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier; - if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) { - // Ignore when moving off the end unless there is a selection, - // because then moving will do something (deselect). - int cursorPosition = d->control->cursor(); - if (cursorPosition == 0) - ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right); - if (cursorPosition == d->control->text().length()) - ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left); - } - if (ignore) { - ev->ignore(); - } else { - d->control->processKeyEvent(ev); - } - if (!ev->isAccepted()) - QSGImplicitSizeItem::keyPressEvent(ev); -} - -void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev) -{ - Q_D(QSGTextInput); - const bool wasComposing = d->control->preeditAreaText().length() > 0; - if (d->control->isReadOnly()) { - ev->ignore(); - } else { - d->control->processInputMethodEvent(ev); - } - if (!ev->isAccepted()) - QSGImplicitSizeItem::inputMethodEvent(ev); - - if (wasComposing != (d->control->preeditAreaText().length() > 0)) - emit inputMethodComposingChanged(); -} - -void QSGTextInput::mouseDoubleClickEvent(QMouseEvent *event) -{ - Q_D(QSGTextInput); - if (d->sendMouseEventToInputContext(event)) - return; - if (d->selectByMouse) { - int cursor = d->xToPos(event->localPos().x()); - d->control->selectWordAtPos(cursor); - event->setAccepted(true); - if (!d->hasPendingTripleClick()) { - d->tripleClickStartPoint = event->localPos().toPoint(); - d->tripleClickTimer.start(); - } - } else { - QSGImplicitSizeItem::mouseDoubleClickEvent(event); - } -} - -void QSGTextInput::mousePressEvent(QMouseEvent *event) -{ - Q_D(QSGTextInput); - if (d->sendMouseEventToInputContext(event)) - return; - if (d->focusOnPress) { - bool hadActiveFocus = hasActiveFocus(); - forceActiveFocus(); - // re-open input panel on press if already focused - if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) - openSoftwareInputPanel(); - } - if (d->selectByMouse) { - setKeepMouseGrab(false); - d->selectPressed = true; - d->pressPos = event->localPos(); - QPoint distanceVector = d->pressPos.toPoint() - d->tripleClickStartPoint; - if (d->hasPendingTripleClick() - && distanceVector.manhattanLength() < qApp->styleHints()->startDragDistance()) { - event->setAccepted(true); - selectAll(); - return; - } - } - bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; - int cursor = d->xToPos(event->localPos().x()); - d->control->moveCursor(cursor, mark); - event->setAccepted(true); -} - -void QSGTextInput::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QSGTextInput); - if (d->sendMouseEventToInputContext(event)) - return; - if (d->selectPressed) { - if (qAbs(int(event->localPos().x() - d->pressPos.x())) > qApp->styleHints()->startDragDistance()) - setKeepMouseGrab(true); - moveCursorSelection(d->xToPos(event->localPos().x()), d->mouseSelectionMode); - event->setAccepted(true); - } else { - QSGImplicitSizeItem::mouseMoveEvent(event); - } -} - -void QSGTextInput::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QSGTextInput); - if (d->sendMouseEventToInputContext(event)) - return; - if (d->selectPressed) { - d->selectPressed = false; - setKeepMouseGrab(false); - } - d->control->processEvent(event); - if (!event->isAccepted()) - QSGImplicitSizeItem::mouseReleaseEvent(event); -} - -bool QSGTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event) -{ -#if !defined QT_NO_IM - if (control->composeMode() && event->type() == QEvent::KeyRelease) { - int tmp_cursor = xToPos(event->localPos().x()); - int mousePos = tmp_cursor - control->cursor(); - if (mousePos < 0 || mousePos > control->preeditAreaText().length()) { - mousePos = -1; - // don't send move events outside the preedit area - if (event->type() == QEvent::MouseMove) - return true; - } - - // may be causing reset() in some input methods - qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos); - if (!control->preeditAreaText().isEmpty()) - return true; - } -#else - Q_UNUSED(event); - Q_UNUSED(eventType) -#endif - - return false; -} - -void QSGTextInput::mouseUngrabEvent() -{ - Q_D(QSGTextInput); - d->selectPressed = false; - setKeepMouseGrab(false); -} - -bool QSGTextInput::event(QEvent* ev) -{ - Q_D(QSGTextInput); - //Anything we don't deal with ourselves, pass to the control - bool handled = false; - switch (ev->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease://###Should the control be doing anything with release? - case QEvent::InputMethod: - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - break; - default: - handled = d->control->processEvent(ev); - } - if (!handled) - handled = QSGImplicitSizeItem::event(ev); - return handled; -} - -void QSGTextInput::geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry) -{ - if (newGeometry.width() != oldGeometry.width()) { - updateSize(); - updateCursorRectangle(); - } - QSGImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); -} - -int QSGTextInputPrivate::calculateTextWidth() -{ - return qRound(control->naturalTextWidth()); -} - -void QSGTextInputPrivate::updateHorizontalScroll() -{ - Q_Q(QSGTextInput); - const int preeditLength = control->preeditAreaText().length(); - const int width = q->width(); - int widthUsed = calculateTextWidth(); - - if (!autoScroll || widthUsed <= width) { - QSGTextInput::HAlignment effectiveHAlign = q->effectiveHAlign(); - // text fits in br; use hscroll for alignment - switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { - case Qt::AlignRight: - hscroll = widthUsed - width; - break; - case Qt::AlignHCenter: - hscroll = (widthUsed - width) / 2; - break; - default: - // Left - hscroll = 0; - break; - } - } else { - int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); - if (cix - hscroll >= width) { - // text doesn't fit, cursor is to the right of br (scroll right) - hscroll = cix - width; - } else if (cix - hscroll < 0 && hscroll < widthUsed) { - // text doesn't fit, cursor is to the left of br (scroll left) - hscroll = cix; - } else if (widthUsed - hscroll < width) { - // text doesn't fit, text document is to the left of br; align - // right - hscroll = widthUsed - width; - } - if (preeditLength > 0) { - // check to ensure long pre-edit text doesn't push the cursor - // off to the left - cix = qRound(control->cursorToX( - control->cursor() + qMax(0, control->preeditCursor() - 1))); - if (cix < hscroll) - hscroll = cix; - } - } -} - -QSGNode *QSGTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) -{ - Q_UNUSED(data); - Q_D(QSGTextInput); - - QSGTextNode *node = static_cast(oldNode); - if (node == 0) - node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext()); - d->textNode = node; - - if (!d->textLayoutDirty) { - QSGSimpleRectNode *cursorNode = node->cursorNode(); - if (cursorNode != 0 && !isReadOnly()) { - QFontMetrics fm = QFontMetrics(d->font); - // the y offset is there to keep the baseline constant in case we have script changes in the text. - QPoint offset(-d->hscroll, fm.ascent() - d->control->ascent()); - offset.rx() += d->control->cursorToX(); - - QRect br(boundingRect().toRect()); - cursorNode->setRect(QRectF(offset, QSizeF(d->control->cursorWidth(), br.height()))); - - if (!d->cursorVisible - || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) { - d->hideCursor(); - } else { - d->showCursor(); - } - } - } else { - node->deleteContent(); - node->setMatrix(QMatrix4x4()); - - QPoint offset = QPoint(0,0); - QFontMetrics fm = QFontMetrics(d->font); - QRect br(boundingRect().toRect()); - if (d->autoScroll) { - // the y offset is there to keep the baseline constant in case we have script changes in the text. - offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent()); - } else { - offset = QPoint(d->hscroll, 0); - } - - QTextLayout *textLayout = d->control->textLayout(); - if (!textLayout->text().isEmpty()) { - node->addTextLayout(offset, textLayout, d->color, - QSGText::Normal, QColor(), - d->selectionColor, d->selectedTextColor, - d->control->selectionStart(), - d->control->selectionEnd() - 1); // selectionEnd() returns first char after - // selection - } - - if (!isReadOnly() && d->cursorItem == 0) { - offset.rx() += d->control->cursorToX(); - node->setCursor(QRectF(offset, QSizeF(d->control->cursorWidth(), br.height())), d->color); - if (!d->cursorVisible - || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) { - d->hideCursor(); - } else { - d->showCursor(); - } - } - - d->textLayoutDirty = false; - } - - return node; -} - -QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const -{ - Q_D(const QSGTextInput); - switch (property) { - case Qt::ImEnabled: - return QVariant((bool)(flags() & ItemAcceptsInputMethod)); - case Qt::ImHints: - return QVariant((int)inputMethodHints()); - case Qt::ImCursorRectangle: - return cursorRectangle(); - case Qt::ImFont: - return font(); - case Qt::ImCursorPosition: - return QVariant(d->control->cursor()); - case Qt::ImSurroundingText: - if (d->control->echoMode() == QLineControl::PasswordEchoOnEdit - && !d->control->passwordEchoEditing()) { - return QVariant(displayText()); - } else { - return QVariant(text()); - } - case Qt::ImCurrentSelection: - return QVariant(selectedText()); - case Qt::ImMaximumTextLength: - return QVariant(maxLength()); - case Qt::ImAnchorPosition: - if (d->control->selectionStart() == d->control->selectionEnd()) - return QVariant(d->control->cursor()); - else if (d->control->selectionStart() == d->control->cursor()) - return QVariant(d->control->selectionEnd()); - else - return QVariant(d->control->selectionStart()); - default: - return QVariant(); - } -} - -/*! - \qmlmethod void QtQuick2::TextInput::deselect() - - Removes active text selection. -*/ -void QSGTextInput::deselect() -{ - Q_D(QSGTextInput); - d->control->deselect(); -} - -/*! - \qmlmethod void QtQuick2::TextInput::selectAll() - - Causes all text to be selected. -*/ -void QSGTextInput::selectAll() -{ - Q_D(QSGTextInput); - d->control->setSelection(0, d->control->text().length()); -} - -/*! - \qmlmethod void QtQuick2::TextInput::isRightToLeft(int start, int end) - - Returns true if the natural reading direction of the editor text - found between positions \a start and \a end is right to left. -*/ -bool QSGTextInput::isRightToLeft(int start, int end) -{ - Q_D(QSGTextInput); - if (start > end) { - qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start."; - return false; - } else { - return d->control->text().mid(start, end - start).isRightToLeft(); - } -} - -#ifndef QT_NO_CLIPBOARD -/*! - \qmlmethod QtQuick2::TextInput::cut() - - Moves the currently selected text to the system clipboard. -*/ -void QSGTextInput::cut() -{ - Q_D(QSGTextInput); - d->control->copy(); - d->control->del(); -} - -/*! - \qmlmethod QtQuick2::TextInput::copy() - - Copies the currently selected text to the system clipboard. -*/ -void QSGTextInput::copy() -{ - Q_D(QSGTextInput); - d->control->copy(); -} - -/*! - \qmlmethod QtQuick2::TextInput::paste() - - Replaces the currently selected text by the contents of the system clipboard. -*/ -void QSGTextInput::paste() -{ - Q_D(QSGTextInput); - if (!d->control->isReadOnly()) - d->control->paste(); -} -#endif // QT_NO_CLIPBOARD - -/*! - \qmlmethod void QtQuick2::TextInput::selectWord() - - Causes the word closest to the current cursor position to be selected. -*/ -void QSGTextInput::selectWord() -{ - Q_D(QSGTextInput); - d->control->selectWordAtPos(d->control->cursor()); -} - -/*! - \qmlproperty bool QtQuick2::TextInput::smooth - - This property holds whether the text is smoothly scaled or transformed. - - Smooth filtering gives better visual quality, but is slower. If - the item is displayed at its natural size, this property has no visual or - performance effect. - - \note Generally scaling artifacts are only visible if the item is stationary on - the screen. A common pattern when animating an item is to disable smooth - filtering at the beginning of the animation and reenable it at the conclusion. -*/ - -/*! - \qmlproperty string QtQuick2::TextInput::passwordCharacter - - This is the character displayed when echoMode is set to Password or - PasswordEchoOnEdit. By default it is an asterisk. - - If this property is set to a string with more than one character, - the first character is used. If the string is empty, the value - is ignored and the property is not set. -*/ -QString QSGTextInput::passwordCharacter() const -{ - Q_D(const QSGTextInput); - return QString(d->control->passwordCharacter()); -} - -void QSGTextInput::setPasswordCharacter(const QString &str) -{ - Q_D(QSGTextInput); - if (str.length() < 1) - return; - d->control->setPasswordCharacter(str.constData()[0]); - EchoMode echoMode_ = echoMode(); - if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) { - updateSize(); - } - emit passwordCharacterChanged(); -} - -/*! - \qmlproperty string QtQuick2::TextInput::displayText - - This is the text displayed in the TextInput. - - If \l echoMode is set to TextInput::Normal, this holds the - same value as the TextInput::text property. Otherwise, - this property holds the text visible to the user, while - the \l text property holds the actual entered text. -*/ -QString QSGTextInput::displayText() const -{ - Q_D(const QSGTextInput); - return d->control->displayText(); -} - -/*! - \qmlproperty bool QtQuick2::TextInput::selectByMouse - - Defaults to false. - - If true, the user can use the mouse to select text in some - platform-specific way. Note that for some platforms this may - not be an appropriate interaction (eg. may conflict with how - the text needs to behave inside a Flickable. -*/ -bool QSGTextInput::selectByMouse() const -{ - Q_D(const QSGTextInput); - return d->selectByMouse; -} - -void QSGTextInput::setSelectByMouse(bool on) -{ - Q_D(QSGTextInput); - if (d->selectByMouse != on) { - d->selectByMouse = on; - emit selectByMouseChanged(on); - } -} - -/*! - \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode - - Specifies how text should be selected using a mouse. - - \list - \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default) - \o TextInput.SelectWords - The selection is updated with whole words. - \endlist - - This property only applies when \l selectByMouse is true. -*/ - -QSGTextInput::SelectionMode QSGTextInput::mouseSelectionMode() const -{ - Q_D(const QSGTextInput); - return d->mouseSelectionMode; -} - -void QSGTextInput::setMouseSelectionMode(SelectionMode mode) -{ - Q_D(QSGTextInput); - if (d->mouseSelectionMode != mode) { - d->mouseSelectionMode = mode; - emit mouseSelectionModeChanged(mode); - } -} - -/*! - \qmlproperty bool QtQuick2::TextInput::canPaste - - Returns true if the TextInput is writable and the content of the clipboard is - suitable for pasting into the TextEdit. -*/ -bool QSGTextInput::canPaste() const -{ - Q_D(const QSGTextInput); - return d->canPaste; -} - -void QSGTextInput::moveCursorSelection(int position) -{ - Q_D(QSGTextInput); - d->control->moveCursor(position, true); -} - -/*! - \qmlmethod void QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters) - - Moves the cursor to \a position and updates the selection according to the optional \a mode - parameter. (To only move the cursor, set the \l cursorPosition property.) - - When this method is called it additionally sets either the - selectionStart or the selectionEnd (whichever was at the previous cursor position) - to the specified position. This allows you to easily extend and contract the selected - text range. - - The selection mode specifies whether the selection is updated on a per character or a per word - basis. If not specified the selection mode will default to TextInput.SelectCharacters. - - \list - \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at - the previous cursor position) to the specified position. - \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all - words between the specified postion and the previous cursor position. Words partially in the - range are included. - \endlist - - For example, take this sequence of calls: - - \code - cursorPosition = 5 - moveCursorSelection(9, TextInput.SelectCharacters) - moveCursorSelection(7, TextInput.SelectCharacters) - \endcode - - This moves the cursor to position 5, extend the selection end from 5 to 9 - and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 - selected (the 6th and 7th characters). - - The same sequence with TextInput.SelectWords will extend the selection start to a word boundary - before or on position 5 and extend the selection end to a word boundary on or past position 9. -*/ -void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode) -{ - Q_D(QSGTextInput); - - if (mode == SelectCharacters) { - d->control->moveCursor(pos, true); - } else if (pos != d->control->cursor()){ - const int cursor = d->control->cursor(); - int anchor; - if (!d->control->hasSelectedText()) - anchor = d->control->cursor(); - else if (d->control->selectionStart() == d->control->cursor()) - anchor = d->control->selectionEnd(); - else - anchor = d->control->selectionStart(); - - if (anchor < pos || (anchor == pos && cursor < pos)) { - const QString text = d->control->text(); - QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); - finder.setPosition(anchor); - - const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); - if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord) - || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) { - finder.toPreviousBoundary(); - } - anchor = finder.position() != -1 ? finder.position() : 0; - - finder.setPosition(pos); - if (pos > 0 && !finder.boundaryReasons()) - finder.toNextBoundary(); - const int cursor = finder.position() != -1 ? finder.position() : text.length(); - - d->control->setSelection(anchor, cursor - anchor); - } else if (anchor > pos || (anchor == pos && cursor > pos)) { - const QString text = d->control->text(); - QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); - finder.setPosition(anchor); - - const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); - if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord) - || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) { - finder.toNextBoundary(); - } - - anchor = finder.position() != -1 ? finder.position() : text.length(); - - finder.setPosition(pos); - if (pos < text.length() && !finder.boundaryReasons()) - finder.toPreviousBoundary(); - const int cursor = finder.position() != -1 ? finder.position() : 0; - - d->control->setSelection(anchor, cursor - anchor); - } - } -} - -/*! - \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel() - - Opens software input panels like virtual keyboards for typing, useful for - customizing when you want the input keyboard to be shown and hidden in - your application. - - By default the opening of input panels follows the platform style. On Symbian^1 and - Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms - the panels are automatically opened when TextInput element gains active focus. Input panels are - always closed if no editor has active focus. - - . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false - and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement - the behavior you want. - - Only relevant on platforms, which provide virtual keyboards. - - \qml - import QtQuick 1.0 - TextInput { - id: textInput - text: "Hello world!" - activeFocusOnPress: false - MouseArea { - anchors.fill: parent - onClicked: { - if (!textInput.activeFocus) { - textInput.forceActiveFocus() - textInput.openSoftwareInputPanel(); - } else { - textInput.focus = false; - } - } - onPressAndHold: textInput.closeSoftwareInputPanel(); - } - } - \endqml -*/ -void QSGTextInput::openSoftwareInputPanel() -{ - if (qGuiApp) - qGuiApp->inputPanel()->show(); -} - -/*! - \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel() - - Closes a software input panel like a virtual keyboard shown on the screen, useful - for customizing when you want the input keyboard to be shown and hidden in - your application. - - By default the opening of input panels follows the platform style. On Symbian^1 and - Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms - the panels are automatically opened when TextInput element gains active focus. Input panels are - always closed if no editor has active focus. - - . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false - and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement - the behavior you want. - - Only relevant on platforms, which provide virtual keyboards. - - \qml - import QtQuick 1.0 - TextInput { - id: textInput - text: "Hello world!" - activeFocusOnPress: false - MouseArea { - anchors.fill: parent - onClicked: { - if (!textInput.activeFocus) { - textInput.forceActiveFocus(); - textInput.openSoftwareInputPanel(); - } else { - textInput.focus = false; - } - } - onPressAndHold: textInput.closeSoftwareInputPanel(); - } - } - \endqml -*/ -void QSGTextInput::closeSoftwareInputPanel() -{ - if (qGuiApp) - qGuiApp->inputPanel()->hide(); -} - -void QSGTextInput::focusInEvent(QFocusEvent *event) -{ - Q_D(const QSGTextInput); - if (d->focusOnPress && !isReadOnly()) - openSoftwareInputPanel(); - QSGImplicitSizeItem::focusInEvent(event); -} - -void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value) -{ - Q_D(QSGTextInput); - if (change == ItemActiveFocusHasChanged) { - bool hasFocus = value.boolValue; - d->focused = hasFocus; - setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus() - if (echoMode() == QSGTextInput::PasswordEchoOnEdit && !hasFocus) - d->control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events - if (!hasFocus) - d->control->deselect(); - } - QSGItem::itemChange(change, value); -} - -/*! - \qmlproperty bool QtQuick2::TextInput::inputMethodComposing - - - This property holds whether the TextInput has partial text input from an - input method. - - While it is composing an input method may rely on mouse or key events from - the TextInput to edit or commit the partial text. This property can be - used to determine when to disable events handlers that may interfere with - the correct operation of an input method. -*/ -bool QSGTextInput::isInputMethodComposing() const -{ - Q_D(const QSGTextInput); - return d->control->preeditAreaText().length() > 0; -} - -void QSGTextInputPrivate::init() -{ - Q_Q(QSGTextInput); -#if defined(Q_WS_MAC) - control->setThreadChecks(true); -#endif - control->setParent(q);//Now mandatory due to accessibility changes - control->setCursorWidth(1); - control->setPasswordCharacter(QLatin1Char('*')); - q->setSmooth(smooth); - q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFlag(QSGItem::ItemAcceptsInputMethod); - q->setFlag(QSGItem::ItemHasContents); - q->connect(control, SIGNAL(cursorPositionChanged(int,int)), - q, SLOT(cursorPosChanged())); - q->connect(control, SIGNAL(selectionChanged()), - q, SLOT(selectionChanged())); - q->connect(control, SIGNAL(textChanged(QString)), - q, SLOT(q_textChanged())); - q->connect(control, SIGNAL(accepted()), - q, SIGNAL(accepted())); - q->connect(control, SIGNAL(updateNeeded(QRect)), - q, SLOT(updateRect(QRect))); -#ifndef QT_NO_CLIPBOARD - q->connect(q, SIGNAL(readOnlyChanged(bool)), - q, SLOT(q_canPasteChanged())); - q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), - q, SLOT(q_canPasteChanged())); - canPaste = !control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; -#endif // QT_NO_CLIPBOARD - q->connect(control, SIGNAL(updateMicroFocus()), - q, SLOT(updateCursorRectangle())); - q->connect(control, SIGNAL(displayTextChanged(QString)), - q, SLOT(updateRect())); - q->updateSize(); - imHints &= ~Qt::ImhMultiLine; - oldValidity = control->hasAcceptableInput(); - lastSelectionStart = 0; - lastSelectionEnd = 0; - QPalette p = control->palette(); - selectedTextColor = p.color(QPalette::HighlightedText); - selectionColor = p.color(QPalette::Highlight); - determineHorizontalAlignment(); - - if (!qmlDisableDistanceField()) { - QTextOption option = control->textLayout()->textOption(); - option.setUseDesignMetrics(true); - control->textLayout()->setTextOption(option); - } -} - -void QSGTextInput::cursorPosChanged() -{ - Q_D(QSGTextInput); - updateCursorRectangle(); - emit cursorPositionChanged(); - // XXX todo - not in 4.8? -#if 0 - d->control->resetCursorBlinkTimer(); -#endif - - if (!d->control->hasSelectedText()) { - if (d->lastSelectionStart != d->control->cursor()) { - d->lastSelectionStart = d->control->cursor(); - emit selectionStartChanged(); - } - if (d->lastSelectionEnd != d->control->cursor()) { - d->lastSelectionEnd = d->control->cursor(); - emit selectionEndChanged(); - } - } -} - -void QSGTextInput::updateCursorRectangle() -{ - Q_D(QSGTextInput); - d->determineHorizontalAlignment(); - d->updateHorizontalScroll(); - updateRect();//TODO: Only update rect between pos's - updateMicroFocus(); - emit cursorRectangleChanged(); - if (d->cursorItem) - d->cursorItem->setX(d->control->cursorToX() - d->hscroll); -} - -void QSGTextInput::selectionChanged() -{ - Q_D(QSGTextInput); - updateRect();//TODO: Only update rect in selection - emit selectedTextChanged(); - - if (d->lastSelectionStart != d->control->selectionStart()) { - d->lastSelectionStart = d->control->selectionStart(); - if (d->lastSelectionStart == -1) - d->lastSelectionStart = d->control->cursor(); - emit selectionStartChanged(); - } - if (d->lastSelectionEnd != d->control->selectionEnd()) { - d->lastSelectionEnd = d->control->selectionEnd(); - if (d->lastSelectionEnd == -1) - d->lastSelectionEnd = d->control->cursor(); - emit selectionEndChanged(); - } -} - -void QSGTextInput::q_textChanged() -{ - Q_D(QSGTextInput); - emit textChanged(); - emit displayTextChanged(); - updateSize(); - d->determineHorizontalAlignment(); - d->updateHorizontalScroll(); - updateMicroFocus(); - if (hasAcceptableInput() != d->oldValidity) { - d->oldValidity = hasAcceptableInput(); - emit acceptableInputChanged(); - } -} - -void QSGTextInputPrivate::showCursor() -{ - if (textNode != 0 && textNode->cursorNode() != 0) - textNode->cursorNode()->setColor(color); -} - -void QSGTextInputPrivate::hideCursor() -{ - if (textNode != 0 && textNode->cursorNode() != 0) - textNode->cursorNode()->setColor(QColor(0, 0, 0, 0)); -} - -void QSGTextInput::updateRect(const QRect &r) -{ - Q_D(QSGTextInput); - if (!isComponentComplete()) - return; - - if (r.isEmpty()) { - d->textLayoutDirty = true; - } - - update(); -} - -QRectF QSGTextInput::boundingRect() const -{ - Q_D(const QSGTextInput); - QRectF r = QSGImplicitSizeItem::boundingRect(); - - int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth(); - - // Could include font max left/right bearings to either side of rectangle. - - r.setRight(r.right() + cursorWidth); - return r; -} - -void QSGTextInput::updateSize(bool needsRedraw) -{ - Q_D(QSGTextInput); - int w = width(); - int h = height(); - setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. - setImplicitWidth(d->calculateTextWidth()); - if (w==width() && h==height() && needsRedraw) - update(); -} - -void QSGTextInput::q_canPasteChanged() -{ - Q_D(QSGTextInput); - bool old = d->canPaste; -#ifndef QT_NO_CLIPBOARD - d->canPaste = !d->control->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0; -#endif - if (d->canPaste != old) - emit canPasteChanged(); -} - -QT_END_NAMESPACE - diff --git a/src/declarative/items/qsgtextinput_p.h b/src/declarative/items/qsgtextinput_p.h deleted file mode 100644 index 6addc94bbb..0000000000 --- a/src/declarative/items/qsgtextinput_p.h +++ /dev/null @@ -1,303 +0,0 @@ -// Commit: 2f173e4945dd8414636c1061acfaf9c2d8b718d8 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTEXTINPUT_P_H -#define QSGTEXTINPUT_P_H - -#include "qsgimplicitsizeitem_p.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGTextInputPrivate; -class QValidator; -class Q_AUTOTEST_EXPORT QSGTextInput : public QSGImplicitSizeItem -{ - Q_OBJECT - Q_ENUMS(HAlignment) - Q_ENUMS(EchoMode) - Q_ENUMS(SelectionMode) - - Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged) - Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged) - Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) - Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged) - Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged) - - Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged) - Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged) - Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) - Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) - Q_PROPERTY(QDeclarativeComponent *cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) - Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) - Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) - Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged) - - Q_PROPERTY(int maximumLength READ maxLength WRITE setMaxLength NOTIFY maximumLengthChanged) -#ifndef QT_NO_VALIDATOR - Q_PROPERTY(QValidator* validator READ validator WRITE setValidator NOTIFY validatorChanged) -#endif - Q_PROPERTY(QString inputMask READ inputMask WRITE setInputMask NOTIFY inputMaskChanged) - Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ imHints WRITE setIMHints) - - Q_PROPERTY(bool acceptableInput READ hasAcceptableInput NOTIFY acceptableInputChanged) - Q_PROPERTY(EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged) - Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged) - Q_PROPERTY(QString passwordCharacter READ passwordCharacter WRITE setPasswordCharacter NOTIFY passwordCharacterChanged) - Q_PROPERTY(QString displayText READ displayText NOTIFY displayTextChanged) - Q_PROPERTY(bool autoScroll READ autoScroll WRITE setAutoScroll NOTIFY autoScrollChanged) - Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) - Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged) - Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged) - Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged) - -public: - QSGTextInput(QSGItem * parent=0); - ~QSGTextInput(); - - enum EchoMode {//To match QLineEdit::EchoMode - Normal, - NoEcho, - Password, - PasswordEchoOnEdit - }; - - enum HAlignment { - AlignLeft = Qt::AlignLeft, - AlignRight = Qt::AlignRight, - AlignHCenter = Qt::AlignHCenter - }; - - enum SelectionMode { - SelectCharacters, - SelectWords - }; - - enum CursorPosition { - CursorBetweenCharacters, - CursorOnCharacter - }; - - //Auxilliary functions needed to control the TextInput from QML - Q_INVOKABLE int positionAt(int x) const; - Q_INVOKABLE int positionAt(int x, CursorPosition position) const; - Q_INVOKABLE QRectF positionToRectangle(int pos) const; - Q_INVOKABLE void moveCursorSelection(int pos); - Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode); - - Q_INVOKABLE void openSoftwareInputPanel(); - Q_INVOKABLE void closeSoftwareInputPanel(); - - QString text() const; - void setText(const QString &); - - QFont font() const; - void setFont(const QFont &font); - - QColor color() const; - void setColor(const QColor &c); - - QColor selectionColor() const; - void setSelectionColor(const QColor &c); - - QColor selectedTextColor() const; - void setSelectedTextColor(const QColor &c); - - HAlignment hAlign() const; - void setHAlign(HAlignment align); - void resetHAlign(); - HAlignment effectiveHAlign() const; - - bool isReadOnly() const; - void setReadOnly(bool); - - bool isCursorVisible() const; - void setCursorVisible(bool on); - - int cursorPosition() const; - void setCursorPosition(int cp); - - QRect cursorRectangle() const; - - int selectionStart() const; - int selectionEnd() const; - - QString selectedText() const; - - int maxLength() const; - void setMaxLength(int ml); - -#ifndef QT_NO_VALIDATOR - QValidator * validator() const; - void setValidator(QValidator* v); -#endif - QString inputMask() const; - void setInputMask(const QString &im); - - EchoMode echoMode() const; - void setEchoMode(EchoMode echo); - - QString passwordCharacter() const; - void setPasswordCharacter(const QString &str); - - QString displayText() const; - - QDeclarativeComponent* cursorDelegate() const; - void setCursorDelegate(QDeclarativeComponent*); - - bool focusOnPress() const; - void setFocusOnPress(bool); - - bool autoScroll() const; - void setAutoScroll(bool); - - bool selectByMouse() const; - void setSelectByMouse(bool); - - SelectionMode mouseSelectionMode() const; - void setMouseSelectionMode(SelectionMode mode); - - bool hasAcceptableInput() const; - - QVariant inputMethodQuery(Qt::InputMethodQuery property) const; - - QRectF boundingRect() const; - bool canPaste() const; - - bool isInputMethodComposing() const; - - Qt::InputMethodHints imHints() const; - void setIMHints(Qt::InputMethodHints hints); - -Q_SIGNALS: - void textChanged(); - void cursorPositionChanged(); - void cursorRectangleChanged(); - void selectionStartChanged(); - void selectionEndChanged(); - void selectedTextChanged(); - void accepted(); - void acceptableInputChanged(); - void colorChanged(const QColor &color); - void selectionColorChanged(const QColor &color); - void selectedTextColorChanged(const QColor &color); - void fontChanged(const QFont &font); - void horizontalAlignmentChanged(HAlignment alignment); - void readOnlyChanged(bool isReadOnly); - void cursorVisibleChanged(bool isCursorVisible); - void cursorDelegateChanged(); - void maximumLengthChanged(int maximumLength); - void validatorChanged(); - void inputMaskChanged(const QString &inputMask); - void echoModeChanged(EchoMode echoMode); - void passwordCharacterChanged(); - void displayTextChanged(); - void activeFocusOnPressChanged(bool activeFocusOnPress); - void autoScrollChanged(bool autoScroll); - void selectByMouseChanged(bool selectByMouse); - void mouseSelectionModeChanged(SelectionMode mode); - void canPasteChanged(); - void inputMethodComposingChanged(); - void effectiveHorizontalAlignmentChanged(); - -protected: - virtual void geometryChanged(const QRectF &newGeometry, - const QRectF &oldGeometry); - - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void mouseDoubleClickEvent(QMouseEvent *event); - bool sceneEvent(QEvent *event); - void keyPressEvent(QKeyEvent* ev); - void inputMethodEvent(QInputMethodEvent *); - void mouseUngrabEvent(); - bool event(QEvent *e); - void focusInEvent(QFocusEvent *event); - virtual void itemChange(ItemChange, const ItemChangeData &); - QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data); - -public Q_SLOTS: - void selectAll(); - void selectWord(); - void select(int start, int end); - void deselect(); - bool isRightToLeft(int start, int end); -#ifndef QT_NO_CLIPBOARD - void cut(); - void copy(); - void paste(); -#endif - -private Q_SLOTS: - void updateSize(bool needsRedraw = true); - void q_textChanged(); - void selectionChanged(); - void createCursor(); - void cursorPosChanged(); - void updateCursorRectangle(); - void updateRect(const QRect &r = QRect()); - void q_canPasteChanged(); - -private: - Q_DECLARE_PRIVATE(QSGTextInput) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGTextInput) -#ifndef QT_NO_VALIDATOR -QML_DECLARE_TYPE(QValidator) -QML_DECLARE_TYPE(QIntValidator) -QML_DECLARE_TYPE(QDoubleValidator) -QML_DECLARE_TYPE(QRegExpValidator) -#endif - -QT_END_HEADER - -#endif // QSGTEXTINPUT_P_H diff --git a/src/declarative/items/qsgtextinput_p_p.h b/src/declarative/items/qsgtextinput_p_p.h deleted file mode 100644 index ed2395bb0e..0000000000 --- a/src/declarative/items/qsgtextinput_p_p.h +++ /dev/null @@ -1,172 +0,0 @@ -// Commit: 47712d1f330e4b22ce6dd30e7557288ef7f7fca0 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTEXTINPUT_P_P_H -#define QSGTEXTINPUT_P_P_H - -#include "qsgtextinput_p.h" -#include "qsgtext_p.h" -#include "qsgimplicitsizeitem_p_p.h" - -#include - -#include -#include -#include -#include -#include - - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. - -QT_BEGIN_NAMESPACE - -class QSGTextNode; - -class Q_AUTOTEST_EXPORT QSGTextInputPrivate : public QSGImplicitSizeItemPrivate -{ - Q_DECLARE_PUBLIC(QSGTextInput) -public: - QSGTextInputPrivate() - : control(new QLineControl(QString())) - , color((QRgb)0) - , style(QSGText::Normal) - , styleColor((QRgb)0) - , hAlign(QSGTextInput::AlignLeft) - , mouseSelectionMode(QSGTextInput::SelectCharacters) - , inputMethodHints(Qt::ImhNone) - , textNode(0) - , hscroll(0) - , oldScroll(0) - , oldValidity(false) - , focused(false) - , focusOnPress(true) - , cursorVisible(false) - , autoScroll(true) - , selectByMouse(false) - , canPaste(false) - , hAlignImplicit(true) - , selectPressed(false) - , textLayoutDirty(true) - { - } - - ~QSGTextInputPrivate() - { - } - - int xToPos(int x, QTextLine::CursorPosition betweenOrOn = QTextLine::CursorBetweenCharacters) const - { - Q_Q(const QSGTextInput); - QRect cr = q->boundingRect().toRect(); - x-= cr.x() - hscroll; - return control->xToPos(x, betweenOrOn); - } - - void init(); - void startCreatingCursor(); - void updateHorizontalScroll(); - bool determineHorizontalAlignment(); - bool setHAlign(QSGTextInput::HAlignment, bool forceAlign = false); - void mirrorChange(); - int calculateTextWidth(); - bool sendMouseEventToInputContext(QMouseEvent *event); - void updateInputMethodHints(); - void hideCursor(); - void showCursor(); - - QLineControl* control; - - QFont font; - QFont sourceFont; - QColor color; - QColor selectionColor; - QColor selectedTextColor; - QSGText::TextStyle style; - QColor styleColor; - QSGTextInput::HAlignment hAlign; - QSGTextInput::SelectionMode mouseSelectionMode; - Qt::InputMethodHints inputMethodHints; - QPointer cursorComponent; - QPointer cursorItem; - QPointF pressPos; - QSGTextNode *textNode; - QElapsedTimer tripleClickTimer; - QPoint tripleClickStartPoint; - - int lastSelectionStart; - int lastSelectionEnd; - int oldHeight; - int oldWidth; - int hscroll; - int oldScroll; - - bool oldValidity:1; - bool focused:1; - bool focusOnPress:1; - bool cursorVisible:1; - bool autoScroll:1; - bool selectByMouse:1; - bool canPaste:1; - bool hAlignImplicit:1; - bool selectPressed:1; - bool textLayoutDirty:1; - - static inline QSGTextInputPrivate *get(QSGTextInput *t) { - return t->d_func(); - } - bool hasPendingTripleClick() const { - return !tripleClickTimer.hasExpired(qApp->styleHints()->mouseDoubleClickInterval()); - } -}; - -QT_END_NAMESPACE - -#endif // QSGTEXTINPUT_P_P_H diff --git a/src/declarative/items/qsgtextnode.cpp b/src/declarative/items/qsgtextnode.cpp deleted file mode 100644 index f712bd768e..0000000000 --- a/src/declarative/items/qsgtextnode.cpp +++ /dev/null @@ -1,1343 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgtextnode_p.h" -#include "qsgsimplerectnode.h" -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -/*! - Creates an empty QSGTextNode -*/ -QSGTextNode::QSGTextNode(QSGContext *context) - : m_context(context), m_cursorNode(0) -{ -#if defined(QML_RUNTIME_TESTING) - description = QLatin1String("text"); -#endif -} - -QSGTextNode::~QSGTextNode() -{ - qDeleteAll(m_textures); -} - -#if 0 -void QSGTextNode::setColor(const QColor &color) -{ - if (m_usePixmapCache) { - setUpdateFlag(UpdateNodes); - } else { - for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) { - if (childNode->subType() == GlyphNodeSubType) { - QSGGlyphNode *glyphNode = static_cast(childNode); - if (glyphNode->color() == m_color) - glyphNode->setColor(color); - } else if (childNode->subType() == SolidRectNodeSubType) { - QSGSimpleRectNode *solidRectNode = static_cast(childNode); - if (solidRectNode->color() == m_color) - solidRectNode->setColor(color); - } - } - } - m_color = color; -} - -void QSGTextNode::setStyleColor(const QColor &styleColor) -{ - if (m_textStyle != QSGTextNode::NormalTextStyle) { - if (m_usePixmapCache) { - setUpdateFlag(UpdateNodes); - } else { - for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) { - if (childNode->subType() == GlyphNodeSubType) { - QSGGlyphNode *glyphNode = static_cast(childNode); - if (glyphNode->color() == m_styleColor) - glyphNode->setColor(styleColor); - } else if (childNode->subType() == SolidRectNodeSubType) { - QSGSimpleRectNode *solidRectNode = static_cast(childNode); - if (solidRectNode->color() == m_styleColor) - solidRectNode->setColor(styleColor); - } - } - } - } - m_styleColor = styleColor; -} -#endif - -QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, - QSGText::TextStyle style, const QColor &styleColor, - QSGNode *parentNode) -{ - QSGGlyphNode *node = m_context->createGlyphNode(); - node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs); - node->setStyle(style); - node->setStyleColor(styleColor); - node->setColor(color); - node->update(); - - /* We flag the geometry as static, but we never call markVertexDataDirty - or markIndexDataDirty on them. This is because all text nodes are - discarded when a change occurs. If we start appending/removing from - existing geometry, then we also need to start marking the geometry as - dirty. - */ - node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern); - node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern); - - if (parentNode == 0) - parentNode = this; - parentNode->appendChildNode(node); - - return node; -} - -void QSGTextNode::setCursor(const QRectF &rect, const QColor &color) -{ - if (m_cursorNode != 0) - delete m_cursorNode; - - m_cursorNode = new QSGSimpleRectNode(rect, color); - appendChildNode(m_cursorNode); -} - -namespace { - - struct BinaryTreeNode { - enum SelectionState { - Unselected, - Selected - }; - - BinaryTreeNode() - : selectionState(Unselected) - , clipNode(0) - , decorations(QSGTextNode::NoDecoration) - , ascent(0.0) - , leftChildIndex(-1) - , rightChildIndex(-1) - { - - } - - BinaryTreeNode(const QRectF &brect, const QImage &i, SelectionState selState, qreal a) - : boundingRect(brect) - , selectionState(selState) - , clipNode(0) - , decorations(QSGTextNode::NoDecoration) - , image(i) - , ascent(a) - , leftChildIndex(-1) - , rightChildIndex(-1) - { - } - - BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect, - const QSGTextNode::Decorations &decs, const QColor &c, const QColor &bc, - const QPointF &pos, qreal a) - : glyphRun(g) - , boundingRect(brect) - , selectionState(selState) - , clipNode(0) - , decorations(decs) - , color(c) - , backgroundColor(bc) - , position(pos) - , ascent(a) - , leftChildIndex(-1) - , rightChildIndex(-1) - { - } - - QGlyphRun glyphRun; - QRectF boundingRect; - SelectionState selectionState; - QSGClipNode *clipNode; - QSGTextNode::Decorations decorations; - QColor color; - QColor backgroundColor; - QPointF position; - QImage image; - qreal ascent; - - int leftChildIndex; - int rightChildIndex; - - static void insert(QVarLengthArray *binaryTree, - const QRectF &rect, - const QImage &image, - qreal ascent, - SelectionState selectionState) - { - insert(binaryTree, BinaryTreeNode(rect, image, selectionState, ascent)); - } - - static void insert(QVarLengthArray *binaryTree, - const QGlyphRun &glyphRun, - SelectionState selectionState, - const QColor &textColor, - const QColor &backgroundColor, - const QPointF &position) - { - QRectF searchRect = glyphRun.boundingRect(); - searchRect.translate(position); - - if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height())) - return; - - QSGTextNode::Decorations decorations = QSGTextNode::NoDecoration; - decorations |= (glyphRun.underline() ? QSGTextNode::Underline : QSGTextNode::NoDecoration); - decorations |= (glyphRun.overline() ? QSGTextNode::Overline : QSGTextNode::NoDecoration); - decorations |= (glyphRun.strikeOut() ? QSGTextNode::StrikeOut : QSGTextNode::NoDecoration); - decorations |= (backgroundColor.isValid() ? QSGTextNode::Background : QSGTextNode::NoDecoration); - - qreal ascent = glyphRun.rawFont().ascent(); - insert(binaryTree, BinaryTreeNode(glyphRun, selectionState, searchRect, decorations, - textColor, backgroundColor, position, ascent)); - } - - static void insert(QVarLengthArray *binaryTree, - const BinaryTreeNode &binaryTreeNode) - { - int newIndex = binaryTree->size(); - binaryTree->append(binaryTreeNode); - if (newIndex == 0) - return; - - int searchIndex = 0; - forever { - BinaryTreeNode *node = binaryTree->data() + searchIndex; - if (binaryTreeNode.boundingRect.left() < node->boundingRect.left()) { - if (node->leftChildIndex < 0) { - node->leftChildIndex = newIndex; - break; - } else { - searchIndex = node->leftChildIndex; - } - } else { - if (node->rightChildIndex < 0) { - node->rightChildIndex = newIndex; - break; - } else { - searchIndex = node->rightChildIndex; - } - } - } - } - - static void inOrder(const QVarLengthArray &binaryTree, - QVarLengthArray *sortedIndexes, - int currentIndex = 0) - { - Q_ASSERT(currentIndex < binaryTree.size()); - - const BinaryTreeNode *node = binaryTree.data() + currentIndex; - if (node->leftChildIndex >= 0) - inOrder(binaryTree, sortedIndexes, node->leftChildIndex); - - sortedIndexes->append(currentIndex); - - if (node->rightChildIndex >= 0) - inOrder(binaryTree, sortedIndexes, node->rightChildIndex); - } - }; - - // Engine that takes glyph runs as input, and produces a set of glyph nodes, clip nodes, - // and rectangle nodes to represent the text, decorations and selection. Will try to minimize - // number of nodes, and join decorations in neighbouring items - class SelectionEngine - { - public: - SelectionEngine() : m_hasSelection(false) {} - - QTextLine currentLine() const { return m_currentLine; } - - void setCurrentLine(const QTextLine ¤tLine) - { - if (m_currentLine.isValid()) - processCurrentLine(); - - m_currentLine = currentLine; - } - - void addBorder(const QRectF &rect, qreal border, QTextFrameFormat::BorderStyle borderStyle, - const QBrush &borderBrush); - void addFrameDecorations(QTextDocument *document, QTextFrame *frame); - void addImage(const QRectF &rect, const QImage &image, qreal ascent, - BinaryTreeNode::SelectionState selectionState, - QTextFrameFormat::Position layoutPosition); - void addTextObject(const QPointF &position, const QTextCharFormat &format, - BinaryTreeNode::SelectionState selectionState, - QTextDocument *textDocument, int pos, - QTextFrameFormat::Position layoutPosition = QTextFrameFormat::InFlow); - void addSelectedGlyphs(const QGlyphRun &glyphRun); - void addUnselectedGlyphs(const QGlyphRun &glyphRun); - void addGlyphsInRange(int rangeStart, int rangeEnd, - const QColor &color, const QColor &backgroundColor, - int selectionStart, int selectionEnd); - void addGlyphsForRanges(const QVarLengthArray &ranges, - int start, int end, - int selectionStart, int selectionEnd); - - void addToSceneGraph(QSGTextNode *parent, - QSGText::TextStyle style = QSGText::Normal, - const QColor &styleColor = QColor()); - - void setSelectionColor(const QColor &selectionColor) - { - m_selectionColor = selectionColor; - } - - void setSelectedTextColor(const QColor &selectedTextColor) - { - m_selectedTextColor = selectedTextColor; - } - - void setTextColor(const QColor &textColor) - { - m_textColor = textColor; - } - - void setPosition(const QPointF &position) - { - m_position = position; - } - - private: - struct TextDecoration - { - TextDecoration() : selectionState(BinaryTreeNode::Unselected) {} - TextDecoration(const BinaryTreeNode::SelectionState &s, - const QRectF &r, - const QColor &c) - : selectionState(s) - , rect(r) - , color(c) - { - } - - BinaryTreeNode::SelectionState selectionState; - QRectF rect; - QColor color; - }; - - void processCurrentLine(); - void addTextDecorations(const QVarLengthArray &textDecorations, - qreal offset, qreal thickness); - - QColor m_selectionColor; - QColor m_textColor; - QColor m_backgroundColor; - QColor m_selectedTextColor; - QPointF m_position; - - QTextLine m_currentLine; - bool m_hasSelection; - - QList > m_backgrounds; - QList m_selectionRects; - QVarLengthArray m_currentLineTree; - - QList m_lines; - QVector m_processedNodes; - - QList > m_images; - }; - - void SelectionEngine::addTextDecorations(const QVarLengthArray &textDecorations, - qreal offset, qreal thickness) - { - for (int i=0; i sortedIndexes; // Indexes in tree sorted by x position - BinaryTreeNode::inOrder(m_currentLineTree, &sortedIndexes); - - Q_ASSERT(sortedIndexes.size() == m_currentLineTree.size()); - - BinaryTreeNode::SelectionState currentSelectionState = BinaryTreeNode::Unselected; - QRectF currentRect; - - QSGTextNode::Decorations currentDecorations = QSGTextNode::NoDecoration; - qreal underlineOffset = 0.0; - qreal underlineThickness = 0.0; - - qreal overlineOffset = 0.0; - qreal overlineThickness = 0.0; - - qreal strikeOutOffset = 0.0; - qreal strikeOutThickness = 0.0; - - QRectF decorationRect = currentRect; - - QColor lastColor; - QColor lastBackgroundColor; - - QVarLengthArray pendingUnderlines; - QVarLengthArray pendingOverlines; - QVarLengthArray pendingStrikeOuts; - if (!sortedIndexes.isEmpty()) { - QSGClipNode *currentClipNode = m_hasSelection ? new QSGClipNode : 0; - bool currentClipNodeUsed = false; - for (int i=0; i<=sortedIndexes.size(); ++i) { - BinaryTreeNode *node = 0; - if (i < sortedIndexes.size()) { - int sortedIndex = sortedIndexes.at(i); - Q_ASSERT(sortedIndex < m_currentLineTree.size()); - - node = m_currentLineTree.data() + sortedIndex; - } - - if (i == 0) - currentSelectionState = node->selectionState; - - // Update decorations - if (currentDecorations != QSGTextNode::NoDecoration) { - decorationRect.setY(m_position.y() + m_currentLine.y()); - decorationRect.setHeight(m_currentLine.height()); - - if (node != 0) - decorationRect.setRight(node->boundingRect.left()); - - TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor); - if (currentDecorations & QSGTextNode::Underline) - pendingUnderlines.append(textDecoration); - - if (currentDecorations & QSGTextNode::Overline) - pendingOverlines.append(textDecoration); - - if (currentDecorations & QSGTextNode::StrikeOut) - pendingStrikeOuts.append(textDecoration); - - if (currentDecorations & QSGTextNode::Background) - m_backgrounds.append(qMakePair(decorationRect, lastBackgroundColor)); - } - - // If we've reached an unselected node from a selected node, we add the - // selection rect to the graph, and we add decoration every time the - // selection state changes, because that means the text color changes - if (node == 0 || node->selectionState != currentSelectionState) { - if (node != 0) - currentRect.setRight(node->boundingRect.left()); - currentRect.setY(m_position.y() + m_currentLine.y()); - currentRect.setHeight(m_currentLine.height()); - - // Draw selection all the way up to the left edge of the unselected item - if (currentSelectionState == BinaryTreeNode::Selected) - m_selectionRects.append(currentRect); - - if (currentClipNode != 0) { - if (!currentClipNodeUsed) { - delete currentClipNode; - } else { - currentClipNode->setIsRectangular(true); - currentClipNode->setClipRect(currentRect); - } - } - - if (node != 0 && m_hasSelection) - currentClipNode = new QSGClipNode; - else - currentClipNode = 0; - currentClipNodeUsed = false; - - if (node != 0) { - currentSelectionState = node->selectionState; - currentRect = node->boundingRect; - - // Make sure currentRect is valid, otherwise the unite won't work - if (currentRect.isNull()) - currentRect.setSize(QSizeF(1, 1)); - } - } else { - if (currentRect.isNull()) - currentRect = node->boundingRect; - else - currentRect = currentRect.united(node->boundingRect); - } - - if (node != 0) { - node->clipNode = currentClipNode; - currentClipNodeUsed = true; - - decorationRect = node->boundingRect; - - // If previous item(s) had underline and current does not, then we add the - // pending lines to the lists and likewise for overlines and strikeouts - if (!pendingUnderlines.isEmpty() - && !(node->decorations & QSGTextNode::Underline)) { - addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); - - pendingUnderlines.clear(); - - underlineOffset = 0.0; - underlineThickness = 0.0; - } - - // ### Add pending when overlineOffset/thickness changes to minimize number of - // nodes - if (!pendingOverlines.isEmpty()) { - addTextDecorations(pendingOverlines, overlineOffset, overlineThickness); - - pendingOverlines.clear(); - - overlineOffset = 0.0; - overlineThickness = 0.0; - } - - // ### Add pending when overlineOffset/thickness changes to minimize number of - // nodes - if (!pendingStrikeOuts.isEmpty()) { - addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness); - - pendingStrikeOuts.clear(); - - strikeOutOffset = 0.0; - strikeOutThickness = 0.0; - } - - // Merge current values with previous. Prefer greatest thickness - QRawFont rawFont = node->glyphRun.rawFont(); - if (node->decorations & QSGTextNode::Underline) { - if (rawFont.lineThickness() > underlineThickness) { - underlineThickness = rawFont.lineThickness(); - underlineOffset = rawFont.underlinePosition(); - } - } - - if (node->decorations & QSGTextNode::Overline) { - overlineOffset = -rawFont.ascent(); - overlineThickness = rawFont.lineThickness(); - } - - if (node->decorations & QSGTextNode::StrikeOut) { - strikeOutThickness = rawFont.lineThickness(); - strikeOutOffset = rawFont.ascent() / -3.0; - } - - currentDecorations = node->decorations; - lastColor = node->color; - lastBackgroundColor = node->backgroundColor; - m_processedNodes.append(*node); - } - } - - if (!pendingUnderlines.isEmpty()) - addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); - - if (!pendingOverlines.isEmpty()) - addTextDecorations(pendingOverlines, overlineOffset, overlineThickness); - - if (!pendingStrikeOuts.isEmpty()) - addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness); - } - - m_currentLineTree.clear(); - m_currentLine = QTextLine(); - m_hasSelection = false; - } - - void SelectionEngine::addImage(const QRectF &rect, const QImage &image, qreal ascent, - BinaryTreeNode::SelectionState selectionState, - QTextFrameFormat::Position layoutPosition) - { - QRectF searchRect = rect; - if (layoutPosition == QTextFrameFormat::InFlow) { - if (m_currentLineTree.isEmpty()) { - searchRect.moveTopLeft(m_position); - } else { - const BinaryTreeNode *lastNode = m_currentLineTree.data() + m_currentLineTree.size() - 1; - if (lastNode->glyphRun.isRightToLeft()) { - QPointF lastPos = lastNode->boundingRect.topLeft(); - searchRect.moveTopRight(lastPos - QPointF(0, ascent)); - } else { - QPointF lastPos = lastNode->boundingRect.topRight(); - searchRect.moveTopLeft(lastPos - QPointF(0, ascent)); - } - } - } - - BinaryTreeNode::insert(&m_currentLineTree, searchRect, image, ascent, selectionState); - } - - void SelectionEngine::addTextObject(const QPointF &position, const QTextCharFormat &format, - BinaryTreeNode::SelectionState selectionState, - QTextDocument *textDocument, int pos, - QTextFrameFormat::Position layoutPosition) - { - QTextObjectInterface *handler = textDocument->documentLayout()->handlerForObject(format.objectType()); - if (handler != 0) { - QImage image; - QSizeF size = handler->intrinsicSize(textDocument, pos, format); - - if (format.objectType() == QTextFormat::ImageObject) { - QTextImageFormat imageFormat = format.toImageFormat(); - QTextImageHandler *imageHandler = static_cast(handler); - image = imageHandler->image(textDocument, imageFormat); - } - - if (image.isNull()) { - image = QImage(size.toSize(), QImage::Format_ARGB32_Premultiplied); - image.fill(Qt::transparent); - { - QPainter painter(&image); - handler->drawObject(&painter, image.rect(), textDocument, pos, format); - } - } - - qreal ascent; - QFontMetrics m(format.font()); - switch (format.verticalAlignment()) - { - case QTextCharFormat::AlignMiddle: - ascent = size.height() / 2 - 1; - break; - case QTextCharFormat::AlignBaseline: - ascent = size.height() - m.descent() - 1; - break; - default: - ascent = size.height() - 1; - } - - addImage(QRectF(position, size), image, ascent, selectionState, layoutPosition); - } - } - - void SelectionEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun) - { - BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Unselected, - m_textColor, m_backgroundColor, m_position); - } - - void SelectionEngine::addSelectedGlyphs(const QGlyphRun &glyphRun) - { - int currentSize = m_currentLineTree.size(); - BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Selected, - m_textColor, m_backgroundColor, m_position); - m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize; - } - - void SelectionEngine::addGlyphsForRanges(const QVarLengthArray &ranges, - int start, int end, - int selectionStart, int selectionEnd) - { - int currentPosition = start; - int remainingLength = end - start; - for (int j=0; j= currentPosition - && range.start < currentPosition + remainingLength) { - - if (range.start > currentPosition) { - addGlyphsInRange(currentPosition, range.start - currentPosition, - QColor(), QColor(), selectionStart, selectionEnd); - } - - int rangeEnd = qMin(range.start + range.length, currentPosition + remainingLength); - QColor rangeColor = range.format.hasProperty(QTextFormat::ForegroundBrush) - ? range.format.foreground().color() - : QColor(); - QColor rangeBackgroundColor = range.format.hasProperty(QTextFormat::BackgroundBrush) - ? range.format.background().color() - : QColor(); - - addGlyphsInRange(range.start, rangeEnd - range.start, - rangeColor, rangeBackgroundColor, - selectionStart, selectionEnd); - - currentPosition = range.start + range.length; - remainingLength = end - currentPosition; - - } else if (range.start > currentPosition + remainingLength || remainingLength <= 0) { - break; - } - } - - if (remainingLength > 0) { - addGlyphsInRange(currentPosition, remainingLength, QColor(), QColor(), - selectionStart, selectionEnd); - } - - } - - void SelectionEngine::addGlyphsInRange(int rangeStart, int rangeLength, - const QColor &color, const QColor &backgroundColor, - int selectionStart, int selectionEnd) - { - QColor oldColor; - if (color.isValid()) { - oldColor = m_textColor; - m_textColor = color; - } - - QColor oldBackgroundColor = m_backgroundColor; - if (backgroundColor.isValid()) { - oldBackgroundColor = m_backgroundColor; - m_backgroundColor = backgroundColor; - } - - bool hasSelection = selectionEnd >= 0 - && selectionStart <= selectionEnd; - - QTextLine &line = m_currentLine; - int rangeEnd = rangeStart + rangeLength; - if (!hasSelection || (selectionStart > rangeEnd || selectionEnd < rangeStart)) { - QList glyphRuns = line.glyphRuns(rangeStart, rangeLength); - for (int j=0; j glyphRuns = line.glyphRuns(rangeStart, - qMin(selectionStart - rangeStart, - rangeLength)); - - for (int j=0; j selectionStart) { - int start = qMax(selectionStart, rangeStart); - int length = qMin(selectionEnd - start + 1, rangeEnd - start); - QList glyphRuns = line.glyphRuns(start, length); - - for (int j=0; j= rangeStart && selectionEnd < rangeEnd) { - QList glyphRuns = line.glyphRuns(selectionEnd + 1, rangeEnd - selectionEnd - 1); - for (int j=0; j(document->documentLayout()); - QTextFrameFormat frameFormat = frame->format().toFrameFormat(); - - QTextTable *table = qobject_cast(frame); - QRectF boundingRect = table == 0 - ? documentLayout->frameBoundingRect(frame) - : documentLayout->tableBoundingRect(table); - - QBrush bg = frame->frameFormat().background(); - if (bg.style() != Qt::NoBrush) - m_backgrounds.append(qMakePair(boundingRect, bg.color())); - - if (!frameFormat.hasProperty(QTextFormat::FrameBorder)) - return; - - qreal borderWidth = frameFormat.border(); - if (qFuzzyIsNull(borderWidth)) - return; - - QBrush borderBrush = frameFormat.borderBrush(); - QTextFrameFormat::BorderStyle borderStyle = frameFormat.borderStyle(); - if (borderStyle == QTextFrameFormat::BorderStyle_None) - return; - - addBorder(boundingRect.adjusted(frameFormat.leftMargin(), frameFormat.topMargin(), - -frameFormat.rightMargin(), -frameFormat.bottomMargin()), - borderWidth, borderStyle, borderBrush); - if (table != 0) { - int rows = table->rows(); - int columns = table->columns(); - - for (int row=0; rowcellAt(row, column); - - QRectF cellRect = documentLayout->tableCellBoundingRect(table, cell); - addBorder(cellRect.adjusted(-borderWidth, -borderWidth, 0, 0), borderWidth, - borderStyle, borderBrush); - } - } - } - } - - void SelectionEngine::addToSceneGraph(QSGTextNode *parentNode, - QSGText::TextStyle style, - const QColor &styleColor) - { - if (m_currentLine.isValid()) - processCurrentLine(); - - - for (int i=0; iappendChildNode(new QSGSimpleRectNode(rect, color)); - } - - // First, prepend all selection rectangles to the tree - for (int i=0; iappendChildNode(new QSGSimpleRectNode(rect, m_selectionColor)); - } - - // Finally, add decorations for each node to the tree. - for (int i=0; iappendChildNode(new QSGSimpleRectNode(textDecoration.rect, color)); - } - - // Then, go through all the nodes for all lines and combine all QGlyphRuns with a common - // font, selection state and clip node. - typedef QPair > > KeyType; - QHash map; - for (int i=0; iimage.isNull()) { - QGlyphRun glyphRun = node->glyphRun; - QRawFont rawFont = glyphRun.rawFont(); - QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont); - - QFontEngine *fontEngine = rawFontD->fontEngine; - - KeyType key(qMakePair(fontEngine, - qMakePair(node->clipNode, - qMakePair(node->color.rgba(), int(node->selectionState))))); - - BinaryTreeNode *otherNode = map.value(key, 0); - if (otherNode != 0) { - QGlyphRun &otherGlyphRun = otherNode->glyphRun; - - QVector otherGlyphIndexes = otherGlyphRun.glyphIndexes(); - QVector otherGlyphPositions = otherGlyphRun.positions(); - - otherGlyphIndexes += glyphRun.glyphIndexes(); - - QVector glyphPositions = glyphRun.positions(); - for (int j=0; jposition - otherNode->position); - } - - otherGlyphRun.setGlyphIndexes(otherGlyphIndexes); - otherGlyphRun.setPositions(otherGlyphPositions); - - } else { - map.insert(key, node); - } - } else { - parentNode->addImage(node->boundingRect, node->image); - if (node->selectionState == BinaryTreeNode::Selected) { - QColor color = m_selectionColor; - color.setAlpha(128); - parentNode->appendChildNode(new QSGSimpleRectNode(node->boundingRect, color)); - } - } - } - - // ...and add clip nodes and glyphs to tree. - QHash::const_iterator it = map.constBegin(); - while (it != map.constEnd()) { - - BinaryTreeNode *node = it.value(); - - QSGClipNode *clipNode = node->clipNode; - if (clipNode != 0 && clipNode->parent() == 0 ) - parentNode->appendChildNode(clipNode); - - QColor color = node->selectionState == BinaryTreeNode::Selected - ? m_selectedTextColor - : node->color; - - parentNode->addGlyphs(node->position, node->glyphRun, color, style, styleColor, clipNode); - - ++it; - } - } -} - -void QSGTextNode::mergeFormats(QTextLayout *textLayout, - QVarLengthArray *mergedFormats) -{ - Q_ASSERT(mergedFormats != 0); - if (textLayout == 0) - return; - - QList additionalFormats = textLayout->additionalFormats(); - for (int i=0; iisEmpty()) { - QTextLayout::FormatRange *lastFormat = mergedFormats->data() + mergedFormats->size() - 1; - - if (additionalFormat.start < lastFormat->start + lastFormat->length) { - QTextLayout::FormatRange *mergedRange = 0; - - int length = additionalFormat.length; - if (additionalFormat.start > lastFormat->start) { - lastFormat->length = additionalFormat.start - lastFormat->start; - length -= lastFormat->length; - - mergedFormats->append(QTextLayout::FormatRange()); - mergedRange = mergedFormats->data() + mergedFormats->size() - 1; - lastFormat = mergedFormats->data() + mergedFormats->size() - 2; - } else { - mergedRange = lastFormat; - } - - mergedRange->format = lastFormat->format; - mergedRange->format.merge(additionalFormat.format); - mergedRange->start = additionalFormat.start; - - int end = qMin(additionalFormat.start + additionalFormat.length, - lastFormat->start + lastFormat->length); - - mergedRange->length = end - mergedRange->start; - length -= mergedRange->length; - - additionalFormat.start = end; - additionalFormat.length = length; - } - } - - if (additionalFormat.length > 0) - mergedFormats->append(additionalFormat); - } - } - -} - -namespace { - - class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout - { - public: - inline QTextCharFormat formatAccessor(int pos) - { - return format(pos); - } - }; - -} - -void QSGTextNode::addImage(const QRectF &rect, const QImage &image) -{ - QSGImageNode *node = m_context->createImageNode(); - QSGTexture *texture = m_context->createTexture(image); - m_textures.append(texture); - node->setTargetRect(rect); - node->setTexture(texture); - appendChildNode(node); - node->update(); -} - -void QSGTextNode::addTextDocument(const QPointF &, QTextDocument *textDocument, - const QColor &textColor, - QSGText::TextStyle style, const QColor &styleColor, - const QColor &selectionColor, const QColor &selectedTextColor, - int selectionStart, int selectionEnd) -{ - SelectionEngine engine; - engine.setTextColor(textColor); - engine.setSelectedTextColor(selectedTextColor); - engine.setSelectionColor(selectionColor); - - QList frames; - frames.append(textDocument->rootFrame()); - while (!frames.isEmpty()) { - QTextFrame *textFrame = frames.takeFirst(); - frames.append(textFrame->childFrames()); - - engine.addFrameDecorations(textDocument, textFrame); - - if (textFrame->firstPosition() > textFrame->lastPosition() - && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) { - const int pos = textFrame->firstPosition() - 1; - ProtectedLayoutAccessor *a = static_cast(textDocument->documentLayout()); - QTextCharFormat format = a->formatAccessor(pos); - QRectF rect = a->frameBoundingRect(textFrame); - - QTextBlock block = textFrame->firstCursorPosition().block(); - engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); - engine.addTextObject(rect.topLeft(), format, BinaryTreeNode::Unselected, textDocument, - pos, textFrame->frameFormat().position()); - } else { - QTextFrame::iterator it = textFrame->begin(); - - while (!it.atEnd()) { - Q_ASSERT(!engine.currentLine().isValid()); - - QTextBlock block = it.currentBlock(); - int preeditLength = block.isValid() ? block.layout()->preeditAreaText().length() : 0; - int preeditPosition = block.isValid() ? block.layout()->preeditAreaPosition() : -1; - - QVarLengthArray colorChanges; - mergeFormats(block.layout(), &colorChanges); - - QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft(); - if (QTextList *textList = block.textList()) { - QPointF pos = blockPosition; - QTextLayout *layout = block.layout(); - if (layout->lineCount() > 0) { - QTextLine firstLine = layout->lineAt(0); - Q_ASSERT(firstLine.isValid()); - - engine.setCurrentLine(firstLine); - - QRectF textRect = firstLine.naturalTextRect(); - pos += textRect.topLeft(); - if (block.textDirection() == Qt::RightToLeft) - pos.rx() += textRect.width(); - - const QTextCharFormat charFormat = block.charFormat(); - QFont font(charFormat.font()); - QFontMetricsF fontMetrics(font); - QTextListFormat listFormat = textList->format(); - - QString listItemBullet; - switch (listFormat.style()) { - case QTextListFormat::ListCircle: - listItemBullet = QChar(0x25E6); // White bullet - break; - case QTextListFormat::ListSquare: - listItemBullet = QChar(0x25AA); // Black small square - break; - case QTextListFormat::ListDecimal: - case QTextListFormat::ListLowerAlpha: - case QTextListFormat::ListUpperAlpha: - case QTextListFormat::ListLowerRoman: - case QTextListFormat::ListUpperRoman: - listItemBullet = textList->itemText(block); - break; - default: - listItemBullet = QChar(0x2022); // Black bullet - break; - }; - - QSizeF size(fontMetrics.width(listItemBullet), fontMetrics.height()); - qreal xoff = fontMetrics.width(QLatin1Char(' ')); - if (block.textDirection() == Qt::LeftToRight) - xoff = -xoff - size.width(); - engine.setPosition(pos + QPointF(xoff, 0)); - - QTextLayout layout; - layout.setFont(font); - layout.setText(listItemBullet); // Bullet - layout.beginLayout(); - QTextLine line = layout.createLine(); - line.setPosition(QPointF(0, 0)); - layout.endLayout(); - - QList glyphRuns = layout.glyphRuns(); - for (int i=0; i(textDocument->objectForFormat(charFormat)); - if (frame && frame->frameFormat().position() == QTextFrameFormat::InFlow) { - BinaryTreeNode::SelectionState selectionState = - (selectionStart < textPos + text.length() - && selectionEnd >= textPos) - ? BinaryTreeNode::Selected - : BinaryTreeNode::Unselected; - - engine.addTextObject(QPointF(), charFormat, selectionState, textDocument, textPos); - } - textPos += text.length(); - } else { - if (!textColor.isValid()) - engine.setTextColor(charFormat.foreground().color()); - - int fragmentEnd = textPos + fragment.length(); - if (preeditPosition >= 0 - && preeditPosition >= textPos - && preeditPosition < fragmentEnd) { - fragmentEnd += preeditLength; - } - - while (textPos < fragmentEnd) { - int blockRelativePosition = textPos - block.position(); - QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); - if (!engine.currentLine().isValid() - || line.lineNumber() != engine.currentLine().lineNumber()) { - engine.setCurrentLine(line); - } - - Q_ASSERT(line.textLength() > 0); - int lineEnd = line.textStart() + block.position() + line.textLength(); - - int len = qMin(lineEnd - textPos, fragmentEnd - textPos); - Q_ASSERT(len > 0); - - int currentStepEnd = textPos + len; - - engine.addGlyphsForRanges(colorChanges, - textPos - block.position(), - currentStepEnd - block.position(), - selectionStart - block.position(), - selectionEnd - block.position()); - - textPos = currentStepEnd; - } - } - - ++blockIterator; - } - - engine.setCurrentLine(QTextLine()); // Reset current line because the text layout changed - ++it; - } - } - } - - engine.addToSceneGraph(this, style, styleColor); -} - -void QSGTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color, - QSGText::TextStyle style, const QColor &styleColor, - const QColor &selectionColor, const QColor &selectedTextColor, - int selectionStart, int selectionEnd) -{ - SelectionEngine engine; - engine.setTextColor(color); - engine.setSelectedTextColor(selectedTextColor); - engine.setSelectionColor(selectionColor); - engine.setPosition(position); - - int preeditLength = textLayout->preeditAreaText().length(); - int preeditPosition = textLayout->preeditAreaPosition(); - - QVarLengthArray colorChanges; - mergeFormats(textLayout, &colorChanges); - - for (int i=0; ilineCount(); ++i) { - QTextLine line = textLayout->lineAt(i); - - int start = line.textStart(); - int length = line.textLength(); - int end = start + length; - - if (preeditPosition >= 0 - && preeditPosition >= start - && preeditPosition < end) { - end += preeditLength; - } - - engine.setCurrentLine(line); - engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); - } - - engine.addToSceneGraph(this, style, styleColor); -} - -void QSGTextNode::deleteContent() -{ - while (firstChild() != 0) - delete firstChild(); - m_cursorNode = 0; -} - -#if 0 -void QSGTextNode::updateNodes() -{ - return; - deleteContent(); - if (m_text.isEmpty()) - return; - - if (m_usePixmapCache) { - // ### gunnar: port properly -// QPixmap pixmap = generatedPixmap(); -// if (pixmap.isNull()) -// return; - -// QSGImageNode *pixmapNode = m_context->createImageNode(); -// pixmapNode->setRect(pixmap.rect()); -// pixmapNode->setSourceRect(pixmap.rect()); -// pixmapNode->setOpacity(m_opacity); -// pixmapNode->setClampToEdge(true); -// pixmapNode->setLinearFiltering(m_linearFiltering); - -// appendChildNode(pixmapNode); - } else { - if (m_text.isEmpty()) - return; - - // Implement styling by drawing text several times at slight shifts. shiftForStyle - // contains the sequence of shifted positions at which to draw the text. All except - // the last will be drawn with styleColor. - QList shiftForStyle; - switch (m_textStyle) { - case OutlineTextStyle: - // ### Should be made faster by implementing outline material - shiftForStyle << QPointF(-1, 0); - shiftForStyle << QPointF(0, -1); - shiftForStyle << QPointF(1, 0); - shiftForStyle << QPointF(0, 1); - break; - case SunkenTextStyle: - shiftForStyle << QPointF(0, -1); - break; - case RaisedTextStyle: - shiftForStyle << QPointF(0, 1); - break; - default: - break; - } - - shiftForStyle << QPointF(0, 0); // Regular position - while (!shiftForStyle.isEmpty()) { - QPointF shift = shiftForStyle.takeFirst(); - - // Use styleColor for all but last shift - if (m_richText) { - QColor overrideColor = shiftForStyle.isEmpty() ? QColor() : m_styleColor; - - QTextFrame *textFrame = m_textDocument->rootFrame(); - QPointF p = m_textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft(); - - QTextFrame::iterator it = textFrame->begin(); - while (!it.atEnd()) { - addTextBlock(shift + p, it.currentBlock(), overrideColor); - ++it; - } - } else { - addTextLayout(shift, m_textLayout, shiftForStyle.isEmpty() - ? m_color - : m_styleColor); - } - } - } -} -#endif - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgtextnode_p.h b/src/declarative/items/qsgtextnode_p.h deleted file mode 100644 index 7a9462c24e..0000000000 --- a/src/declarative/items/qsgtextnode_p.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTEXTNODE_P_H -#define QSGTEXTNODE_P_H - -#include -#include "qsgtext_p.h" -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QSGGlyphNode; -class QTextBlock; -class QColor; -class QTextDocument; -class QSGContext; -class QRawFont; -class QSGSimpleRectNode; -class QSGClipNode; -class QSGTexture; - -class QSGTextNode : public QSGTransformNode -{ -public: - enum Decoration { - NoDecoration = 0x0, - Underline = 0x1, - Overline = 0x2, - StrikeOut = 0x4, - Background = 0x8 - }; - Q_DECLARE_FLAGS(Decorations, Decoration) - - QSGTextNode(QSGContext *); - ~QSGTextNode(); - - static bool isComplexRichText(QTextDocument *); - - void deleteContent(); - void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color = QColor(), - QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor(), - const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(), - int selectionStart = -1, int selectionEnd = -1); - void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color = QColor(), - QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor(), - const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(), - int selectionStart = -1, int selectionEnd = -1); - - void setCursor(const QRectF &rect, const QColor &color); - QSGSimpleRectNode *cursorNode() const { return m_cursorNode; } - - QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, - QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor(), - QSGNode *parentNode = 0); - void addImage(const QRectF &rect, const QImage &image); - -private: - void mergeFormats(QTextLayout *textLayout, QVarLengthArray *mergedFormats); - - QSGContext *m_context; - QSGSimpleRectNode *m_cursorNode; - QList m_textures; -}; - -QT_END_NAMESPACE - -#endif // QSGTEXTNODE_P_H diff --git a/src/declarative/items/qsgtranslate.cpp b/src/declarative/items/qsgtranslate.cpp deleted file mode 100644 index 577cf4d427..0000000000 --- a/src/declarative/items/qsgtranslate.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgtranslate_p.h" -#include "qsgitem_p.h" - -#include - -QT_BEGIN_NAMESPACE - -class QSGTranslatePrivate : public QSGTransformPrivate -{ -public: - QSGTranslatePrivate() - : x(0), y(0) {} - - qreal x; - qreal y; -}; - -/*! - Constructs an empty QSGTranslate object with the given \a parent. -*/ -QSGTranslate::QSGTranslate(QObject *parent) -: QSGTransform(*new QSGTranslatePrivate, parent) -{ -} - -/*! - Destroys the graphics scale. -*/ -QSGTranslate::~QSGTranslate() -{ -} - -/*! - \property QSGTranslate::x - \brief the horizontal translation. - - The translation can be any real number; the default value is 0.0. - - \sa y -*/ -qreal QSGTranslate::x() const -{ - Q_D(const QSGTranslate); - return d->x; -} - -void QSGTranslate::setX(qreal x) -{ - Q_D(QSGTranslate); - if (d->x == x) - return; - d->x = x; - update(); - emit xChanged(); -} - -/*! - \property QSGTranslate::y - \brief the vertical translation. - - The translation can be any real number; the default value is 0.0. - - \sa x -*/ -qreal QSGTranslate::y() const -{ - Q_D(const QSGTranslate); - return d->y; -} -void QSGTranslate::setY(qreal y) -{ - Q_D(QSGTranslate); - if (d->y == y) - return; - d->y = y; - update(); - emit yChanged(); -} - -void QSGTranslate::applyTo(QMatrix4x4 *matrix) const -{ - Q_D(const QSGTranslate); - matrix->translate(d->x, d->y, 0); -} - -class QSGScalePrivate : public QSGTransformPrivate -{ -public: - QSGScalePrivate() - : xScale(1), yScale(1), zScale(1) {} - QVector3D origin; - qreal xScale; - qreal yScale; - qreal zScale; -}; - -QSGScale::QSGScale(QObject *parent) - : QSGTransform(*new QSGScalePrivate, parent) -{ -} - -QSGScale::~QSGScale() -{ -} - -QVector3D QSGScale::origin() const -{ - Q_D(const QSGScale); - return d->origin; -} -void QSGScale::setOrigin(const QVector3D &point) -{ - Q_D(QSGScale); - if (d->origin == point) - return; - d->origin = point; - update(); - emit originChanged(); -} - -qreal QSGScale::xScale() const -{ - Q_D(const QSGScale); - return d->xScale; -} -void QSGScale::setXScale(qreal scale) -{ - Q_D(QSGScale); - if (d->xScale == scale) - return; - d->xScale = scale; - update(); - emit xScaleChanged(); - emit scaleChanged(); -} - -qreal QSGScale::yScale() const -{ - Q_D(const QSGScale); - return d->yScale; -} -void QSGScale::setYScale(qreal scale) -{ - Q_D(QSGScale); - if (d->yScale == scale) - return; - d->yScale = scale; - update(); - emit yScaleChanged(); - emit scaleChanged(); -} - -qreal QSGScale::zScale() const -{ - Q_D(const QSGScale); - return d->zScale; -} -void QSGScale::setZScale(qreal scale) -{ - Q_D(QSGScale); - if (d->zScale == scale) - return; - d->zScale = scale; - update(); - emit zScaleChanged(); - emit scaleChanged(); -} - -void QSGScale::applyTo(QMatrix4x4 *matrix) const -{ - Q_D(const QSGScale); - matrix->translate(d->origin); - matrix->scale(d->xScale, d->yScale, d->zScale); - matrix->translate(-d->origin); -} - -class QSGRotationPrivate : public QSGTransformPrivate -{ -public: - QSGRotationPrivate() - : angle(0), axis(0, 0, 1) {} - QVector3D origin; - qreal angle; - QVector3D axis; -}; - -QSGRotation::QSGRotation(QObject *parent) - : QSGTransform(*new QSGRotationPrivate, parent) -{ -} - -QSGRotation::~QSGRotation() -{ -} - -QVector3D QSGRotation::origin() const -{ - Q_D(const QSGRotation); - return d->origin; -} - -void QSGRotation::setOrigin(const QVector3D &point) -{ - Q_D(QSGRotation); - if (d->origin == point) - return; - d->origin = point; - update(); - emit originChanged(); -} - -qreal QSGRotation::angle() const -{ - Q_D(const QSGRotation); - return d->angle; -} -void QSGRotation::setAngle(qreal angle) -{ - Q_D(QSGRotation); - if (d->angle == angle) - return; - d->angle = angle; - update(); - emit angleChanged(); -} - -QVector3D QSGRotation::axis() const -{ - Q_D(const QSGRotation); - return d->axis; -} -void QSGRotation::setAxis(const QVector3D &axis) -{ - Q_D(QSGRotation); - if (d->axis == axis) - return; - d->axis = axis; - update(); - emit axisChanged(); -} - -void QSGRotation::setAxis(Qt::Axis axis) -{ - switch (axis) - { - case Qt::XAxis: - setAxis(QVector3D(1, 0, 0)); - break; - case Qt::YAxis: - setAxis(QVector3D(0, 1, 0)); - break; - case Qt::ZAxis: - setAxis(QVector3D(0, 0, 1)); - break; - } -} - -class QGraphicsRotation { -public: - static inline void projectedRotate(QMatrix4x4 *matrix, qreal angle, qreal x, qreal y, qreal z) - { - matrix->projectedRotate(angle, x, y, z); - } -}; - -void QSGRotation::applyTo(QMatrix4x4 *matrix) const -{ - Q_D(const QSGRotation); - - if (d->angle == 0. || d->axis.isNull()) - return; - - matrix->translate(d->origin); - QGraphicsRotation::projectedRotate(matrix, d->angle, d->axis.x(), d->axis.y(), d->axis.z()); - matrix->translate(-d->origin); -} - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgtranslate_p.h b/src/declarative/items/qsgtranslate_p.h deleted file mode 100644 index fb85daabcf..0000000000 --- a/src/declarative/items/qsgtranslate_p.h +++ /dev/null @@ -1,162 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGTRANSLATE_P_H -#define QSGTRANSLATE_P_H - -#include "qsgitem.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGTranslatePrivate; -class Q_AUTOTEST_EXPORT QSGTranslate : public QSGTransform -{ - Q_OBJECT - - Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) - Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) - -public: - QSGTranslate(QObject *parent = 0); - ~QSGTranslate(); - - qreal x() const; - void setX(qreal); - - qreal y() const; - void setY(qreal); - - void applyTo(QMatrix4x4 *matrix) const; - -Q_SIGNALS: - void xChanged(); - void yChanged(); - -private: - Q_DECLARE_PRIVATE(QSGTranslate) - Q_DISABLE_COPY(QSGTranslate) -}; - -class QSGScalePrivate; -class Q_AUTOTEST_EXPORT QSGScale : public QSGTransform -{ - Q_OBJECT - - Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) - Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY xScaleChanged) - Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY yScaleChanged) - Q_PROPERTY(qreal zScale READ zScale WRITE setZScale NOTIFY zScaleChanged) -public: - QSGScale(QObject *parent = 0); - ~QSGScale(); - - QVector3D origin() const; - void setOrigin(const QVector3D &point); - - qreal xScale() const; - void setXScale(qreal); - - qreal yScale() const; - void setYScale(qreal); - - qreal zScale() const; - void setZScale(qreal); - - void applyTo(QMatrix4x4 *matrix) const; - -Q_SIGNALS: - void originChanged(); - void xScaleChanged(); - void yScaleChanged(); - void zScaleChanged(); - void scaleChanged(); - -private: - Q_DECLARE_PRIVATE(QSGScale) -}; - -class QSGRotationPrivate; -class Q_AUTOTEST_EXPORT QSGRotation : public QSGTransform -{ - Q_OBJECT - - Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) - Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) - Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) -public: - QSGRotation(QObject *parent = 0); - ~QSGRotation(); - - QVector3D origin() const; - void setOrigin(const QVector3D &point); - - qreal angle() const; - void setAngle(qreal); - - QVector3D axis() const; - void setAxis(const QVector3D &axis); - void setAxis(Qt::Axis axis); - - void applyTo(QMatrix4x4 *matrix) const; - -Q_SIGNALS: - void originChanged(); - void angleChanged(); - void axisChanged(); - -private: - Q_DECLARE_PRIVATE(QSGRotation) -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGTranslate) - -QT_END_HEADER - -#endif diff --git a/src/declarative/items/qsgview.cpp b/src/declarative/items/qsgview.cpp deleted file mode 100644 index 841c8ebe31..0000000000 --- a/src/declarative/items/qsgview.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgview.h" -#include "qsgview_p.h" - -#include "qsgcanvas_p.h" -#include "qsgitem_p.h" -#include "qsgitemchangelistener_p.h" - -#include -#include - -#include -#include -#include - - -// XXX todo - This whole class should probably be merged with QDeclarativeView for -// maximum seamlessness -QT_BEGIN_NAMESPACE - -DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE) - -void QSGViewPrivate::init() -{ - Q_Q(QSGView); - - QDeclarativeEnginePrivate::get(&engine)->sgContext = QSGCanvasPrivate::context; - - engine.setIncubationController(q->incubationController()); - - if (QDeclarativeDebugService::isDebuggingEnabled()) - QDeclarativeInspectorService::instance()->addView(q); -} - -QSGViewPrivate::QSGViewPrivate() - : root(0), component(0), resizeMode(QSGView::SizeViewToRootObject), initialSize(0,0), resized(false) -{ -} - -QSGViewPrivate::~QSGViewPrivate() -{ - if (QDeclarativeDebugService::isDebuggingEnabled()) - QDeclarativeInspectorService::instance()->removeView(q_func()); - - delete root; -} - -void QSGViewPrivate::execute() -{ - Q_Q(QSGView); - if (root) { - delete root; - root = 0; - } - if (component) { - delete component; - component = 0; - } - if (!source.isEmpty()) { - component = new QDeclarativeComponent(&engine, source, q); - if (!component->isLoading()) { - q->continueExecute(); - } else { - QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), - q, SLOT(continueExecute())); - } - } -} - -void QSGViewPrivate::itemGeometryChanged(QSGItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) -{ - Q_Q(QSGView); - if (resizeItem == root && resizeMode == QSGView::SizeViewToRootObject) { - // wait for both width and height to be changed - resizetimer.start(0,q); - } - QSGItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); -} - -QSGView::QSGView(QWindow *parent, Qt::WindowFlags f) -: QSGCanvas(*(new QSGViewPrivate), parent) -{ - setWindowFlags(f); - d_func()->init(); -} - -QSGView::QSGView(const QUrl &source, QWindow *parent, Qt::WindowFlags f) -: QSGCanvas(*(new QSGViewPrivate), parent) -{ - setWindowFlags(f); - d_func()->init(); - setSource(source); -} - -QSGView::~QSGView() -{ -} - -void QSGView::setSource(const QUrl& url) -{ - Q_D(QSGView); - d->source = url; - d->execute(); -} - -QUrl QSGView::source() const -{ - Q_D(const QSGView); - return d->source; -} - -QDeclarativeEngine* QSGView::engine() const -{ - Q_D(const QSGView); - return const_cast(&d->engine); -} - -QDeclarativeContext* QSGView::rootContext() const -{ - Q_D(const QSGView); - return d->engine.rootContext(); -} - -QSGView::Status QSGView::status() const -{ - Q_D(const QSGView); - if (!d->component) - return QSGView::Null; - - return QSGView::Status(d->component->status()); -} - -QList QSGView::errors() const -{ - Q_D(const QSGView); - if (d->component) - return d->component->errors(); - return QList(); -} - -void QSGView::setResizeMode(ResizeMode mode) -{ - Q_D(QSGView); - if (d->resizeMode == mode) - return; - - if (d->root) { - if (d->resizeMode == SizeViewToRootObject) { - QSGItemPrivate *p = QSGItemPrivate::get(d->root); - p->removeItemChangeListener(d, QSGItemPrivate::Geometry); - } - } - - d->resizeMode = mode; - if (d->root) { - d->initResize(); - } -} - -void QSGViewPrivate::initResize() -{ - if (root) { - if (resizeMode == QSGView::SizeViewToRootObject) { - QSGItemPrivate *p = QSGItemPrivate::get(root); - p->addItemChangeListener(this, QSGItemPrivate::Geometry); - } - } - updateSize(); -} - -void QSGViewPrivate::updateSize() -{ - Q_Q(QSGView); - if (!root) - return; - - if (resizeMode == QSGView::SizeViewToRootObject) { - QSize newSize = QSize(root->width(), root->height()); - if (newSize.isValid() && newSize != q->size()) { - q->resize(newSize); - } - } else if (resizeMode == QSGView::SizeRootObjectToView) { - if (!qFuzzyCompare(q->width(), root->width())) - root->setWidth(q->width()); - if (!qFuzzyCompare(q->height(), root->height())) - root->setHeight(q->height()); - } -} - -QSize QSGViewPrivate::rootObjectSize() const -{ - QSize rootObjectSize(0,0); - int widthCandidate = -1; - int heightCandidate = -1; - if (root) { - widthCandidate = root->width(); - heightCandidate = root->height(); - } - if (widthCandidate > 0) { - rootObjectSize.setWidth(widthCandidate); - } - if (heightCandidate > 0) { - rootObjectSize.setHeight(heightCandidate); - } - return rootObjectSize; -} - -QSGView::ResizeMode QSGView::resizeMode() const -{ - Q_D(const QSGView); - return d->resizeMode; -} - -/*! - \internal - */ -void QSGView::continueExecute() -{ - Q_D(QSGView); - disconnect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueExecute())); - - if (d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QDeclarativeError &error, errorList) { - qWarning() << error; - } - emit statusChanged(status()); - return; - } - - QObject *obj = d->component->create(); - - if (d->component->isError()) { - QList errorList = d->component->errors(); - foreach (const QDeclarativeError &error, errorList) { - qWarning() << error; - } - emit statusChanged(status()); - return; - } - - d->setRootObject(obj); - emit statusChanged(status()); -} - - -/*! - \internal -*/ -void QSGViewPrivate::setRootObject(QObject *obj) -{ - Q_Q(QSGView); - if (root == obj) - return; - if (QSGItem *sgItem = qobject_cast(obj)) { - root = sgItem; - sgItem->setParentItem(q->QSGCanvas::rootItem()); - } else { - qWarning() << "QSGView only supports loading of root objects that derive from QSGItem." << endl - << endl - << "If your example is using QML 2, (such as qmlscene) and the .qml file you" << endl - << "loaded has 'import QtQuick 1.0' or 'import Qt 4.7', this error will occur." << endl - << endl - << "To load files with 'import QtQuick 1.0' with QML 2, specify:" << endl - << " QMLSCENE_IMPORT_NAME=quick1" << endl - << "on as an environment variable prior to launching the application." << endl - << endl - << "To load files with 'import Qt 4.7' with QML 2, specify:" << endl - << " QMLSCENE_IMPORT_NAME=qt" << endl - << "on as an environment variable prior to launching the application." << endl; - delete obj; - root = 0; - } - if (root) { - initialSize = rootObjectSize(); - if ((resizeMode == QSGView::SizeViewToRootObject || !resized) // ### refactor: || !q->testAttribute(Qt::WA_Resized) - && initialSize != q->size()) { - - q->resize(initialSize); - resized = true; - } - initResize(); - } -} - -/*! - \internal - If the \l {QTimerEvent} {timer event} \a e is this - view's resize timer, sceneResized() is emitted. - */ -void QSGView::timerEvent(QTimerEvent* e) -{ - Q_D(QSGView); - if (!e || e->timerId() == d->resizetimer.timerId()) { - d->updateSize(); - d->resizetimer.stop(); - } -} - -/*! - \internal - Preferred size follows the root object geometry. -*/ -QSize QSGView::sizeHint() const -{ - Q_D(const QSGView); - QSize rootObjectSize = d->rootObjectSize(); - if (rootObjectSize.isEmpty()) { - return size(); - } else { - return rootObjectSize; - } -} - -QSize QSGView::initialSize() const -{ - Q_D(const QSGView); - return d->initialSize; -} - -QSGItem *QSGView::rootObject() const -{ - Q_D(const QSGView); - return d->root; -} - -/*! - \internal - This function handles the \l {QResizeEvent} {resize event} - \a e. - */ -void QSGView::resizeEvent(QResizeEvent *e) -{ - Q_D(QSGView); - if (d->resizeMode == SizeRootObjectToView) - d->updateSize(); - - QSGCanvas::resizeEvent(e); -} - -void QSGView::keyPressEvent(QKeyEvent *e) -{ - QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); - - QSGCanvas::keyPressEvent(e); -} - -void QSGView::keyReleaseEvent(QKeyEvent *e) -{ - QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); - - QSGCanvas::keyReleaseEvent(e); -} - -void QSGView::mouseMoveEvent(QMouseEvent *e) -{ - QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); - - QSGCanvas::mouseMoveEvent(e); -} - -void QSGView::mousePressEvent(QMouseEvent *e) -{ - QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); - - QSGCanvas::mousePressEvent(e); -} - -void QSGView::mouseReleaseEvent(QMouseEvent *e) -{ - QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); - - QSGCanvas::mouseReleaseEvent(e); -} - - -QT_END_NAMESPACE diff --git a/src/declarative/items/qsgview.h b/src/declarative/items/qsgview.h deleted file mode 100644 index 2d8e8b4067..0000000000 --- a/src/declarative/items/qsgview.h +++ /dev/null @@ -1,120 +0,0 @@ -// Commit: 0b83a2161261be525f01359397ab1c8c34827749 -/**************************************************************************** -** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGVIEW_H -#define QSGVIEW_H - -#include -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QDeclarativeEngine; -class QDeclarativeContext; -class QDeclarativeError; -class QSGItem; - -class QSGViewPrivate; -class Q_DECLARATIVE_EXPORT QSGView : public QSGCanvas -{ - Q_OBJECT - Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) - Q_PROPERTY(Status status READ status NOTIFY statusChanged) - Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true) - Q_ENUMS(ResizeMode Status) -public: - explicit QSGView(QWindow *parent = 0, Qt::WindowFlags f = 0); - QSGView(const QUrl &source, QWindow *parent = 0, Qt::WindowFlags f = 0); - virtual ~QSGView(); - - QUrl source() const; - - QDeclarativeEngine* engine() const; - QDeclarativeContext* rootContext() const; - - QSGItem *rootObject() const; - - enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView }; - ResizeMode resizeMode() const; - void setResizeMode(ResizeMode); - - enum Status { Null, Ready, Loading, Error }; - Status status() const; - - QList errors() const; - - QSize sizeHint() const; - QSize initialSize() const; - -public Q_SLOTS: - void setSource(const QUrl&); - -Q_SIGNALS: - void statusChanged(QSGView::Status); - -private Q_SLOTS: - void continueExecute(); - -protected: - virtual void resizeEvent(QResizeEvent *); - virtual void timerEvent(QTimerEvent*); - - virtual void keyPressEvent(QKeyEvent *); - virtual void keyReleaseEvent(QKeyEvent *); - virtual void mousePressEvent(QMouseEvent *); - virtual void mouseReleaseEvent(QMouseEvent *); - virtual void mouseMoveEvent(QMouseEvent *); -private: - Q_DISABLE_COPY(QSGView) - Q_DECLARE_PRIVATE(QSGView) -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QSGVIEW_H diff --git a/src/declarative/items/qsgview_p.h b/src/declarative/items/qsgview_p.h deleted file mode 100644 index f2b962c28e..0000000000 --- a/src/declarative/items/qsgview_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGVIEW_P_H -#define QSGVIEW_P_H - -#include "qsgview.h" - -#include -#include -#include -#include -#include -#include "qsgcanvas_p.h" - -#include "qsgitemchangelistener_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QDeclarativeContext; -class QDeclarativeError; -class QSGItem; -class QDeclarativeComponent; - -class QSGViewPrivate : public QSGCanvasPrivate, - public QSGItemChangeListener -{ - Q_DECLARE_PUBLIC(QSGView) -public: - static QSGViewPrivate* get(QSGView *view) { return view->d_func(); } - static const QSGViewPrivate* get(const QSGView *view) { return view->d_func(); } - - QSGViewPrivate(); - ~QSGViewPrivate(); - - void execute(); - void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); - void initResize(); - void updateSize(); - void setRootObject(QObject *); - - void init(); - - QSize rootObjectSize() const; - - QPointer root; - - QUrl source; - - QDeclarativeEngine engine; - QDeclarativeComponent *component; - QBasicTimer resizetimer; - - QSGView::ResizeMode resizeMode; - QSize initialSize; - QElapsedTimer frameTimer; - - bool resized; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QSGVIEW_P_H diff --git a/src/declarative/items/qsgvisualadaptormodel.cpp b/src/declarative/items/qsgvisualadaptormodel.cpp deleted file mode 100644 index 4e3a1a6dc3..0000000000 --- a/src/declarative/items/qsgvisualadaptormodel.cpp +++ /dev/null @@ -1,889 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgvisualadaptormodel_p.h" -#include "qsgitem.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -Q_DECLARE_METATYPE(QModelIndex) - -QT_BEGIN_NAMESPACE - -class VDMDelegateDataType : public QDeclarativeRefCount -{ -public: - VDMDelegateDataType() - : metaObject(0) - , propertyCache(0) - , propertyOffset(0) - , signalOffset(0) - , shared(true) - { - } - - VDMDelegateDataType(const VDMDelegateDataType &type) - : metaObject(0) - , propertyCache(0) - , propertyOffset(type.propertyOffset) - , signalOffset(type.signalOffset) - , shared(false) - , builder(type.metaObject, QMetaObjectBuilder::Properties - | QMetaObjectBuilder::Signals - | QMetaObjectBuilder::SuperClass - | QMetaObjectBuilder::ClassName) - { - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - } - - ~VDMDelegateDataType() - { - if (propertyCache) - propertyCache->release(); - qFree(metaObject); - } - - QMetaObject *metaObject; - QDeclarativePropertyCache *propertyCache; - int propertyOffset; - int signalOffset; - bool shared : 1; - QMetaObjectBuilder builder; -}; - -class QSGVisualAdaptorModelData : public QObject -{ - Q_OBJECT - Q_PROPERTY(int index READ index NOTIFY indexChanged) -public: - QSGVisualAdaptorModelData(int index, QSGVisualAdaptorModel *model); - ~QSGVisualAdaptorModelData(); - - int index() const; - void setIndex(int index); - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - QDeclarativeGuard m_model; - QIntrusiveListNode m_cacheNode; -}; - -typedef QIntrusiveList QSGVisualAdaptorModelDataCache; - -class QSGVisualAdaptorModelDataMetaObject; -class QSGVisualAdaptorModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QSGVisualAdaptorModel) -public: - QSGVisualAdaptorModelPrivate() - : m_engine(0) - , m_listAccessor(0) - , m_delegateDataType(0) - , createModelData(&initializeModelData) - , m_ref(0) - , m_count(0) - , m_objectList(false) - { - } - - - static QSGVisualAdaptorModelPrivate *get(QSGVisualAdaptorModel *m) { - return static_cast(QObjectPrivate::get(m)); - } - - void addProperty(int role, int propertyId, const char *propertyName, const char *propertyType, bool isModelData = false); - template void setModelDataType() - { - createModelData = &T::create; - m_delegateDataType->builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - m_delegateDataType->builder.setClassName(T::staticMetaObject.className()); - m_delegateDataType->builder.setSuperClass(&T::staticMetaObject); - m_delegateDataType->propertyOffset = T::staticMetaObject.propertyCount(); - m_delegateDataType->signalOffset = T::staticMetaObject.methodCount(); - } - QSGVisualAdaptorModelData *createMetaObject(int index, QSGVisualAdaptorModel *model); - - static QSGVisualAdaptorModelData *initializeModelData(int index, QSGVisualAdaptorModel *model) { - return get(model)->createMetaObject(index, model); - } - - typedef QSGVisualAdaptorModelData *(*CreateModelData)(int index, QSGVisualAdaptorModel *model); - - struct PropertyData { - int role; - bool isModelData : 1; - }; - - int modelCount() const { - if (m_listModelInterface) - return m_listModelInterface->count(); - if (m_abstractItemModel) - return m_abstractItemModel->rowCount(m_root); - if (m_listAccessor) - return m_listAccessor->count(); - return 0; - } - - QDeclarativeGuard m_engine; - QDeclarativeGuard m_listModelInterface; - QDeclarativeGuard m_abstractItemModel; - QDeclarativeListAccessor *m_listAccessor; - VDMDelegateDataType *m_delegateDataType; - CreateModelData createModelData; - - int m_ref; - int m_count; - QSGVisualAdaptorModel::Flags m_flags; - bool m_objectList : 1; - - QVariant m_modelVariant; - QModelIndex m_root; - - QList m_roles; - QList watchedRoleIds; - QList watchedRoles; - QHash m_roleNames; - QVector m_propertyData; - QSGVisualAdaptorModelDataCache m_cache; -}; - -class QSGVisualAdaptorModelDataMetaObject : public QAbstractDynamicMetaObject -{ -public: - QSGVisualAdaptorModelDataMetaObject(QSGVisualAdaptorModelData *data, VDMDelegateDataType *type) - : m_data(data) - , m_type(type) - { - QObjectPrivate *op = QObjectPrivate::get(m_data); - *static_cast(this) = *type->metaObject; - op->metaObject = this; - m_type->addref(); - } - - ~QSGVisualAdaptorModelDataMetaObject() { m_type->release(); } - - QSGVisualAdaptorModelData *m_data; - VDMDelegateDataType *m_type; -}; - -class QSGVDMAbstractItemModelDataMetaObject : public QSGVisualAdaptorModelDataMetaObject -{ -public: - QSGVDMAbstractItemModelDataMetaObject(QSGVisualAdaptorModelData *object, VDMDelegateDataType *type) - : QSGVisualAdaptorModelDataMetaObject(object, type) {} - - int metaCall(QMetaObject::Call call, int id, void **arguments) - { - if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) { - QSGVisualAdaptorModelPrivate *model = QSGVisualAdaptorModelPrivate::get(m_data->m_model); - if (m_data->m_index == -1 || !model->m_abstractItemModel) - return -1; - *static_cast(arguments[0]) = model->m_abstractItemModel->index( - m_data->m_index, 0, model->m_root).data(model->m_propertyData.at(id - m_type->propertyOffset).role); - return -1; - } else { - return m_data->qt_metacall(call, id, arguments); - } - } -}; - -class QSGVDMAbstractItemModelData : public QSGVisualAdaptorModelData -{ - Q_OBJECT - Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) -public: - bool hasModelChildren() const - { - QSGVisualAdaptorModelPrivate *model = QSGVisualAdaptorModelPrivate::get(m_model); - return model->m_abstractItemModel->hasChildren(model->m_abstractItemModel->index(m_index, 0, model->m_root)); - } - - static QSGVisualAdaptorModelData *create(int index, QSGVisualAdaptorModel *model) { - return new QSGVDMAbstractItemModelData(index, model); } -private: - QSGVDMAbstractItemModelData(int index, QSGVisualAdaptorModel *model) - : QSGVisualAdaptorModelData(index, model) - { - new QSGVDMAbstractItemModelDataMetaObject( - this, QSGVisualAdaptorModelPrivate::get(m_model)->m_delegateDataType); - } -}; - -class QSGVDMListModelInterfaceDataMetaObject : public QSGVisualAdaptorModelDataMetaObject -{ -public: - QSGVDMListModelInterfaceDataMetaObject(QSGVisualAdaptorModelData *object, VDMDelegateDataType *type) - : QSGVisualAdaptorModelDataMetaObject(object, type) {} - - int metaCall(QMetaObject::Call call, int id, void **arguments) - { - if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) { - QSGVisualAdaptorModelPrivate *model = QSGVisualAdaptorModelPrivate::get(m_data->m_model); - if (m_data->m_index == -1 || !model->m_listModelInterface) - return -1; - *static_cast(arguments[0]) = model->m_listModelInterface->data( - m_data->m_index, model->m_propertyData.at(id - m_type->propertyOffset).role); - return -1; - } else { - return m_data->qt_metacall(call, id, arguments); - } - } -}; - -class QSGVDMListModelInterfaceData : public QSGVisualAdaptorModelData -{ -public: - static QSGVisualAdaptorModelData *create(int index, QSGVisualAdaptorModel *model) { - return new QSGVDMListModelInterfaceData(index, model); } -private: - QSGVDMListModelInterfaceData(int index, QSGVisualAdaptorModel *model) - : QSGVisualAdaptorModelData(index, model) - { - new QSGVDMListModelInterfaceDataMetaObject( - this, QSGVisualAdaptorModelPrivate::get(m_model)->m_delegateDataType); - } -}; - -class QSGVDMListAccessorData : public QSGVisualAdaptorModelData -{ - Q_OBJECT - Q_PROPERTY(QVariant modelData READ modelData CONSTANT) -public: - QVariant modelData() const { - return QSGVisualAdaptorModelPrivate::get(m_model)->m_listAccessor->at(m_index); } - - static QSGVisualAdaptorModelData *create(int index, QSGVisualAdaptorModel *model) { - return new QSGVDMListAccessorData(index, model); } -private: - QSGVDMListAccessorData(int index, QSGVisualAdaptorModel *model) - : QSGVisualAdaptorModelData(index, model) - { - } -}; - -class QSGVDMObjectDataMetaObject : public QSGVisualAdaptorModelDataMetaObject -{ -public: - QSGVDMObjectDataMetaObject(QSGVisualAdaptorModelData *data, VDMDelegateDataType *type) - : QSGVisualAdaptorModelDataMetaObject(data, type) - , m_object(QSGVisualAdaptorModelPrivate::get(data->m_model)->m_listAccessor->at(data->m_index).value()) - {} - - int metaCall(QMetaObject::Call call, int id, void **arguments) - { - if (id >= m_type->propertyOffset - && (call == QMetaObject::ReadProperty - || call == QMetaObject::WriteProperty - || call == QMetaObject::ResetProperty)) { - if (m_object) - QMetaObject::metacall(m_object, call, id - m_type->propertyOffset + 1, arguments); - return -1; - } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) { - QMetaObject::activate(m_data, this, id, 0); - return -1; - } else { - return m_data->qt_metacall(call, id, arguments); - } - } - - int createProperty(const char *name, const char *) - { - if (!m_object) - return -1; - const QMetaObject *metaObject = m_object->metaObject(); - - const int previousPropertyCount = propertyCount() - propertyOffset(); - int propertyIndex = metaObject->indexOfProperty(name); - if (propertyIndex == -1) - return -1; - if (previousPropertyCount + 1 == metaObject->propertyCount()) - return propertyIndex + m_type->propertyOffset - 1; - - if (m_type->shared) { - VDMDelegateDataType *type = m_type; - m_type = new VDMDelegateDataType(*m_type); - type->release(); - } - - const int previousMethodCount = methodCount(); - int notifierId = previousMethodCount; - for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - 1; ++propertyId) { - QMetaProperty property = metaObject->property(propertyId + 1); - QMetaPropertyBuilder propertyBuilder; - if (property.hasNotifySignal()) { - m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId); - ++notifierId; - } else { - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName()); - } - propertyBuilder.setWritable(property.isWritable()); - propertyBuilder.setResettable(property.isResettable()); - propertyBuilder.setConstant(property.isConstant()); - } - - if (m_type->metaObject) - qFree(m_type->metaObject); - m_type->metaObject = m_type->builder.toMetaObject(); - *static_cast(this) = *m_type->metaObject; - - notifierId = previousMethodCount; - for (int i = previousPropertyCount; i < metaObject->propertyCount(); ++i) { - QMetaProperty property = metaObject->property(i); - if (property.hasNotifySignal()) { - QDeclarativePropertyPrivate::connect( - m_object, property.notifySignalIndex(), m_data, notifierId); - ++notifierId; - } - } - return propertyIndex + m_type->propertyOffset - 1; - } - - QDeclarativeGuard m_object; -}; - -class QSGVDMObjectData : public QSGVisualAdaptorModelData, public QSGVisualAdaptorModelProxyInterface -{ - Q_OBJECT - Q_PROPERTY(QObject *modelData READ modelData CONSTANT) - Q_INTERFACES(QSGVisualAdaptorModelProxyInterface) -public: - QObject *modelData() const { return m_metaObject->m_object; } - QObject *proxiedObject() { return m_metaObject->m_object; } - - static QSGVisualAdaptorModelData *create(int index, QSGVisualAdaptorModel *model) { - return new QSGVDMObjectData(index, model); } - -private: - QSGVDMObjectData(int index, QSGVisualAdaptorModel *model) - : QSGVisualAdaptorModelData(index, model) - , m_metaObject(new QSGVDMObjectDataMetaObject(this, QSGVisualAdaptorModelPrivate::get(m_model)->m_delegateDataType)) - { - } - - QSGVDMObjectDataMetaObject *m_metaObject; -}; - -void QSGVisualAdaptorModelPrivate::addProperty( - int role, int propertyId, const char *propertyName, const char *propertyType, bool isModelData) -{ - PropertyData propertyData; - propertyData.role = role; - propertyData.isModelData = isModelData; - m_delegateDataType->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); - QMetaPropertyBuilder property = m_delegateDataType->builder.addProperty( - propertyName, propertyType, propertyId); - property.setWritable(false); - - m_propertyData.append(propertyData); -} - -QSGVisualAdaptorModelData *QSGVisualAdaptorModelPrivate::createMetaObject(int index, QSGVisualAdaptorModel *model) -{ - Q_ASSERT(!m_delegateDataType); - - m_objectList = false; - m_propertyData.clear(); - if (m_listAccessor - && m_listAccessor->type() != QDeclarativeListAccessor::ListProperty - && m_listAccessor->type() != QDeclarativeListAccessor::Instance) { - createModelData = &QSGVDMListAccessorData::create; - m_flags = QSGVisualAdaptorModel::MetaObjectCacheable; - return QSGVDMListAccessorData::create(index, model); - } - - m_delegateDataType = new VDMDelegateDataType; - if (m_listModelInterface) { - setModelDataType(); - QList roles = m_listModelInterface->roles(); - for (int propertyId = 0; propertyId < roles.count(); ++propertyId) { - const int role = roles.at(propertyId); - const QByteArray propertyName = m_listModelInterface->toString(role).toUtf8(); - addProperty(role, propertyId, propertyName, "QVariant"); - m_roleNames.insert(propertyName, role); - } - if (m_propertyData.count() == 1) - addProperty(roles.first(), 1, "modelData", "QVariant", true); - m_flags = QSGVisualAdaptorModel::MetaObjectCacheable; - } else if (m_abstractItemModel) { - setModelDataType(); - QHash roleNames = m_abstractItemModel->roleNames(); - for (QHash::const_iterator it = roleNames.begin(); it != roleNames.end(); ++it) { - addProperty(it.key(), m_propertyData.count(), it.value(), "QVariant"); - m_roleNames.insert(it.value(), it.key()); - } - if (m_propertyData.count() == 1) - addProperty(roleNames.begin().key(), 1, "modelData", "QVariant", true); - m_flags = QSGVisualAdaptorModel::MetaObjectCacheable; - } else if (m_listAccessor) { - setModelDataType(); - m_objectList = true; - m_flags = QSGVisualAdaptorModel::ProxiedObject; - } else { - Q_ASSERT(!"No model set on VisualDataModel"); - return 0; - } - m_delegateDataType->metaObject = m_delegateDataType->builder.toMetaObject(); - if (!m_objectList) { - m_delegateDataType->propertyCache = new QDeclarativePropertyCache( - m_engine, m_delegateDataType->metaObject); - } - return createModelData(index, model); -} - -QSGVisualAdaptorModelData::QSGVisualAdaptorModelData(int index, QSGVisualAdaptorModel *model) - : m_index(index) - , m_model(model) -{ -} - -QSGVisualAdaptorModelData::~QSGVisualAdaptorModelData() -{ -} - -int QSGVisualAdaptorModelData::index() const -{ - return m_index; -} - -// This is internal only - it should not be set from qml -void QSGVisualAdaptorModelData::setIndex(int index) -{ - m_index = index; - emit indexChanged(); -} - -//--------------------------------------------------------------------------- - -QSGVisualAdaptorModel::QSGVisualAdaptorModel(QObject *parent) - : QObject(*(new QSGVisualAdaptorModelPrivate), parent) -{ -} - -QSGVisualAdaptorModel::~QSGVisualAdaptorModel() -{ - Q_D(QSGVisualAdaptorModel); - if (d->m_listAccessor) - delete d->m_listAccessor; - if (d->m_delegateDataType) - d->m_delegateDataType->release(); -} - -QSGVisualAdaptorModel::Flags QSGVisualAdaptorModel::flags() const -{ - Q_D(const QSGVisualAdaptorModel); - return d->m_flags; -} - -QVariant QSGVisualAdaptorModel::model() const -{ - Q_D(const QSGVisualAdaptorModel); - return d->m_modelVariant; -} - -void QSGVisualAdaptorModel::setModel(const QVariant &model, QDeclarativeEngine *engine) -{ - Q_D(QSGVisualAdaptorModel); - delete d->m_listAccessor; - d->m_engine = engine; - d->m_listAccessor = 0; - d->m_modelVariant = model; - if (d->m_listModelInterface) { - // Assume caller has released all items. - QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList)), - this, SLOT(_q_itemsChanged(int,int,QList))); - QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), - this, SLOT(_q_itemsInserted(int,int))); - QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), - this, SLOT(_q_itemsRemoved(int,int))); - QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), - this, SLOT(_q_itemsMoved(int,int,int))); - d->m_listModelInterface = 0; - } else if (d->m_abstractItemModel) { - QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::disconnect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(_q_dataChanged(QModelIndex,QModelIndex))); - QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset())); - QObject::disconnect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); - d->m_abstractItemModel = 0; - } - - d->m_roles.clear(); - d->m_roleNames.clear(); - d->m_flags = QSGVisualAdaptorModel::Flags(); - if (d->m_delegateDataType) - d->m_delegateDataType->release(); - d->m_delegateDataType = 0; - d->createModelData = &QSGVisualAdaptorModelPrivate::initializeModelData; - - if (d->m_count) - emit itemsRemoved(0, d->m_count); - - QObject *object = qvariant_cast(model); - if (object && (d->m_listModelInterface = qobject_cast(object))) { - QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList)), - this, SLOT(_q_itemsChanged(int,int,QList))); - QObject::connect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)), - this, SLOT(_q_itemsInserted(int,int))); - QObject::connect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)), - this, SLOT(_q_itemsRemoved(int,int))); - QObject::connect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)), - this, SLOT(_q_itemsMoved(int,int,int))); - if ((d->m_count = d->m_listModelInterface->count())) - emit itemsInserted(0, d->m_count); - return; - } else if (object && (d->m_abstractItemModel = qobject_cast(object))) { - QObject::connect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::connect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::connect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(_q_dataChanged(QModelIndex,QModelIndex))); - QObject::connect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::connect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset())); - QObject::connect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged())); - - if ((d->m_count = d->m_abstractItemModel->rowCount(d->m_root))) - emit itemsInserted(0, d->m_count); - return; - } - - d->m_listAccessor = new QDeclarativeListAccessor; - d->m_listAccessor->setList(model, d->m_engine); - if ((d->m_count = d->m_listAccessor->count())) - emit itemsInserted(0, d->m_count); -} - -QVariant QSGVisualAdaptorModel::rootIndex() const -{ - Q_D(const QSGVisualAdaptorModel); - return QVariant::fromValue(d->m_root); -} - -void QSGVisualAdaptorModel::setRootIndex(const QVariant &root) -{ - Q_D(QSGVisualAdaptorModel); - QModelIndex modelIndex = qvariant_cast(root); - if (d->m_root != modelIndex) { - int oldCount = d->modelCount(); - d->m_root = modelIndex; - if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(modelIndex)) - d->m_abstractItemModel->fetchMore(modelIndex); - int newCount = d->modelCount(); - if (oldCount) - emit itemsRemoved(0, oldCount); - if (newCount) - emit itemsInserted(0, newCount); - emit rootIndexChanged(); - } -} - -QVariant QSGVisualAdaptorModel::modelIndex(int idx) const -{ - Q_D(const QSGVisualAdaptorModel); - if (d->m_abstractItemModel) - return QVariant::fromValue(d->m_abstractItemModel->index(idx, 0, d->m_root)); - return QVariant::fromValue(QModelIndex()); -} - -QVariant QSGVisualAdaptorModel::parentModelIndex() const -{ - Q_D(const QSGVisualAdaptorModel); - if (d->m_abstractItemModel) - return QVariant::fromValue(d->m_abstractItemModel->parent(d->m_root)); - return QVariant::fromValue(QModelIndex()); -} - -int QSGVisualAdaptorModel::count() const -{ - Q_D(const QSGVisualAdaptorModel); - return d->modelCount(); -} - -QObject *QSGVisualAdaptorModel::data(int index) -{ - Q_D(QSGVisualAdaptorModel); - QSGVisualAdaptorModelData *data = d->createModelData(index, this); - d->m_cache.insert(data); - return data; -} - -QString QSGVisualAdaptorModel::stringValue(int index, const QString &name) -{ - Q_D(QSGVisualAdaptorModel); - if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor) { - if (QObject *object = d->m_listAccessor->at(index).value()) - return object->property(name.toUtf8()).toString(); - } - - QString val; - QSGVisualAdaptorModelData *data = d->createModelData(index, this); - - QDeclarativeData *ddata = QDeclarativeData::get(data); - if (ddata && ddata->propertyCache) { - QDeclarativePropertyCache::Data *prop = ddata->propertyCache->property(name); - if (prop) { - if (prop->propType == QVariant::String) { - void *args[] = { &val, 0 }; - QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args); - } else if (prop->propType == qMetaTypeId()) { - QVariant v; - void *args[] = { &v, 0 }; - QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args); - val = v.toString(); - } - } else { - val = data->property(name.toUtf8()).toString(); - } - } else { - val = data->property(name.toUtf8()).toString(); - } - - delete data; - - return val; -} - -int QSGVisualAdaptorModel::indexOf(QObject *object) const -{ - if (QSGVisualAdaptorModelData *data = qobject_cast(object)) - return data->index(); - return -1; -} - -bool QSGVisualAdaptorModel::canFetchMore() const -{ - Q_D(const QSGVisualAdaptorModel); - return d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root); -} - -void QSGVisualAdaptorModel::fetchMore() -{ - Q_D(QSGVisualAdaptorModel); - if (d->m_abstractItemModel) - d->m_abstractItemModel->fetchMore(d->m_root); -} - -void QSGVisualAdaptorModel::replaceWatchedRoles(const QList &oldRoles, const QList &newRoles) -{ - Q_D(QSGVisualAdaptorModel); - d->watchedRoleIds.clear(); - foreach (const QByteArray &oldRole, oldRoles) - d->watchedRoles.removeOne(oldRole); - d->watchedRoles += newRoles; -} - -void QSGVisualAdaptorModel::_q_itemsChanged(int index, int count, const QList &roles) -{ - Q_D(QSGVisualAdaptorModel); - bool changed = roles.isEmpty(); - if (!d->watchedRoles.isEmpty() && d->watchedRoleIds.isEmpty()) { - foreach (QByteArray r, d->watchedRoles) { - if (d->m_roleNames.contains(r)) - d->watchedRoleIds << d->m_roleNames.value(r); - } - } - - QVector signalIndexes; - for (int i = 0; i < roles.count(); ++i) { - const int role = roles.at(i); - if (!changed && d->watchedRoleIds.contains(role)) - changed = true; - for (int propertyId = 0; propertyId < d->m_propertyData.count(); ++propertyId) { - if (d->m_propertyData.at(propertyId).role == role) - signalIndexes.append(propertyId + d->m_delegateDataType->signalOffset); - } - } - if (roles.isEmpty()) { - for (int propertyId = 0; propertyId < d->m_propertyData.count(); ++propertyId) - signalIndexes.append(propertyId + d->m_delegateDataType->signalOffset); - } - - typedef QSGVisualAdaptorModelDataCache::iterator iterator; - for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { - const int idx = it->index(); - if (idx >= index && idx < index + count) { - QSGVisualAdaptorModelData *data = *it; - for (int i = 0; i < signalIndexes.count(); ++i) - QMetaObject::activate(data, signalIndexes.at(i), 0); - } - } - if (changed) - emit itemsChanged(index, count); -} - -void QSGVisualAdaptorModel::_q_itemsInserted(int index, int count) -{ - Q_D(QSGVisualAdaptorModel); - if (count <= 0) - return; - d->m_count += count; - - typedef QSGVisualAdaptorModelDataCache::iterator iterator; - for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { - if (it->index() >= index) - it->setIndex(it->index() + count); - } - - emit itemsInserted(index, count); -} - -void QSGVisualAdaptorModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QSGVisualAdaptorModel); - if (count <= 0) - return; - d->m_count -= count; - - typedef QSGVisualAdaptorModelDataCache::iterator iterator; - for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { - if (it->index() >= index + count) - it->setIndex(it->index() - count); - else if (it->index() >= index) - it->setIndex(-1); - } - - emit itemsRemoved(index, count); -} - -void QSGVisualAdaptorModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QSGVisualAdaptorModel); - const int minimum = qMin(from, to); - const int maximum = qMax(from, to) + count; - const int difference = from > to ? count : -count; - - typedef QSGVisualAdaptorModelDataCache::iterator iterator; - for (iterator it = d->m_cache.begin(); it != d->m_cache.end(); ++it) { - if (it->index() >= from && it->index() < from + count) - it->setIndex(it->index() - from + to); - else if (it->index() >= minimum && it->index() < maximum) - it->setIndex(it->index() + difference); - } - emit itemsMoved(from, to, count); -} - -void QSGVisualAdaptorModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) -{ - Q_D(QSGVisualAdaptorModel); - if (parent == d->m_root) - _q_itemsInserted(begin, end - begin + 1); -} - -void QSGVisualAdaptorModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QSGVisualAdaptorModel); - if (parent == d->m_root) - _q_itemsRemoved(begin, end - begin + 1); -} - -void QSGVisualAdaptorModel::_q_rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) -{ - Q_D(QSGVisualAdaptorModel); - const int count = sourceEnd - sourceStart + 1; - if (destinationParent == d->m_root && sourceParent == d->m_root) { - _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow-count, count); - } else if (sourceParent == d->m_root) { - _q_itemsRemoved(sourceStart, count); - } else if (destinationParent == d->m_root) { - _q_itemsInserted(destinationRow, count); - } -} - -void QSGVisualAdaptorModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end) -{ - Q_D(QSGVisualAdaptorModel); - if (begin.parent() == d->m_root) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, d->m_roles); -} - -void QSGVisualAdaptorModel::_q_layoutChanged() -{ - Q_D(QSGVisualAdaptorModel); - _q_itemsChanged(0, count(), d->m_roles); -} - -void QSGVisualAdaptorModel::_q_modelReset() -{ - Q_D(QSGVisualAdaptorModel); - int oldCount = d->m_count; - d->m_root = QModelIndex(); - d->m_count = d->modelCount(); - emit modelReset(oldCount, d->m_count); - emit rootIndexChanged(); - if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root)) - d->m_abstractItemModel->fetchMore(d->m_root); -} - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QListModelInterface) - -#include diff --git a/src/declarative/items/qsgvisualadaptormodel_p.h b/src/declarative/items/qsgvisualadaptormodel_p.h deleted file mode 100644 index 43af78b37e..0000000000 --- a/src/declarative/items/qsgvisualadaptormodel_p.h +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGVISUALADAPTORMODEL_P_H -#define QSGVISUALADAPTORMODEL_P_H - -#include -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QDeclarativeEngine; - -class QSGVisualAdaptorModelPrivate; -class QSGVisualAdaptorModel : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGVisualAdaptorModel) -public: - enum Flag - { - MetaObjectCacheable = 0x01, - ProxiedObject = 0x02 - }; - Q_DECLARE_FLAGS(Flags, Flag) - - QSGVisualAdaptorModel(QObject *parent = 0); - virtual ~QSGVisualAdaptorModel(); - - Flags flags() const; - - QVariant model() const; - void setModel(const QVariant &, QDeclarativeEngine *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - QVariant modelIndex(int idx) const; - QVariant parentModelIndex() const; - - int count() const; - QObject *data(int index); - QString stringValue(int index, const QString &role); - void replaceWatchedRoles(const QList &oldRoles, const QList &newRoles); - int indexOf(QObject *object) const; - - bool canFetchMore() const; - void fetchMore(); - -Q_SIGNALS: - void rootIndexChanged(); - void modelReset(int oldCount, int newCount); - - void itemsInserted(int index, int count); - void itemsRemoved(int index, int count); - void itemsMoved(int from, int to, int count); - void itemsChanged(int index, int count); - -private Q_SLOTS: - void _q_itemsChanged(int, int, const QList &); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_rowsInserted(const QModelIndex &,int,int); - void _q_rowsRemoved(const QModelIndex &,int,int); - void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); - void _q_dataChanged(const QModelIndex&,const QModelIndex&); - void _q_layoutChanged(); - void _q_modelReset(); - -private: - Q_DISABLE_COPY(QSGVisualAdaptorModel) -}; - -class QSGVisualAdaptorModelProxyInterface -{ -public: - virtual ~QSGVisualAdaptorModelProxyInterface() {} - - virtual QObject *proxiedObject() = 0; -}; - -Q_DECLARE_INTERFACE(QSGVisualAdaptorModelProxyInterface, "com.trolltech.qml.QSGVisualAdaptorModelProxyInterface") - -QT_END_NAMESPACE - -#endif diff --git a/src/declarative/items/qsgvisualdatamodel.cpp b/src/declarative/items/qsgvisualdatamodel.cpp deleted file mode 100644 index 47edc5d8f0..0000000000 --- a/src/declarative/items/qsgvisualdatamodel.cpp +++ /dev/null @@ -1,2538 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgvisualdatamodel_p.h" -#include "qsgitem.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -typedef QDeclarativeListCompositor Compositor; - -class QSGVisualDataGroupEmitter -{ -public: - virtual void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) = 0; - virtual void createdPackage(int, QDeclarativePackage *) {} - virtual void destroyingPackage(QDeclarativePackage *) {} - - QIntrusiveListNode emitterNode; -}; - -typedef QIntrusiveList QSGVisualDataGroupEmitterList; - -//--------------------------------------------------------------------------- - -class QSGVisualDataGroupPrivate : public QObjectPrivate -{ -public: - Q_DECLARE_PUBLIC(QSGVisualDataGroup) - - QSGVisualDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - - static QSGVisualDataGroupPrivate *get(QSGVisualDataGroup *group) { - return static_cast(QObjectPrivate::get(group)); } - - void setModel(QSGVisualDataModel *model, Compositor::Group group); - void emitChanges(QV8Engine *engine); - void emitModelUpdated(bool reset); - - void createdPackage(int index, QDeclarativePackage *package); - void destroyingPackage(QDeclarativePackage *package); - - bool parseGroupArgs(QDeclarativeV8Function *args, int *index, int *count, int *groups) const; - - Compositor::Group group; - QDeclarativeGuard model; - QSGVisualDataGroupEmitterList emitters; - QDeclarativeChangeSet changeSet; - QString name; - bool defaultInclude; -}; - -//--------------------------------------------------------------------------- - -class QSGVisualDataModelCacheItem; -class QSGVisualDataModelCacheMetaType; -class QSGVisualDataModelParts; - -class QSGVisualDataModelPrivate : public QObjectPrivate, public QSGVisualDataGroupEmitter -{ - Q_DECLARE_PUBLIC(QSGVisualDataModel) -public: - QSGVisualDataModelPrivate(QDeclarativeContext *); - - static QSGVisualDataModelPrivate *get(QSGVisualDataModel *m) { - return static_cast(QObjectPrivate::get(m)); - } - - void init(); - void connectModel(QSGVisualAdaptorModel *model); - - QObject *object(Compositor::Group group, int index, bool complete, bool reference); - void destroy(QObject *object); - QSGVisualDataModel::ReleaseFlags release(QObject *object); - QString stringValue(Compositor::Group group, int index, const QString &name); - int cacheIndexOf(QObject *object) const; - void emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package); - void emitCreatedItem(Compositor::iterator at, QSGItem *item) { - emit q_func()->createdItem(at.index[m_compositorGroup], item); } - void emitDestroyingPackage(QDeclarativePackage *package); - void emitDestroyingItem(QSGItem *item) { emit q_func()->destroyingItem(item); } - - void updateFilterGroup(); - - void addGroups(Compositor::Group group, int index, int count, int groupFlags); - void removeGroups(Compositor::Group group, int index, int count, int groupFlags); - void setGroups(Compositor::Group group, int index, int count, int groupFlags); - - void itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems = 0); - void itemsInserted(const QVector &inserts); - void itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems = 0); - void itemsRemoved(const QVector &removes); - void itemsMoved( - const QVector &removes, const QVector &inserts); - void itemsChanged(const QVector &changes); - template static v8::Local buildChangeList(const QVector &changes); - void emitChanges(); - void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); - - - static void group_append(QDeclarativeListProperty *property, QSGVisualDataGroup *group); - static int group_count(QDeclarativeListProperty *property); - static QSGVisualDataGroup *group_at(QDeclarativeListProperty *property, int index); - - QSGVisualAdaptorModel *m_adaptorModel; - QDeclarativeComponent *m_delegate; - QSGVisualDataModelCacheMetaType *m_cacheMetaType; - QDeclarativeGuard m_context; - - QList m_cache; - QSGVisualDataModelParts *m_parts; - QSGVisualDataGroupEmitterList m_pendingParts; - - QDeclarativeListCompositor m_compositor; - QDeclarativeListCompositor::Group m_compositorGroup; - bool m_complete : 1; - bool m_delegateValidated : 1; - bool m_completePending : 1; - bool m_reset : 1; - bool m_transaction : 1; - - QString m_filterGroup; - QList watchedRoles; - - union { - struct { - QSGVisualDataGroup *m_cacheItems; - QSGVisualDataGroup *m_items; - QSGVisualDataGroup *m_persistedItems; - }; - QSGVisualDataGroup *m_groups[Compositor::MaximumGroupCount]; - }; - int m_groupCount; -}; - -//--------------------------------------------------------------------------- - -class QSGVisualPartsModel : public QSGVisualModel, public QSGVisualDataGroupEmitter -{ - Q_OBJECT - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) -public: - QSGVisualPartsModel(QSGVisualDataModel *model, const QString &part, QObject *parent = 0); - ~QSGVisualPartsModel(); - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - void updateFilterGroup(); - void updateFilterGroup(Compositor::Group group, const QDeclarativeChangeSet &changeSet); - - int count() const; - bool isValid() const; - QSGItem *item(int index, bool complete=true); - ReleaseFlags release(QSGItem *item); - bool completePending() const; - void completeItem(); - QString stringValue(int index, const QString &role); - void setWatchedRoles(QList roles); - - int indexOf(QSGItem *item, QObject *objectContext) const; - - void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); - - void createdPackage(int index, QDeclarativePackage *package); - void destroyingPackage(QDeclarativePackage *package); - -Q_SIGNALS: - void filterGroupChanged(); - -private: - QSGVisualDataModel *m_model; - QHash m_packaged; - QString m_part; - QString m_filterGroup; - QList m_watchedRoles; - Compositor::Group m_compositorGroup; - bool m_inheritGroup; -}; - -class QSGVisualDataModelPartsMetaObject : public QDeclarativeOpenMetaObject -{ -public: - QSGVisualDataModelPartsMetaObject(QObject *parent) - : QDeclarativeOpenMetaObject(parent) {} - - virtual void propertyCreated(int, QMetaPropertyBuilder &); - virtual QVariant initialValue(int); -}; - -class QSGVisualDataModelParts : public QObject -{ -Q_OBJECT -public: - QSGVisualDataModelParts(QSGVisualDataModel *parent); - - QSGVisualDataModel *model; - QList models; -}; - -void QSGVisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) -{ - prop.setWritable(false); -} - -QVariant QSGVisualDataModelPartsMetaObject::initialValue(int id) -{ - QSGVisualDataModelParts *parts = static_cast(object()); - QSGVisualPartsModel *m = new QSGVisualPartsModel( - parts->model, QString::fromUtf8(name(id)), parts); - parts->models.append(m); - return QVariant::fromValue(static_cast(m)); -} - -QSGVisualDataModelParts::QSGVisualDataModelParts(QSGVisualDataModel *parent) -: QObject(parent), model(parent) -{ - new QSGVisualDataModelPartsMetaObject(this); -} - -//--------------------------------------------------------------------------- - -class QSGVisualDataModelCacheMetaType : public QDeclarativeRefCount -{ -public: - QSGVisualDataModelCacheMetaType(QV8Engine *engine, QSGVisualDataModel *model, const QStringList &groupNames); - ~QSGVisualDataModelCacheMetaType(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(QV8Engine *engine, const v8::Local &groupNames) const; - - static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); - static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); - static void set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); - static void set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); - - QDeclarativeGuard model; - const int groupCount; - const int memberPropertyOffset; - const int indexPropertyOffset; - QV8Engine * const v8Engine; - QMetaObject *metaObject; - const QStringList groupNames; - v8::Persistent constructor; -}; - -class QSGVisualDataModelCacheItem : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(VisualDataItemType) -public: - QSGVisualDataModelCacheItem(QSGVisualDataModelCacheMetaType *metaType) - : QV8ObjectResource(metaType->v8Engine) - , metaType(metaType) - , object(0) - , attached(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - { - metaType->addref(); - } - - ~QSGVisualDataModelCacheItem() - { - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - metaType->release(); - } - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef == 0 && !(groups & Compositor::PersistedFlag); } - - bool isReferenced() const { return objectRef || scriptRef || (groups & Compositor::PersistedFlag); } - - void Dispose(); - - QSGVisualDataModelCacheMetaType * const metaType; - QDeclarativeGuard object; - QSGVisualDataModelAttached *attached; - int objectRef; - int scriptRef; - int groups; - int index[Compositor::MaximumGroupCount]; -}; - -class QSGVisualDataModelAttachedMetaObject : public QAbstractDynamicMetaObject -{ -public: - QSGVisualDataModelAttachedMetaObject( - QSGVisualDataModelAttached *attached, QSGVisualDataModelCacheMetaType *metaType); - ~QSGVisualDataModelAttachedMetaObject(); - - int metaCall(QMetaObject::Call, int _id, void **); - -private: - QSGVisualDataModelAttached *attached; - QSGVisualDataModelCacheMetaType *metaType; -}; - -//--------------------------------------------------------------------------- - -QHash QSGVisualDataModelAttached::attachedProperties; - -/*! - \qmlclass VisualDataModel QSGVisualDataModel - \inqmlmodule QtQuick 2 - \ingroup qml-working-with-data - \brief The VisualDataModel encapsulates a model and delegate - - A VisualDataModel encapsulates a model and the delegate that will - be instantiated for items in the model. - - It is usually not necessary to create VisualDataModel elements. - However, it can be useful for manipulating and accessing the \l modelIndex - when a QAbstractItemModel subclass is used as the - model. Also, VisualDataModel is used together with \l Package to - provide delegates to multiple views. - - The example below illustrates using a VisualDataModel with a ListView. - - \snippet doc/src/snippets/declarative/visualdatamodel.qml 0 -*/ - -QSGVisualDataModelPrivate::QSGVisualDataModelPrivate(QDeclarativeContext *ctxt) - : m_adaptorModel(0) - , m_delegate(0) - , m_cacheMetaType(0) - , m_context(ctxt) - , m_parts(0) - , m_compositorGroup(Compositor::Cache) - , m_complete(false) - , m_delegateValidated(false) - , m_completePending(false) - , m_reset(false) - , m_transaction(false) - , m_filterGroup(QStringLiteral("items")) - , m_cacheItems(0) - , m_items(0) - , m_groupCount(3) -{ -} - -void QSGVisualDataModelPrivate::connectModel(QSGVisualAdaptorModel *model) -{ - Q_Q(QSGVisualDataModel); - - QObject::connect(model, SIGNAL(itemsInserted(int,int)), q, SLOT(_q_itemsInserted(int,int))); - QObject::connect(model, SIGNAL(itemsRemoved(int,int)), q, SLOT(_q_itemsRemoved(int,int))); - QObject::connect(model, SIGNAL(itemsMoved(int,int,int)), q, SLOT(_q_itemsMoved(int,int,int))); - QObject::connect(model, SIGNAL(itemsChanged(int,int)), q, SLOT(_q_itemsChanged(int,int))); - QObject::connect(model, SIGNAL(modelReset(int,int)), q, SLOT(_q_modelReset(int,int))); -} - -void QSGVisualDataModelPrivate::init() -{ - Q_Q(QSGVisualDataModel); - m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - - m_adaptorModel = new QSGVisualAdaptorModel; - QObject::connect(m_adaptorModel, SIGNAL(rootIndexChanged()), q, SIGNAL(rootIndexChanged())); - - m_items = new QSGVisualDataGroup(QStringLiteral("items"), q, Compositor::Default, q); - m_items->setDefaultInclude(true); - m_persistedItems = new QSGVisualDataGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QSGVisualDataGroupPrivate::get(m_items)->emitters.insert(this); -} - -QSGVisualDataModel::QSGVisualDataModel() -: QSGVisualModel(*(new QSGVisualDataModelPrivate(0))) -{ - Q_D(QSGVisualDataModel); - d->init(); -} - -QSGVisualDataModel::QSGVisualDataModel(QDeclarativeContext *ctxt, QObject *parent) -: QSGVisualModel(*(new QSGVisualDataModelPrivate(ctxt)), parent) -{ - Q_D(QSGVisualDataModel); - d->init(); -} - -QSGVisualDataModel::~QSGVisualDataModel() -{ - Q_D(QSGVisualDataModel); - foreach (QSGVisualDataModelCacheItem *cacheItem, d->m_cache) { - cacheItem->object = 0; - cacheItem->objectRef = 0; - if (!cacheItem->isReferenced()) - delete cacheItem; - } - - delete d->m_adaptorModel; - if (d->m_cacheMetaType) - d->m_cacheMetaType->release(); -} - - -void QSGVisualDataModel::classBegin() -{ -} - -void QSGVisualDataModel::componentComplete() -{ - Q_D(QSGVisualDataModel); - d->m_complete = true; - - int defaultGroups = 0; - QStringList groupNames; - groupNames.append(QStringLiteral("items")); - groupNames.append(QStringLiteral("persistedItems")); - if (QSGVisualDataGroupPrivate::get(d->m_items)->defaultInclude) - defaultGroups |= Compositor::DefaultFlag; - if (QSGVisualDataGroupPrivate::get(d->m_persistedItems)->defaultInclude) - defaultGroups |= Compositor::PersistedFlag; - for (int i = 3; i < d->m_groupCount; ++i) { - QString name = d->m_groups[i]->name(); - if (name.isEmpty()) { - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else if (name.at(0).isUpper()) { - qmlInfo(d->m_groups[i]) << QSGVisualDataGroup::tr("Group names must start with a lower case letter"); - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else { - groupNames.append(name); - - QSGVisualDataGroupPrivate *group = QSGVisualDataGroupPrivate::get(d->m_groups[i]); - group->setModel(this, Compositor::Group(i)); - if (group->defaultInclude) - defaultGroups |= (1 << i); - } - } - if (!d->m_context) - d->m_context = qmlContext(this); - - d->m_cacheMetaType = new QSGVisualDataModelCacheMetaType( - QDeclarativeEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); - - d->m_compositor.setGroupCount(d->m_groupCount); - d->m_compositor.setDefaultGroups(defaultGroups); - d->updateFilterGroup(); - - while (!d->m_pendingParts.isEmpty()) - static_cast(d->m_pendingParts.first())->updateFilterGroup(); - - d->connectModel(d->m_adaptorModel); - QVector inserts; - d->m_reset = true; - d->m_compositor.append( - d->m_adaptorModel, - 0, - qMax(0, d->m_adaptorModel->count()), - defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, - &inserts); - d->itemsInserted(inserts); - d->emitChanges(); - - if (d->m_adaptorModel->canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -} - -/*! - \qmlproperty model QtQuick2::VisualDataModel::model - This property holds the model providing data for the VisualDataModel. - - The model provides a set of data that is used to create the items - for a view. For large or dynamic datasets the model is usually - provided by a C++ model object. The C++ model object must be a \l - {QAbstractItemModel} subclass or a simple list. - - Models can also be created directly in QML, using a \l{ListModel} or - \l{XmlListModel}. - - \sa {qmlmodels}{Data Models} -*/ -QVariant QSGVisualDataModel::model() const -{ - Q_D(const QSGVisualDataModel); - return d->m_adaptorModel->model(); -} - -void QSGVisualDataModel::setModel(const QVariant &model) -{ - Q_D(QSGVisualDataModel); - d->m_adaptorModel->setModel(model, d->m_context ? d->m_context->engine() : qmlEngine(this)); - if (d->m_complete && d->m_adaptorModel->canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -} - -/*! - \qmlproperty Component QtQuick2::VisualDataModel::delegate - - The delegate provides a template defining each item instantiated by a view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qmlmodels}{Data Model}. -*/ -QDeclarativeComponent *QSGVisualDataModel::delegate() const -{ - Q_D(const QSGVisualDataModel); - return d->m_delegate; -} - -void QSGVisualDataModel::setDelegate(QDeclarativeComponent *delegate) -{ - Q_D(QSGVisualDataModel); - if (d->m_transaction) { - qmlInfo(this) << tr("The delegate of a VisualDataModel cannot be changed within onUpdated."); - return; - } - bool wasValid = d->m_delegate != 0; - d->m_delegate = delegate; - d->m_delegateValidated = false; - if (wasValid && d->m_complete) { - for (int i = 1; i < d->m_groupCount; ++i) { - QSGVisualDataGroupPrivate::get(d->m_groups[i])->changeSet.remove( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - if (d->m_complete && d->m_delegate) { - for (int i = 1; i < d->m_groupCount; ++i) { - QSGVisualDataGroupPrivate::get(d->m_groups[i])->changeSet.insert( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - d->emitChanges(); -} - -/*! - \qmlproperty QModelIndex QtQuick2::VisualDataModel::rootIndex - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. \c rootIndex allows the children of - any node in a QAbstractItemModel to be provided by this model. - - This property only affects models of type QAbstractItemModel that - are hierarchical (e.g, a tree model). - - For example, here is a simple interactive file system browser. - When a directory name is clicked, the view's \c rootIndex is set to the - QModelIndex node of the clicked directory, thus updating the view to show - the new directory's contents. - - \c main.cpp: - \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/main.cpp 0 - - \c view.qml: - \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/view.qml 0 - - If the \l model is a QAbstractItemModel subclass, the delegate can also - reference a \c hasModelChildren property (optionally qualified by a - \e model. prefix) that indicates whether the delegate's model item has - any child nodes. - - - \sa modelIndex(), parentModelIndex() -*/ -QVariant QSGVisualDataModel::rootIndex() const -{ - Q_D(const QSGVisualDataModel); - return d->m_adaptorModel->rootIndex(); -} - -void QSGVisualDataModel::setRootIndex(const QVariant &root) -{ - Q_D(QSGVisualDataModel); - d->m_adaptorModel->setRootIndex(root); -} - -/*! - \qmlmethod QModelIndex QtQuick2::VisualDataModel::modelIndex(int index) - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the specified index. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QSGVisualDataModel::modelIndex(int idx) const -{ - Q_D(const QSGVisualDataModel); - return d->m_adaptorModel->modelIndex(idx); -} - -/*! - \qmlmethod QModelIndex QtQuick2::VisualDataModel::parentModelIndex() - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the parent of the current rootIndex. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QSGVisualDataModel::parentModelIndex() const -{ - Q_D(const QSGVisualDataModel); - return d->m_adaptorModel->parentModelIndex(); -} - -/*! - \qmlproperty int QtQuick2::VisualDataModel::count -*/ - -int QSGVisualDataModel::count() const -{ - Q_D(const QSGVisualDataModel); - if (!d->m_delegate) - return 0; - return d->m_compositor.count(d->m_compositorGroup); -} - -void QSGVisualDataModelPrivate::destroy(QObject *object) -{ - QObjectPrivate *p = QObjectPrivate::get(object); - Q_ASSERT(p->declarativeData); - QDeclarativeData *data = static_cast(p->declarativeData); - if (data->ownContext && data->context) - data->context->clearContext(); - object->deleteLater(); -} - -QSGVisualDataModel::ReleaseFlags QSGVisualDataModelPrivate::release(QObject *object) -{ - QSGVisualDataModel::ReleaseFlags stat = 0; - if (!object) - return stat; - - int cacheIndex = cacheIndexOf(object); - if (cacheIndex != -1) { - QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); - if (cacheItem->releaseObject()) { - destroy(object); - cacheItem->object = 0; - stat |= QSGVisualModel::Destroyed; - if (!cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } - } else { - stat |= QSGVisualDataModel::Referenced; - } - } - return stat; -} - -/* - Returns ReleaseStatus flags. -*/ - -QSGVisualDataModel::ReleaseFlags QSGVisualDataModel::release(QSGItem *item) -{ - Q_D(QSGVisualDataModel); - QSGVisualModel::ReleaseFlags stat = d->release(item); - if (stat & Destroyed) - item->setParentItem(0); - return stat; -} - -void QSGVisualDataModelPrivate::group_append( - QDeclarativeListProperty *property, QSGVisualDataGroup *group) -{ - QSGVisualDataModelPrivate *d = static_cast(property->data); - if (d->m_complete) - return; - if (d->m_groupCount == 11) { - qmlInfo(d->q_func()) << QSGVisualDataModel::tr("The maximum number of supported VisualDataGroups is 8"); - return; - } - d->m_groups[d->m_groupCount] = group; - d->m_groupCount += 1; -} - -int QSGVisualDataModelPrivate::group_count( - QDeclarativeListProperty *property) -{ - QSGVisualDataModelPrivate *d = static_cast(property->data); - return d->m_groupCount - 1; -} - -QSGVisualDataGroup *QSGVisualDataModelPrivate::group_at( - QDeclarativeListProperty *property, int index) -{ - QSGVisualDataModelPrivate *d = static_cast(property->data); - return index >= 0 && index < d->m_groupCount - 1 - ? d->m_groups[index - 1] - : 0; -} - -/*! - \qmlproperty list QtQuick2::VisualDataModel::groups - - This property holds a visual data model's group definitions. - - Groups define a sub-set of the items in a visual data model and can be used to filter - a model. - - For every group defined in a VisualDataModel two attached properties are added to each - delegate item. The first of the form VisualDataModel.in\e{GroupName} holds whether the - item belongs to the group and the second VisualDataModel.\e{groupName}Index holds the - index of the item in that group. - - The following example illustrates using groups to select items in a model. - - \snippet doc/src/snippets/declarative/visualdatagroup.qml 0 -*/ - -QDeclarativeListProperty QSGVisualDataModel::groups() -{ - Q_D(QSGVisualDataModel); - return QDeclarativeListProperty( - this, - d, - QSGVisualDataModelPrivate::group_append, - QSGVisualDataModelPrivate::group_count, - QSGVisualDataModelPrivate::group_at); -} - -/*! - \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::items - - This property holds visual data model's default group to which all new items are added. -*/ - -QSGVisualDataGroup *QSGVisualDataModel::items() -{ - Q_D(QSGVisualDataModel); - return d->m_items; -} - -/*! - \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::persistedItems - - This property holds visual data model's persisted items group. - - Items in this group are not destroyed when released by a view, instead they are persisted - until removed from the group. - - An item can be removed from the persistedItems group by setting the - VisualDataModel.inPersistedItems property to false. If the item is not referenced by a view - at that time it will be destroyed. Adding an item to this group will not create a new - instance. - - Items returned by the \l QtQuick2::VisualDataGroup::create() function are automatically added - to this group. -*/ - -QSGVisualDataGroup *QSGVisualDataModel::persistedItems() -{ - Q_D(QSGVisualDataModel); - return d->m_persistedItems; -} - -/*! - \qmlproperty string QtQuick2::VisualDataModel::filterOnGroup - - This property holds the name of the group used to filter the visual data model. - - Only items which belong to this group are visible to a view. - - By default this is the \l items group. -*/ - -QString QSGVisualDataModel::filterGroup() const -{ - Q_D(const QSGVisualDataModel); - return d->m_filterGroup; -} - -void QSGVisualDataModel::setFilterGroup(const QString &group) -{ - Q_D(QSGVisualDataModel); - - if (d->m_transaction) { - qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); - return; - } - - if (d->m_filterGroup != group) { - d->m_filterGroup = group; - d->updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QSGVisualDataModel::resetFilterGroup() -{ - setFilterGroup(QStringLiteral("items")); -} - -void QSGVisualDataModelPrivate::updateFilterGroup() -{ - Q_Q(QSGVisualDataModel); - if (!m_cacheMetaType) - return; - - QDeclarativeListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - for (int i = 1; i < m_groupCount; ++i) { - if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QSGVisualDataGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QDeclarativeChangeSet changeSet; - changeSet.apply(removes, inserts); - emit q->modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit q->countChanged(); - - if (m_parts) { - foreach (QSGVisualPartsModel *model, m_parts->models) - model->updateFilterGroup(m_compositorGroup, changeSet); - } - } -} - -/*! - \qmlproperty object QtQuick2::VisualDataModel::parts - - The \a parts property selects a VisualDataModel which creates - delegates from the part named. This is used in conjunction with - the \l Package element. - - For example, the code below selects a model which creates - delegates named \e list from a \l Package: - - \code - VisualDataModel { - id: visualModel - delegate: Package { - Item { Package.name: "list" } - } - model: myModel - } - - ListView { - width: 200; height:200 - model: visualModel.parts.list - } - \endcode - - \sa Package -*/ - -QObject *QSGVisualDataModel::parts() -{ - Q_D(QSGVisualDataModel); - if (!d->m_parts) - d->m_parts = new QSGVisualDataModelParts(this); - return d->m_parts; -} - -void QSGVisualDataModelPrivate::emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QSGVisualDataGroupPrivate::get(m_groups[i])->createdPackage(at.index[i], package); -} - -void QSGVisualDataModelPrivate::emitDestroyingPackage(QDeclarativePackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QSGVisualDataGroupPrivate::get(m_groups[i])->destroyingPackage(package); -} - -QObject *QSGVisualDataModelPrivate::object(Compositor::Group group, int index, bool complete, bool reference) -{ - Q_Q(QSGVisualDataModel); - - Compositor::iterator it = m_compositor.find(group, index); - QSGVisualDataModelCacheItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - - if (!cacheItem) { - cacheItem = new QSGVisualDataModelCacheItem(m_cacheMetaType); - for (int i = 0; i < m_groupCount; ++i) - cacheItem->index[i] = it.index[i]; - cacheItem->groups = it->flags & Compositor::GroupMask; - } - - if (!cacheItem->object) { - QObject *data = m_adaptorModel->data(it.modelIndex()); - - QDeclarativeContext *creationContext = m_delegate->creationContext(); - QDeclarativeContext *rootContext = new QDeclarativeContext( - creationContext ? creationContext : m_context.data()); - QDeclarativeContext *ctxt = rootContext; - if (m_adaptorModel->flags() & QSGVisualAdaptorModel::ProxiedObject) { - if (QSGVisualAdaptorModelProxyInterface *proxy = qobject_cast(data)) { - ctxt->setContextObject(proxy->proxiedObject()); - ctxt = new QDeclarativeContext(ctxt, ctxt); - } - } - - QDeclarative_setParent_noEvent(data, ctxt); - ctxt->setContextProperty(QLatin1String("model"), data); - ctxt->setContextObject(data); - - m_completePending = false; - cacheItem->object = m_delegate->beginCreate(ctxt); - - if (cacheItem->object) { - QDeclarative_setParent_noEvent(rootContext, cacheItem->object); - if (!it->inCache()) { - m_cache.insert(it.cacheIndex, cacheItem); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } - - cacheItem->attached = QSGVisualDataModelAttached::properties(cacheItem->object); - cacheItem->attached->m_cacheItem = cacheItem; - new QSGVisualDataModelAttachedMetaObject(cacheItem->attached, m_cacheMetaType); - cacheItem->attached->emitChanges(); - - if (QDeclarativePackage *package = qobject_cast(cacheItem->object)) { - emitCreatedPackage(it, package); - } else if (!reference) { - if (QSGItem *item = qobject_cast(cacheItem->object)) - emitCreatedItem(it, item); - } - - m_completePending = !complete; - if (complete) - m_delegate->completeCreate(); - } else { - delete rootContext; - if (!it->inCache()) - delete cacheItem; - qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; - return 0; - } - } - - if (index == m_compositor.count(group) - 1 && m_adaptorModel->canFetchMore()) - QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - if (reference) - cacheItem->referenceObject(); - return cacheItem->object; -} - -QSGItem *QSGVisualDataModel::item(int index, bool complete) -{ - Q_D(QSGVisualDataModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "VisualDataModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return 0; - } - - QObject *object = d->object(d->m_compositorGroup, index, complete, true); - if (QSGItem *item = qobject_cast(object)) - return item; - if (d->m_completePending) - completeItem(); - d->release(object); - if (!d->m_delegateValidated) { - if (object) - qmlInfo(d->m_delegate) << QSGVisualDataModel::tr("Delegate component must be Item type."); - d->m_delegateValidated = true; - } - return 0; -} - -bool QSGVisualDataModel::completePending() const -{ - Q_D(const QSGVisualDataModel); - return d->m_completePending; -} - -void QSGVisualDataModel::completeItem() -{ - Q_D(QSGVisualDataModel); - d->m_delegate->completeCreate(); - d->m_completePending = false; -} - -QString QSGVisualDataModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) -{ - Compositor::iterator it = m_compositor.find(group, index); - if (QSGVisualAdaptorModel *model = it.list()) { - return model->stringValue(it.modelIndex(), name); - } - return QString(); -} - -QString QSGVisualDataModel::stringValue(int index, const QString &name) -{ - Q_D(QSGVisualDataModel); - return d->stringValue(d->m_compositorGroup, index, name); -} - -int QSGVisualDataModelPrivate::cacheIndexOf(QObject *object) const -{ - for (int cacheIndex = 0; cacheIndex < m_cache.count(); ++cacheIndex) { - if (m_cache.at(cacheIndex)->object == object) - return cacheIndex; - } - return -1; -} - -int QSGVisualDataModel::indexOf(QSGItem *item, QObject *) const -{ - Q_D(const QSGVisualDataModel); - const int cacheIndex = d->cacheIndexOf(item); - return cacheIndex != -1 - ? d->m_cache.at(cacheIndex)->index[d->m_compositorGroup] - : -1; -} - -void QSGVisualDataModel::setWatchedRoles(QList roles) -{ - Q_D(QSGVisualDataModel); - d->m_adaptorModel->replaceWatchedRoles(d->watchedRoles, roles); - d->watchedRoles = roles; -} - -void QSGVisualDataModelPrivate::addGroups(Compositor::Group group, int index, int count, int groupFlags) -{ - QVector inserts; - m_compositor.setFlags(group, index, count, groupFlags, &inserts); - itemsInserted(inserts); - emitChanges(); -} - -void QSGVisualDataModelPrivate::removeGroups(Compositor::Group group, int index, int count, int groupFlags) -{ - QVector removes; - m_compositor.clearFlags(group, index, count, groupFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -void QSGVisualDataModelPrivate::setGroups(Compositor::Group group, int index, int count, int groupFlags) -{ - QVector inserts; - m_compositor.setFlags(group, index, count, groupFlags, &inserts); - itemsInserted(inserts); - - const int removeFlags = ~groupFlags & Compositor::GroupMask; - QVector removes; - m_compositor.clearFlags(group, index, count, removeFlags, &removes); - itemsRemoved(removes); - - emitChanges(); -} - -bool QSGVisualDataModel::event(QEvent *e) -{ - Q_D(QSGVisualDataModel); - if (e->type() == QEvent::UpdateRequest) - d->m_adaptorModel->fetchMore(); - return QSGVisualModel::event(e); -} - -void QSGVisualDataModelPrivate::itemsChanged(const QVector &changes) -{ - if (!m_delegate) - return; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); - - foreach (const Compositor::Change &change, changes) { - for (int i = 1; i < m_groupCount; ++i) { - if (change.inGroup(i)) { - translatedChanges[i].append( - QDeclarativeChangeSet::Change(change.index[i], change.count)); - } - } - } - - for (int i = 1; i < m_groupCount; ++i) - QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedChanges.at(i)); -} - -void QSGVisualDataModel::_q_itemsChanged(int index, int count) -{ - Q_D(QSGVisualDataModel); - if (count <= 0) - return; - QVector changes; - d->m_compositor.listItemsChanged(d->m_adaptorModel, index, count, &changes); - d->itemsChanged(changes); - d->emitChanges(); -} - -void QSGVisualDataModelPrivate::itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems) -{ - int cacheIndex = 0; - - int inserted[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - inserted[i] = 0; - - foreach (const Compositor::Insert &insert, inserts) { - for (; cacheIndex < insert.cacheIndex; ++cacheIndex) { - QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); - if (!cacheItem->groups) - continue; - for (int i = 1; i < m_groupCount; ++i) - cacheItem->index[i] += inserted[i]; - } - for (int i = 1; i < m_groupCount; ++i) { - if (insert.inGroup(i)) { - (*translatedInserts)[i].append( - QDeclarativeChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); - inserted[i] += insert.count; - } - } - - if (!insert.inCache()) - continue; - - if (movedItems && insert.isMove()) { - QList items = movedItems->take(insert.moveId); - Q_ASSERT(items.count() == insert.count); - m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); - } - if (insert.inGroup()) { - for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { - QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); - cacheItem->groups |= insert.flags & Compositor::GroupMask; - for (int i = 1; i < m_groupCount; ++i) { - cacheItem->index[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - } - } else { - cacheIndex = insert.cacheIndex + insert.count; - } - } - for (; cacheIndex < m_cache.count(); ++cacheIndex) { - QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); - if (!cacheItem->groups) - continue; - for (int i = 1; i < m_groupCount; ++i) - cacheItem->index[i] += inserted[i]; - } -} - -void QSGVisualDataModelPrivate::itemsInserted(const QVector &inserts) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedInserts.at(i)); -} - -void QSGVisualDataModel::_q_itemsInserted(int index, int count) -{ - - Q_D(QSGVisualDataModel); - if (count <= 0) - return; - QVector inserts; - d->m_compositor.listItemsInserted(d->m_adaptorModel, index, count, &inserts); - d->itemsInserted(inserts); - d->emitChanges(); -} - -void QSGVisualDataModelPrivate::itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems) -{ - int cacheIndex = 0; - int removedCache = 0; - - int removed[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - removed[i] = 0; - - foreach (const Compositor::Remove &remove, removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) { - QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); - if (!cacheItem->groups) - continue; - for (int i = 1; i < m_groupCount; ++i) - cacheItem->index[i] -= removed[i]; - } - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) { - (*translatedRemoves)[i].append( - QDeclarativeChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); - removed[i] += remove.count; - } - } - - if (!remove.inCache()) - continue; - - if (movedItems && remove.isMove()) { - movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); - QList::iterator begin = m_cache.begin() + remove.cacheIndex; - QList::iterator end = begin + remove.count; - m_cache.erase(begin, end); - } else { - for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { - QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); - if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0) { - destroy(cacheItem->object); - if (QDeclarativePackage *package = qobject_cast(cacheItem->object)) - emitDestroyingPackage(package); - else if (QSGItem *item = qobject_cast(cacheItem->object)) - emitDestroyingItem(item); - cacheItem->object = 0; - } - if (remove.groups() == cacheItem->groups && !cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - --cacheIndex; - ++removedCache; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } else if (remove.groups() == cacheItem->groups) { - cacheItem->groups = 0; - for (int i = 1; i < m_groupCount; ++i) - cacheItem->index[i] = -1; - } else { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - cacheItem->index[i] = remove.index[i]; - } - cacheItem->groups &= ~remove.flags & Compositor::GroupMask; - } - } - } - } - - for (; cacheIndex < m_cache.count(); ++cacheIndex) { - QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex); - if (!cacheItem->groups) - continue; - for (int i = 1; i < m_groupCount; ++i) - cacheItem->index[i] -= removed[i]; - } -} - -void QSGVisualDataModelPrivate::itemsRemoved(const QVector &removes) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedRemoves.at(i)); -} - -void QSGVisualDataModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QSGVisualDataModel); - if (count <= 0) - return; - - QVector removes; - d->m_compositor.listItemsRemoved(d->m_adaptorModel, index, count, &removes); - d->itemsRemoved(removes); - d->emitChanges(); -} - -void QSGVisualDataModelPrivate::itemsMoved( - const QVector &removes, const QVector &inserts) -{ - QHash > movedItems; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves, &movedItems); - - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts, &movedItems); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - Q_ASSERT(movedItems.isEmpty()); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) { - QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply( - translatedRemoves.at(i), - translatedInserts.at(i)); - } -} - -void QSGVisualDataModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QSGVisualDataModel); - if (count <= 0) - return; - - QVector removes; - QVector inserts; - d->m_compositor.listItemsMoved(d->m_adaptorModel, from, to, count, &removes, &inserts); - d->itemsMoved(removes, inserts); - d->emitChanges(); -} - -template v8::Local -QSGVisualDataModelPrivate::buildChangeList(const QVector &changes) -{ - v8::Local indexes = v8::Array::New(changes.count()); - v8::Local indexKey = v8::String::New("index"); - v8::Local countKey = v8::String::New("count"); - v8::Local moveIdKey = v8::String::New("moveId"); - - for (int i = 0; i < changes.count(); ++i) { - v8::Local object = v8::Object::New(); - object->Set(indexKey, v8::Integer::New(changes.at(i).index)); - object->Set(countKey, v8::Integer::New(changes.at(i).count)); - object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); - indexes->Set(i, object); - } - return indexes; -} - -void QSGVisualDataModelPrivate::emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) -{ - Q_Q(QSGVisualDataModel); - emit q->modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QSGVisualDataModelPrivate::emitChanges() -{ - if (m_transaction || !m_complete) - return; - - m_transaction = true; - QV8Engine *engine = QDeclarativeEnginePrivate::getV8Engine(m_context->engine()); - for (int i = 1; i < m_groupCount; ++i) - QSGVisualDataGroupPrivate::get(m_groups[i])->emitChanges(engine); - m_transaction = false; - - const bool reset = m_reset; - m_reset = false; - for (int i = 1; i < m_groupCount; ++i) - QSGVisualDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - - foreach (QSGVisualDataModelCacheItem *cacheItem, m_cache) { - if (cacheItem->object) - cacheItem->attached->emitChanges(); - } -} - -void QSGVisualDataModel::_q_modelReset(int oldCount, int newCount) -{ - Q_D(QSGVisualDataModel); - if (!d->m_delegate) - return; - - QVector removes; - QVector inserts; - if (oldCount) - d->m_compositor.listItemsRemoved(d->m_adaptorModel, 0, oldCount, &removes); - if (newCount) - d->m_compositor.listItemsInserted(d->m_adaptorModel, 0, newCount, &inserts); - d->itemsMoved(removes, inserts); - d->m_reset = true; - d->emitChanges(); -} - -QSGVisualDataModelAttached *QSGVisualDataModel::qmlAttachedProperties(QObject *obj) -{ - return QSGVisualDataModelAttached::properties(obj); -} - -//============================================================================ - -QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType( - QV8Engine *engine, QSGVisualDataModel *model, const QStringList &groupNames) - : model(model) - , groupCount(groupNames.count() + 1) - , memberPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount()) - , indexPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount() + groupNames.count()) - , v8Engine(engine) - , metaObject(0) - , groupNames(groupNames) -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName(QSGVisualDataModelAttached::staticMetaObject.className()); - builder.setSuperClass(&QSGVisualDataModelAttached::staticMetaObject); - - v8::HandleScope handleScope; - v8::Context::Scope contextScope(engine->context()); - v8::Local ft = v8::FunctionTemplate::New(); - ft->InstanceTemplate()->SetHasExternalResource(true); - ft->PrototypeTemplate()->SetAccessor(v8::String::New("model"), get_model); - ft->PrototypeTemplate()->SetAccessor(v8::String::New("groups"), get_groups, set_groups); - - int notifierId = 0; - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "bool", notifierId); - propertyBuilder.setWritable(true); - - ft->PrototypeTemplate()->SetAccessor( - engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); - } - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "int", notifierId); - propertyBuilder.setWritable(true); - - ft->PrototypeTemplate()->SetAccessor( - engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); - } - - metaObject = builder.toMetaObject(); - - constructor = qPersistentNew(ft->GetFunction()); -} - -QSGVisualDataModelCacheMetaType::~QSGVisualDataModelCacheMetaType() -{ - qFree(metaObject); - qPersistentDispose(constructor); -} - -int QSGVisualDataModelCacheMetaType::parseGroups(const QStringList &groups) const -{ - int groupFlags = 0; - foreach (const QString &groupName, groups) { - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - return groupFlags; -} - -int QSGVisualDataModelCacheMetaType::parseGroups(QV8Engine *engine, const v8::Local &groups) const -{ - int groupFlags = 0; - if (groups->IsString()) { - const QString groupName = engine->toString(groups); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } else if (groups->IsArray()) { - v8::Local array = v8::Local::Cast(groups); - for (uint i = 0; i < array->Length(); ++i) { - const QString groupName = engine->toString(array->Get(i)); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - } - return groupFlags; -} - -v8::Handle QSGVisualDataModelCacheMetaType::get_model( - v8::Local, const v8::AccessorInfo &info) -{ - QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); - if (!cacheItem) - V8THROW_ERROR("Not a valid VisualData object"); - if (!cacheItem->metaType->model) - return v8::Undefined(); - QObject *data = 0; - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model); - for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { - if (cacheItem->groups & (1 << i)) { - Compositor::iterator it = model->m_compositor.find( - Compositor::Group(i), cacheItem->index[i]); - if (QSGVisualAdaptorModel *list = it.list()) - data = list->data(it.modelIndex()); - break; - } - } - if (!data) - return v8::Undefined(); - return cacheItem->engine->newQObject(data); -} - -v8::Handle QSGVisualDataModelCacheMetaType::get_groups( - v8::Local, const v8::AccessorInfo &info) -{ - QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); - if (!cacheItem) - V8THROW_ERROR("Not a valid VisualData object"); - - QStringList groups; - for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { - if (cacheItem->groups & (1 << i)) - groups.append(cacheItem->metaType->groupNames.at(i - 1)); - } - - return cacheItem->engine->fromVariant(groups); -} - -void QSGVisualDataModelCacheMetaType::set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); - if (!cacheItem) - V8THROW_ERROR_SETTER("Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(cacheItem->engine, value); - for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { - if (cacheItem->groups & (1 << i)) { - model->setGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlags); - break; - } - } -} - -v8::Handle QSGVisualDataModelCacheMetaType::get_member( - v8::Local, const v8::AccessorInfo &info) -{ - QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); - if (!cacheItem) - V8THROW_ERROR("Not a valid VisualData object"); - - return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); -} - -void QSGVisualDataModelCacheMetaType::set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); - if (!cacheItem) - V8THROW_ERROR_SETTER("Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model); - - Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); - const bool member = value->BooleanValue(); - const int groupFlag = (1 << group); - if (member == ((cacheItem->groups & groupFlag) != 0)) - return; - - for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { - if (cacheItem->groups & (1 << i)) { - if (member) - model->addGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag); - else - model->removeGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag); - break; - } - } -} - -v8::Handle QSGVisualDataModelCacheMetaType::get_index( - v8::Local, const v8::AccessorInfo &info) -{ - QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast(info.This()); - if (!cacheItem) - V8THROW_ERROR("Not a valid VisualData object"); - - return v8::Integer::New(cacheItem->index[info.Data()->Int32Value()]); -} - - -//--------------------------------------------------------------------------- - -void QSGVisualDataModelCacheItem::Dispose() -{ - --scriptRef; - if (isReferenced()) - return; - - if (metaType->model) { - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(metaType->model); - const int cacheIndex = model->m_cache.indexOf(this); - if (cacheIndex != -1) { - model->m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - model->m_cache.removeAt(cacheIndex); - } - } - delete this; -} - -//--------------------------------------------------------------------------- - -QSGVisualDataModelAttachedMetaObject::QSGVisualDataModelAttachedMetaObject( - QSGVisualDataModelAttached *attached, QSGVisualDataModelCacheMetaType *metaType) - : attached(attached) - , metaType(metaType) -{ - metaType->addref(); - *static_cast(this) = *metaType->metaObject; - QObjectPrivate::get(attached)->metaObject = this; -} - -QSGVisualDataModelAttachedMetaObject::~QSGVisualDataModelAttachedMetaObject() -{ - metaType->release(); -} - -int QSGVisualDataModelAttachedMetaObject::metaCall(QMetaObject::Call call, int _id, void **arguments) -{ - if (call == QMetaObject::ReadProperty) { - if (_id >= metaType->indexPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - metaType->indexPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_cacheItem->index[group]; - return -1; - } else if (_id >= metaType->memberPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - metaType->memberPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); - return -1; - } - } else if (call == QMetaObject::WriteProperty) { - if (_id >= metaType->memberPropertyOffset) { - if (!metaType->model) - return -1; - Compositor::Group group = Compositor::Group(_id - metaType->memberPropertyOffset + 1); - const bool member = attached->m_cacheItem->groups & (1 << group); - if (member != *static_cast(arguments[0])) { - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(metaType->model); - const int cacheIndex = model->m_cache.indexOf(attached->m_cacheItem); - if (member) - model->removeGroups(Compositor::Cache, cacheIndex, 1, (1 << group)); - else - model->addGroups(Compositor::Cache, cacheIndex, 1, (1 << group)); - } - return -1; - } - } - return attached->qt_metacall(call, _id, arguments); -} - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::model - - This attached property holds the visual data model this delegate instance belongs to. - - It is attached to each instance of the delegate. -*/ - -QSGVisualDataModel *QSGVisualDataModelAttached::model() const -{ - return m_cacheItem ? m_cacheItem->metaType->model : 0; -} - -/*! - \qmlattachedproperty stringlist QtQuick2::VisualDataModel::groups - - This attached property holds the name of VisualDataGroups the item belongs to. - - It is attached to each instance of the delegate. -*/ - -QStringList QSGVisualDataModelAttached::groups() const -{ - QStringList groups; - - if (!m_cacheItem) - return groups; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_cacheItem->groups & (1 << i)) - groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); - } - return groups; -} - -void QSGVisualDataModelAttached::setGroups(const QStringList &groups) -{ - if (!m_cacheItem) - return; - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_cacheItem->metaType->model); - - const int cacheIndex = model->m_cache.indexOf(m_cacheItem); - const int groupFlags = model->m_cacheMetaType->parseGroups(groups); - model->setGroups(Compositor::Cache, cacheIndex, 1, groupFlags); -} - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::inItems - - This attached property holds whether the item belongs to the default \l items VisualDataGroup. - - Changing this property will add or remove the item from the items group. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::itemsIndex - - This attached property holds the index of the item in the default \l items VisualDataGroup. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::inPersistedItems - - This attached property holds whether the item belongs to the \l persistedItems VisualDataGroup. - - Changing this property will add or remove the item from the items group. Change with caution - as removing an item from the persistedItems group will destroy the current instance if it is - not referenced by a model. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::persistedItemsIndex - - This attached property holds the index of the item in the \l persistedItems VisualDataGroup. - - It is attached to each instance of the delegate. -*/ - -void QSGVisualDataModelAttached::emitChanges() -{ - if (m_modelChanged) { - m_modelChanged = false; - emit modelChanged(); - } - - const int groupChanges = m_previousGroups ^ m_cacheItem->groups; - m_previousGroups = m_cacheItem->groups; - - int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_previousIndex[i] != m_cacheItem->index[i]) { - m_previousIndex[i] = m_cacheItem->index[i]; - indexChanges |= (1 << i); - } - } - - int notifierId = 0; - const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (groupChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (indexChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - - if (groupChanges) - emit groupsChanged(); -} - -//============================================================================ - -void QSGVisualDataGroupPrivate::setModel(QSGVisualDataModel *m, Compositor::Group g) -{ - Q_ASSERT(!model); - model = m; - group = g; -} - -void QSGVisualDataGroupPrivate::emitChanges(QV8Engine *engine) -{ - Q_Q(QSGVisualDataGroup); - static int idx = signalIndex("changed(QDeclarativeV8Handle,QDeclarativeV8Handle)"); - if (isSignalConnected(idx)) { - v8::HandleScope handleScope; - v8::Context::Scope contextScope(engine->context()); - v8::Local removed = QSGVisualDataModelPrivate::buildChangeList(changeSet.removes()); - v8::Local inserted = QSGVisualDataModelPrivate::buildChangeList(changeSet.inserts()); - emit q->changed( - QDeclarativeV8Handle::fromHandle(removed), QDeclarativeV8Handle::fromHandle(inserted)); - } - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QSGVisualDataGroupPrivate::emitModelUpdated(bool reset) -{ - for (QSGVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->emitModelUpdated(changeSet, reset); - changeSet.clear(); -} - -void QSGVisualDataGroupPrivate::createdPackage(int index, QDeclarativePackage *package) -{ - for (QSGVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->createdPackage(index, package); -} - -void QSGVisualDataGroupPrivate::destroyingPackage(QDeclarativePackage *package) -{ - for (QSGVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->destroyingPackage(package); -} - -/*! - \qmlclass VisualDataGroup QSGVisualDataGroup - \inqmlmodule QtQuick 2 - \ingroup qml-working-with-data - \brief The VisualDataGroup encapsulates a filtered set of visual data items. - -*/ - -QSGVisualDataGroup::QSGVisualDataGroup(QObject *parent) - : QObject(*new QSGVisualDataGroupPrivate, parent) -{ -} - -QSGVisualDataGroup::QSGVisualDataGroup( - const QString &name, QSGVisualDataModel *model, int index, QObject *parent) - : QObject(*new QSGVisualDataGroupPrivate, parent) -{ - Q_D(QSGVisualDataGroup); - d->name = name; - d->setModel(model, Compositor::Group(index)); -} - -QSGVisualDataGroup::~QSGVisualDataGroup() -{ -} - -/*! - \qmlproperty string QtQuick2::VisualDataGroup::name - - This property holds the name of the group. - - Each group in a model must have a unique name starting with a lower case letter. -*/ - -QString QSGVisualDataGroup::name() const -{ - Q_D(const QSGVisualDataGroup); - return d->name; -} - -void QSGVisualDataGroup::setName(const QString &name) -{ - Q_D(QSGVisualDataGroup); - if (d->model) - return; - if (d->name != name) { - d->name = name; - emit nameChanged(); - } -} - -/*! - \qmlproperty int QtQuick2::VisualDataGroup::count - - This property holds the number of items in the group. -*/ - -int QSGVisualDataGroup::count() const -{ - Q_D(const QSGVisualDataGroup); - if (!d->model) - return 0; - return QSGVisualDataModelPrivate::get(d->model)->m_compositor.count(d->group); -} - -/*! - \qmlproperty bool QtQuick2::VisualDataGroup::includeByDefault - - This property holds whether new items are assigned to this group by default. -*/ - -bool QSGVisualDataGroup::defaultInclude() const -{ - Q_D(const QSGVisualDataGroup); - return d->defaultInclude; -} - -void QSGVisualDataGroup::setDefaultInclude(bool include) -{ - Q_D(QSGVisualDataGroup); - if (d->defaultInclude != include) { - d->defaultInclude = include; - - if (d->model) { - if (include) - QSGVisualDataModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); - else - QSGVisualDataModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); - } - emit defaultIncludeChanged(); - } -} - -/*! - \qmlmethod var QtQuick2::VisualDataGroup::get(int index) - - Returns a javascript object describing the item at \a index in the group. - - The returned object contains the same information that is available to a delegate from the - VisualDataModel attached as well as the model for that item. It has the properties: - - \list - \o \b model The model data of the item. This is the same as the model context property in - a delegate - \o \b groups A list the of names of groups the item is a member of. This property can be - written to change the item's membership. - \o \b inItems Whether the item belongs to the \l {QtQuick2::VisualDataModel::items}{items} group. - Writing to this property will add or remove the item from the group. - \o \b itemsIndex The index of the item within the \l {QtQuick2::VisualDataModel::items}{items} group. - \o \b {in\i{GroupName}} Whether the item belongs to the dynamic group \i groupName. Writing to - this property will add or remove the item from the group. - \o \b {\i{groupName}Index} The index of the item within the dynamic group \i groupName. - \endlist -*/ - -QDeclarativeV8Handle QSGVisualDataGroup::get(int index) -{ - Q_D(QSGVisualDataGroup); - if (!d->model) - return QDeclarativeV8Handle::fromHandle(v8::Undefined());; - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("get: index out of range"); - return QDeclarativeV8Handle::fromHandle(v8::Undefined()); - } - - Compositor::iterator it = model->m_compositor.find(d->group, index); - QSGVisualDataModelCacheItem *cacheItem = it->inCache() - ? model->m_cache.at(it.cacheIndex) - : 0; - - if (!cacheItem) { - cacheItem = new QSGVisualDataModelCacheItem(model->m_cacheMetaType); - for (int i = 0; i < model->m_groupCount; ++i) - cacheItem->index[i] = it.index[i]; - cacheItem->groups = it->flags & Compositor::GroupMask; - - model->m_cache.insert(it.cacheIndex, cacheItem); - model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); - } - - ++cacheItem->scriptRef; - - v8::Local rv = model->m_cacheMetaType->constructor->NewInstance(); - rv->SetExternalResource(cacheItem); - return QDeclarativeV8Handle::fromHandle(rv); -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::create(int index) - - Returns a reference to the instantiated item at \a index in the group. - - All items returned by create are added to the persistedItems group. Items in this - group remain instantiated when not referenced by any view. -*/ - -QObject *QSGVisualDataGroup::create(int index) -{ - Q_D(QSGVisualDataGroup); - if (!d->model) - return 0; - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("create: index out of range"); - return 0; - } - - QObject *object = model->object(d->group, index, true, false); - if (object) - model->addGroups(d->group, index, 1, Compositor::PersistedFlag); - return object; -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count) - - Removes \a count items starting at \a index from the group. -*/ - -void QSGVisualDataGroup::remove(QDeclarativeV8Function *args) -{ - Q_D(QSGVisualDataGroup); - if (!d->model) - return; - int index = -1; - int count = 1; - - if (args->Length() == 0) - return; - - int i = 0; - v8::Local v = (*args)[i]; - if (!v->IsInt32()) - return; - index = v->Int32Value(); - - if (++i < args->Length()) { - v = (*args)[i]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); - if (count < 0) { - qmlInfo(this) << tr("remove: invalid count"); - } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("remove: index out of range"); - } else if (count > 0) { - model->removeGroups(d->group, index, count, 1 << d->group); - } -} - -bool QSGVisualDataGroupPrivate::parseGroupArgs( - QDeclarativeV8Function *args, int *index, int *count, int *groups) const -{ - if (!model) - return false; - - if (args->Length() < 2) - return false; - - int i = 0; - v8::Local v = (*args)[i]; - if (!v->IsInt32()) - return false; - *index = v->Int32Value(); - - v = (*args)[++i]; - if (v->IsInt32()) { - *count = v->Int32Value(); - - if (++i == args->Length()) - return false; - v = (*args)[i]; - } - - *groups = QSGVisualDataModelPrivate::get(model)->m_cacheMetaType->parseGroups(args->engine(), v); - - return true; -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::addGroups(int index, int count, stringlist groups) - - Adds \a count items starting at \a index to \a groups. -*/ - -void QSGVisualDataGroup::addGroups(QDeclarativeV8Function *args) -{ - Q_D(QSGVisualDataGroup); - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &index, &count, &groups)) - return; - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); - if (count < 0) { - qmlInfo(this) << tr("addGroups: invalid count"); - } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("addGroups: index out of range"); - } else if (count > 0 && groups) { - model->addGroups(d->group, index, count, groups); - } -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::removeGroups(int index, int count, stringlist groups) - - Removes \a count items starting at \a index from \a groups. -*/ - -void QSGVisualDataGroup::removeGroups(QDeclarativeV8Function *args) -{ - Q_D(QSGVisualDataGroup); - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &index, &count, &groups)) - return; - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); - if (count < 0) { - qmlInfo(this) << tr("removeGroups: invalid count"); - } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("removeGroups: index out of range"); - } else if (count > 0 && groups) { - model->removeGroups(d->group, index, count, groups); - } -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -void QSGVisualDataGroup::setGroups(QDeclarativeV8Function *args) -{ - Q_D(QSGVisualDataGroup); - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &index, &count, &groups)) - return; - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); - if (count < 0) { - qmlInfo(this) << tr("setGroups: invalid count"); - } else if (index < 0 || index + count > model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("setGroups: index out of range"); - } else if (count > 0) { - model->setGroups(d->group, index, count, groups); - } -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -/*! - \qmlmethod QtQuick2::VisualDataGroup::move(int from, int to, int count) - - Moves \a count at \a from in a group \a to a new position. -*/ - -void QSGVisualDataGroup::move(QDeclarativeV8Function *args) -{ - Q_D(QSGVisualDataGroup); - - if (args->Length() < 2) - return; - - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - int from = -1; - int to = -1; - int count = 1; - - int i = 0; - v8::Local v = (*args)[i]; - if (QSGVisualDataGroup *group = qobject_cast(args->engine()->toQObject(v))) { - QSGVisualDataGroupPrivate *g_d = QSGVisualDataGroupPrivate::get(group); - if (g_d->model != d->model) - return; - fromGroup = g_d->group; - v = (*args)[++i]; - } - - if (!v->IsInt32()) - return; - from = v->Int32Value(); - - if (++i == args->Length()) - return; - v = (*args)[i]; - - if (QSGVisualDataGroup *group = qobject_cast(args->engine()->toQObject(v))) { - QSGVisualDataGroupPrivate *g_d = QSGVisualDataGroupPrivate::get(group); - if (g_d->model != d->model) - return; - toGroup = g_d->group; - - if (++i == args->Length()) - return; - v = (*args)[i]; - } - - if (!v->IsInt32()) - return; - to = v->Int32Value(); - - if (++i < args->Length()) { - v = (*args)[i]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model); - - if (count < 0) { - qmlInfo(this) << tr("move: invalid count"); - } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { - qmlInfo(this) << tr("move: from index out of range"); - } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count)) { - qmlInfo(this) << tr("move: to index out of range"); - } else if (count > 0) { - QVector removes; - QVector inserts; - - model->m_compositor.move(fromGroup, from, toGroup, to, count, &removes, &inserts); - model->itemsMoved(removes, inserts); - model->emitChanges(); - } -} - -/*! - \qmlsignal QtQuick2::VisualDataGroup::onChanged(array removed, array inserted) - - This handler is called when items have been removed from or inserted into the group. - - Each object in the \a removed and \a inserted arrays has two values; the \e index of the first - item inserted or removed and a \e count of the number of consecutive items inserted or removed. - - Each index is adjusted for previous changes with all removed items preceding any inserted - items. -*/ - -//============================================================================ - -QSGVisualPartsModel::QSGVisualPartsModel(QSGVisualDataModel *model, const QString &part, QObject *parent) - : QSGVisualModel(*new QObjectPrivate, parent) - , m_model(model) - , m_part(part) - , m_compositorGroup(Compositor::Cache) - , m_inheritGroup(true) -{ - QSGVisualDataModelPrivate *d = QSGVisualDataModelPrivate::get(m_model); - if (d->m_cacheMetaType) { - QSGVisualDataGroupPrivate::get(d->m_groups[1])->emitters.insert(this); - m_compositorGroup = Compositor::Default; - } else { - d->m_pendingParts.insert(this); - } -} - -QSGVisualPartsModel::~QSGVisualPartsModel() -{ -} - -QString QSGVisualPartsModel::filterGroup() const -{ - if (m_inheritGroup) - return m_model->filterGroup(); - return m_filterGroup; -} - -void QSGVisualPartsModel::setFilterGroup(const QString &group) -{ - if (QSGVisualDataModelPrivate::get(m_model)->m_transaction) { - qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); - return; - } - - if (m_filterGroup != group || m_inheritGroup) { - m_filterGroup = group; - m_inheritGroup = false; - updateFilterGroup(); - - emit filterGroupChanged(); - } -} - -void QSGVisualPartsModel::resetFilterGroup() -{ - if (!m_inheritGroup) { - m_inheritGroup = true; - updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QSGVisualPartsModel::updateFilterGroup() -{ - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); - if (!model->m_cacheMetaType) - return; - - if (m_inheritGroup) - return; - - QDeclarativeListCompositor::Group previousGroup = model->m_compositorGroup; - m_compositorGroup = Compositor::Default; - QSGVisualDataGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); - for (int i = 1; i < model->m_groupCount; ++i) { - if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QSGVisualDataGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QDeclarativeChangeSet changeSet; - changeSet.apply(removes, inserts); - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - } -} - -void QSGVisualPartsModel::updateFilterGroup( - Compositor::Group group, const QDeclarativeChangeSet &changeSet) -{ - if (!m_inheritGroup) - return; - - m_compositorGroup = group; - QSGVisualDataGroupPrivate::get(QSGVisualDataModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); - - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - - emit filterGroupChanged(); -} - -int QSGVisualPartsModel::count() const -{ - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); - return model->m_delegate - ? model->m_compositor.count(m_compositorGroup) - : 0; -} - -bool QSGVisualPartsModel::isValid() const -{ - return m_model->isValid(); -} - -QSGItem *QSGVisualPartsModel::item(int index, bool complete) -{ - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); - - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "VisualDataModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); - return 0; - } - - QObject *object = model->object(m_compositorGroup, index, complete, true); - - if (QDeclarativePackage *package = qobject_cast(object)) { - QObject *part = package->part(m_part); - if (!part) - return 0; - if (QSGItem *item = qobject_cast(part)) { - m_packaged.insertMulti(item, package); - return item; - } - } - - if (m_model->completePending()) - m_model->completeItem(); - model->release(object); - if (!model->m_delegateValidated) { - if (object) - qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); - model->m_delegateValidated = true; - } - - return 0; -} - -QSGVisualModel::ReleaseFlags QSGVisualPartsModel::release(QSGItem *item) -{ - QSGVisualModel::ReleaseFlags flags = 0; - - QHash::iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - QDeclarativePackage *package = *it; - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); - flags = model->release(package); - m_packaged.erase(it); - if (!m_packaged.contains(item)) - flags &= ~Referenced; - if (flags & Destroyed) - QSGVisualDataModelPrivate::get(m_model)->emitDestroyingPackage(package); - } - return flags; -} - -bool QSGVisualPartsModel::completePending() const -{ - return m_model->completePending(); -} - -void QSGVisualPartsModel::completeItem() -{ - m_model->completeItem(); -} - -QString QSGVisualPartsModel::stringValue(int index, const QString &role) -{ - return QSGVisualDataModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); -} - -void QSGVisualPartsModel::setWatchedRoles(QList roles) -{ - QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); - model->m_adaptorModel->replaceWatchedRoles(m_watchedRoles, roles); - m_watchedRoles = roles; -} - -int QSGVisualPartsModel::indexOf(QSGItem *item, QObject *) const -{ - QHash::const_iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - const QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); - const int cacheIndex = model->cacheIndexOf(*it); - return cacheIndex != -1 - ? model->m_cache.at(cacheIndex)->index[m_compositorGroup] - : -1; - } - return -1; -} - -void QSGVisualPartsModel::createdPackage(int index, QDeclarativePackage *package) -{ - if (QSGItem *item = qobject_cast(package->part(m_part))) - emit createdItem(index, item); -} - -void QSGVisualPartsModel::destroyingPackage(QDeclarativePackage *package) -{ - if (QSGItem *item = qobject_cast(package->part(m_part))) { - Q_ASSERT(!m_packaged.contains(item)); - emit destroyingItem(item); - } -} - -void QSGVisualPartsModel::emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) -{ - emit modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit countChanged(); -} - - -QT_END_NAMESPACE - -#include diff --git a/src/declarative/items/qsgvisualdatamodel_p.h b/src/declarative/items/qsgvisualdatamodel_p.h deleted file mode 100644 index 896c51c22a..0000000000 --- a/src/declarative/items/qsgvisualdatamodel_p.h +++ /dev/null @@ -1,242 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGVISUALDATAMODEL_P_H -#define QSGVISUALDATAMODEL_P_H - -#include -#include - - -#include -#include - -#include - -QT_BEGIN_HEADER - -Q_DECLARE_METATYPE(QModelIndex) - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - - -class QDeclarativeChangeSet; -class QDeclarativeComponent; -class QDeclarativePackage; -class QDeclarativeV8Function; -class QSGVisualDataGroup; -class QSGVisualDataModelAttached; -class QSGVisualDataModelPrivate; - - -class Q_DECLARATIVE_EXPORT QSGVisualDataModel : public QSGVisualModel, public QDeclarativeParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGVisualDataModel) - - Q_PROPERTY(QVariant model READ model WRITE setModel) - Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QSGVisualDataGroup *items READ items CONSTANT) - Q_PROPERTY(QSGVisualDataGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QDeclarativeListProperty groups READ groups CONSTANT) - Q_PROPERTY(QObject *parts READ parts CONSTANT) - Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - Q_INTERFACES(QDeclarativeParserStatus) -public: - QSGVisualDataModel(); - QSGVisualDataModel(QDeclarativeContext *, QObject *parent=0); - virtual ~QSGVisualDataModel(); - - void classBegin(); - void componentComplete(); - - QVariant model() const; - void setModel(const QVariant &); - - QDeclarativeComponent *delegate() const; - void setDelegate(QDeclarativeComponent *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - Q_INVOKABLE QVariant modelIndex(int idx) const; - Q_INVOKABLE QVariant parentModelIndex() const; - - int count() const; - bool isValid() const { return delegate() != 0; } - QSGItem *item(int index, bool complete=true); - ReleaseFlags release(QSGItem *item); - bool completePending() const; - void completeItem(); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList roles); - - int indexOf(QSGItem *item, QObject *objectContext) const; - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - - QSGVisualDataGroup *items(); - QSGVisualDataGroup *persistedItems(); - QDeclarativeListProperty groups(); - QObject *parts(); - - bool event(QEvent *); - - static QSGVisualDataModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void filterGroupChanged(); - void defaultGroupsChanged(); - void rootIndexChanged(); - -private Q_SLOTS: - void _q_itemsChanged(int index, int count); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_modelReset(int oldCount, int newCount); -private: - Q_DISABLE_COPY(QSGVisualDataModel) -}; - -class QSGVisualDataGroupPrivate; -class Q_AUTOTEST_EXPORT QSGVisualDataGroup : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) -public: - QSGVisualDataGroup(QObject *parent = 0); - QSGVisualDataGroup(const QString &name, QSGVisualDataModel *model, int compositorType, QObject *parent = 0); - ~QSGVisualDataGroup(); - - QString name() const; - void setName(const QString &name); - - int count() const; - - bool defaultInclude() const; - void setDefaultInclude(bool include); - - Q_INVOKABLE QDeclarativeV8Handle get(int index); - Q_INVOKABLE QObject *create(int index); - -public Q_SLOTS: - void remove(QDeclarativeV8Function *); - void addGroups(QDeclarativeV8Function *); - void removeGroups(QDeclarativeV8Function *); - void setGroups(QDeclarativeV8Function *); - void move(QDeclarativeV8Function *); - -Q_SIGNALS: - void countChanged(); - void nameChanged(); - void defaultIncludeChanged(); - void changed(const QDeclarativeV8Handle &removed, const QDeclarativeV8Handle &inserted); -private: - Q_DECLARE_PRIVATE(QSGVisualDataGroup) -}; - -class QSGVisualDataModelCacheItem; -class QSGVisualDataModelAttachedMetaObject; -class QSGVisualDataModelAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(QSGVisualDataModel *model READ model NOTIFY modelChanged) - Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) -public: - QSGVisualDataModelAttached(QObject *parent) - : QObject(parent) - , m_cacheItem(0) - , m_previousGroups(0) - , m_modelChanged(false) - {} - ~QSGVisualDataModelAttached() { attachedProperties.remove(parent()); } - - QSGVisualDataModel *model() const; - - QStringList groups() const; - void setGroups(const QStringList &groups); - - void emitChanges(); - - static QSGVisualDataModelAttached *properties(QObject *obj) - { - QSGVisualDataModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QSGVisualDataModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void modelChanged(); - void groupsChanged(); - -public: - QSGVisualDataModelCacheItem *m_cacheItem; - int m_previousGroups; - int m_previousIndex[QDeclarativeListCompositor::MaximumGroupCount]; - bool m_modelChanged; - - static QHash attachedProperties; - - friend class QSGVisualDataModelAttachedMetaObject; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGVisualDataModel) -QML_DECLARE_TYPEINFO(QSGVisualDataModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QSGVisualDataGroup) - -QT_END_HEADER - -#endif // QSGVISUALDATAMODEL_P_H diff --git a/src/declarative/items/qsgvisualitemmodel.cpp b/src/declarative/items/qsgvisualitemmodel.cpp deleted file mode 100644 index 90d008e618..0000000000 --- a/src/declarative/items/qsgvisualitemmodel.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgvisualitemmodel_p.h" -#include "qsgitem.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -QHash QSGVisualItemModelAttached::attachedProperties; - - -class QSGVisualItemModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QSGVisualItemModel) -public: - QSGVisualItemModelPrivate() : QObjectPrivate() {} - - static void children_append(QDeclarativeListProperty *prop, QSGItem *item) { - QDeclarative_setParent_noEvent(item, prop->object); - static_cast(prop->data)->children.append(Item(item)); - static_cast(prop->data)->itemAppended(); - static_cast(prop->data)->emitChildrenChanged(); - } - - static int children_count(QDeclarativeListProperty *prop) { - return static_cast(prop->data)->children.count(); - } - - static QSGItem *children_at(QDeclarativeListProperty *prop, int index) { - return static_cast(prop->data)->children.at(index).item; - } - - void itemAppended() { - Q_Q(QSGVisualItemModel); - QSGVisualItemModelAttached *attached = QSGVisualItemModelAttached::properties(children.last().item); - attached->setIndex(children.count()-1); - QDeclarativeChangeSet changeSet; - changeSet.insert(children.count() - 1, 1); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - } - - void emitChildrenChanged() { - Q_Q(QSGVisualItemModel); - emit q->childrenChanged(); - } - - int indexOf(QSGItem *item) const { - for (int i = 0; i < children.count(); ++i) - if (children.at(i).item == item) - return i; - return -1; - } - - class Item { - public: - Item(QSGItem *i) : item(i), ref(0) {} - - void addRef() { ++ref; } - bool deref() { return --ref == 0; } - - QSGItem *item; - int ref; - }; - - QList children; -}; - - -/*! - \qmlclass VisualItemModel QSGVisualItemModel - \inqmlmodule QtQuick 2 - \ingroup qml-working-with-data - \brief The VisualItemModel allows items to be provided to a view. - - A VisualItemModel contains the visual items to be used in a view. - When a VisualItemModel is used in a view, the view does not require - a delegate since the VisualItemModel already contains the visual - delegate (items). - - An item can determine its index within the - model via the \l{VisualItemModel::index}{index} attached property. - - The example below places three colored rectangles in a ListView. - \code - import QtQuick 1.0 - - Rectangle { - VisualItemModel { - id: itemModel - Rectangle { height: 30; width: 80; color: "red" } - Rectangle { height: 30; width: 80; color: "green" } - Rectangle { height: 30; width: 80; color: "blue" } - } - - ListView { - anchors.fill: parent - model: itemModel - } - } - \endcode - - \image visualitemmodel.png - - \sa {declarative/modelviews/visualitemmodel}{VisualItemModel example} -*/ -QSGVisualItemModel::QSGVisualItemModel(QObject *parent) - : QSGVisualModel(*(new QSGVisualItemModelPrivate), parent) -{ -} - -/*! - \qmlattachedproperty int QtQuick2::VisualItemModel::index - This attached property holds the index of this delegate's item within the model. - - It is attached to each instance of the delegate. -*/ - -QDeclarativeListProperty QSGVisualItemModel::children() -{ - Q_D(QSGVisualItemModel); - return QDeclarativeListProperty(this, d, d->children_append, - d->children_count, d->children_at); -} - -/*! - \qmlproperty int QtQuick2::VisualItemModel::count - - The number of items in the model. This property is readonly. -*/ -int QSGVisualItemModel::count() const -{ - Q_D(const QSGVisualItemModel); - return d->children.count(); -} - -bool QSGVisualItemModel::isValid() const -{ - return true; -} - -QSGItem *QSGVisualItemModel::item(int index, bool) -{ - Q_D(QSGVisualItemModel); - QSGVisualItemModelPrivate::Item &item = d->children[index]; - item.addRef(); - return item.item; -} - -QSGVisualModel::ReleaseFlags QSGVisualItemModel::release(QSGItem *item) -{ - Q_D(QSGVisualItemModel); - int idx = d->indexOf(item); - if (idx >= 0) { - if (d->children[idx].deref()) { - // XXX todo - the original did item->scene()->removeItem(). Why? - item->setParentItem(0); - QDeclarative_setParent_noEvent(item, this); - } - } - return 0; -} - -bool QSGVisualItemModel::completePending() const -{ - return false; -} - -void QSGVisualItemModel::completeItem() -{ - // Nothing to do -} - -QString QSGVisualItemModel::stringValue(int index, const QString &name) -{ - Q_D(QSGVisualItemModel); - if (index < 0 || index >= d->children.count()) - return QString(); - return QDeclarativeEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); -} - -int QSGVisualItemModel::indexOf(QSGItem *item, QObject *) const -{ - Q_D(const QSGVisualItemModel); - return d->indexOf(item); -} - -QSGVisualItemModelAttached *QSGVisualItemModel::qmlAttachedProperties(QObject *obj) -{ - return QSGVisualItemModelAttached::properties(obj); -} - -QT_END_NAMESPACE - diff --git a/src/declarative/items/qsgvisualitemmodel_p.h b/src/declarative/items/qsgvisualitemmodel_p.h deleted file mode 100644 index 6e8606cc6b..0000000000 --- a/src/declarative/items/qsgvisualitemmodel_p.h +++ /dev/null @@ -1,178 +0,0 @@ -// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** 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.1, 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. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGVISUALITEMMODEL_P_H -#define QSGVISUALITEMMODEL_P_H - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QSGItem; -class QDeclarativeChangeSet; - -class Q_DECLARATIVE_EXPORT QSGVisualModel : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int count READ count NOTIFY countChanged) - -public: - virtual ~QSGVisualModel() {} - - enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; - Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) - - virtual int count() const = 0; - virtual bool isValid() const = 0; - virtual QSGItem *item(int index, bool complete=true) = 0; - virtual ReleaseFlags release(QSGItem *item) = 0; - virtual bool completePending() const = 0; - virtual void completeItem() = 0; - virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(QList roles) = 0; - - virtual int indexOf(QSGItem *item, QObject *objectContext) const = 0; - -Q_SIGNALS: - void countChanged(); - void modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); - void createdItem(int index, QSGItem *item); - void destroyingItem(QSGItem *item); - -protected: - QSGVisualModel(QObjectPrivate &dd, QObject *parent = 0) - : QObject(dd, parent) {} - -private: - Q_DISABLE_COPY(QSGVisualModel) -}; - -class QSGVisualItemModelAttached; -class QSGVisualItemModelPrivate; -class Q_DECLARATIVE_EXPORT QSGVisualItemModel : public QSGVisualModel -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QSGVisualItemModel) - - Q_PROPERTY(QDeclarativeListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "children") - -public: - QSGVisualItemModel(QObject *parent=0); - virtual ~QSGVisualItemModel() {} - - virtual int count() const; - virtual bool isValid() const; - virtual QSGItem *item(int index, bool complete=true); - virtual ReleaseFlags release(QSGItem *item); - virtual bool completePending() const; - virtual void completeItem(); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList) {} - - virtual int indexOf(QSGItem *item, QObject *objectContext) const; - - QDeclarativeListProperty children(); - - static QSGVisualItemModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void childrenChanged(); - -private: - Q_DISABLE_COPY(QSGVisualItemModel) -}; - -class QSGVisualItemModelAttached : public QObject -{ - Q_OBJECT - -public: - QSGVisualItemModelAttached(QObject *parent) - : QObject(parent), m_index(0) {} - ~QSGVisualItemModelAttached() { - attachedProperties.remove(parent()); - } - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - int index() const { return m_index; } - void setIndex(int idx) { - if (m_index != idx) { - m_index = idx; - emit indexChanged(); - } - } - - static QSGVisualItemModelAttached *properties(QObject *obj) { - QSGVisualItemModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QSGVisualItemModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - - static QHash attachedProperties; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QSGVisualModel) -QML_DECLARE_TYPE(QSGVisualItemModel) -QML_DECLARE_TYPEINFO(QSGVisualItemModel, QML_HAS_ATTACHED_PROPERTIES) - -QT_END_HEADER - -#endif // QSGVISUALITEMMODEL_P_H -- cgit v1.2.3