Merge branch 'master' of git://scm.dev.nokia.troll.no/qt/qtbase-staging
This commit is contained in:
commit
dd1a7a6379
@ -1,8 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
|
||||
** a KDAB Group company, info@kdab.com,
|
||||
** author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
|
@ -2647,10 +2647,7 @@ MakefileGenerator::writeSubTargets(QTextStream &t, QList<MakefileGenerator::SubT
|
||||
QString out_directory_cdin, out_directory_cdout;
|
||||
MAKE_CD_IN_AND_OUT(out_directory);
|
||||
|
||||
//don't need the makefile arg if it isn't changed
|
||||
QString makefilein;
|
||||
if(subtarget->makefile != "$(MAKEFILE)")
|
||||
makefilein = " -f " + subtarget->makefile;
|
||||
QString makefilein = " -f " + subtarget->makefile;
|
||||
|
||||
//write the rule/depends
|
||||
if(flags & SubTargetOrdered) {
|
||||
|
@ -111,8 +111,8 @@ public:
|
||||
TableSummaryChanged,
|
||||
TextAttributeChanged,
|
||||
TextCaretMoved,
|
||||
TextChanged,
|
||||
TextColumnChanged,
|
||||
// TextChanged, deprecated, use TextUpdated
|
||||
TextColumnChanged = TextCaretMoved + 2,
|
||||
TextInserted,
|
||||
TextRemoved,
|
||||
TextUpdated,
|
||||
|
@ -269,12 +269,20 @@ void QGraphicsLayout::activate()
|
||||
return;
|
||||
Q_ASSERT(!parentItem->isLayout());
|
||||
|
||||
setGeometry(parentItem->contentsRect()); // relayout children
|
||||
if (QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
QGraphicsWidget *parentWidget = static_cast<QGraphicsWidget*>(parentItem);
|
||||
if (!parentWidget->parentLayoutItem()) {
|
||||
// we've reached the topmost widget, resize it
|
||||
bool wasResized = parentWidget->testAttribute(Qt::WA_Resized);
|
||||
parentWidget->resize(parentWidget->size());
|
||||
parentWidget->setAttribute(Qt::WA_Resized, wasResized);
|
||||
}
|
||||
|
||||
// ### bug, should be parentItem ?
|
||||
parentLayoutItem()->updateGeometry(); // bubble up; will set activated to false
|
||||
// ### too many resizes? maybe we should walk up the chain to the
|
||||
// ### top-level layouted layoutItem and call activate there.
|
||||
setGeometry(parentItem->contentsRect()); // relayout children
|
||||
} else {
|
||||
setGeometry(parentItem->contentsRect()); // relayout children
|
||||
parentLayoutItem()->updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -300,32 +308,36 @@ bool QGraphicsLayout::isActivated() const
|
||||
*/
|
||||
void QGraphicsLayout::invalidate()
|
||||
{
|
||||
// only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
|
||||
QGraphicsLayoutItem *layoutItem = this;
|
||||
while (layoutItem && layoutItem->isLayout()) {
|
||||
// we could call updateGeometry(), but what if that method
|
||||
// does not call the base implementation? In addition, updateGeometry()
|
||||
// does more than we need.
|
||||
layoutItem->d_func()->sizeHintCacheDirty = true;
|
||||
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
|
||||
layoutItem = layoutItem->parentLayoutItem();
|
||||
}
|
||||
if (layoutItem) {
|
||||
layoutItem->d_func()->sizeHintCacheDirty = true;
|
||||
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
|
||||
}
|
||||
|
||||
bool postIt = layoutItem ? !layoutItem->isLayout() : false;
|
||||
if (postIt) {
|
||||
layoutItem = this;
|
||||
while (layoutItem && layoutItem->isLayout()
|
||||
&& static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
|
||||
static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
|
||||
if (QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
updateGeometry();
|
||||
} else {
|
||||
// only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
|
||||
QGraphicsLayoutItem *layoutItem = this;
|
||||
while (layoutItem && layoutItem->isLayout()) {
|
||||
// we could call updateGeometry(), but what if that method
|
||||
// does not call the base implementation? In addition, updateGeometry()
|
||||
// does more than we need.
|
||||
layoutItem->d_func()->sizeHintCacheDirty = true;
|
||||
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
|
||||
layoutItem = layoutItem->parentLayoutItem();
|
||||
}
|
||||
if (layoutItem && !layoutItem->isLayout()) {
|
||||
// If a layout has a parent that is not a layout it must be a QGraphicsWidget.
|
||||
QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
|
||||
if (layoutItem) {
|
||||
layoutItem->d_func()->sizeHintCacheDirty = true;
|
||||
layoutItem->d_func()->sizeHintWithConstraintCacheDirty = true;
|
||||
}
|
||||
|
||||
bool postIt = layoutItem ? !layoutItem->isLayout() : false;
|
||||
if (postIt) {
|
||||
layoutItem = this;
|
||||
while (layoutItem && layoutItem->isLayout()
|
||||
&& static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
|
||||
static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
|
||||
layoutItem = layoutItem->parentLayoutItem();
|
||||
}
|
||||
if (layoutItem && !layoutItem->isLayout()) {
|
||||
// If a layout has a parent that is not a layout it must be a QGraphicsWidget.
|
||||
QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -335,12 +347,27 @@ void QGraphicsLayout::invalidate()
|
||||
*/
|
||||
void QGraphicsLayout::updateGeometry()
|
||||
{
|
||||
QGraphicsLayoutItem::updateGeometry();
|
||||
if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
|
||||
if (parentItem->isLayout()) {
|
||||
Q_D(QGraphicsLayout);
|
||||
if (QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
d->activated = false;
|
||||
QGraphicsLayoutItem::updateGeometry();
|
||||
|
||||
QGraphicsLayoutItem *parentItem = parentLayoutItem();
|
||||
if (!parentItem)
|
||||
return;
|
||||
|
||||
if (parentItem->isLayout())
|
||||
static_cast<QGraphicsLayout *>(parentItem)->invalidate();
|
||||
else
|
||||
parentItem->updateGeometry();
|
||||
} else {
|
||||
invalidate();
|
||||
} else {
|
||||
QGraphicsLayoutItem::updateGeometry();
|
||||
if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
|
||||
if (parentItem->isLayout()) {
|
||||
parentItem->updateGeometry();
|
||||
} else {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -446,6 +473,50 @@ void QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
|
||||
d->addChildLayoutItem(layoutItem);
|
||||
}
|
||||
|
||||
static bool g_instantInvalidatePropagation = false;
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\since 4.8
|
||||
\see instantInvalidatePropagation
|
||||
|
||||
Calling this function with \a enable set to true will enable a feature that
|
||||
makes propagation of invalidation up to ancestor layout items to be done in
|
||||
one go. It will propagate up the parentLayoutItem() hierarchy until it has
|
||||
reached the root. If the root item is a QGraphicsWidget, it will *post* a
|
||||
layout request to it. When the layout request is consumed it will traverse
|
||||
down the hierarchy of layouts and widgets and activate all layouts that is
|
||||
invalid (not activated). This is the recommended behaviour.
|
||||
|
||||
If not set it will also propagate up the parentLayoutItem() hierarchy, but
|
||||
it will stop at the \i first \i widget it encounters, and post a layout
|
||||
request to the widget. When the layout request is consumed, this might
|
||||
cause it to continue propagation up to the parentLayoutItem() of the
|
||||
widget. It will continue in this fashion until it has reached a widget with
|
||||
no parentLayoutItem(). This strategy might cause drawing artifacts, since
|
||||
it is not done in one go, and the consumption of layout requests might be
|
||||
interleaved by consumption of paint events, which might cause significant
|
||||
flicker.
|
||||
Note, this is not the recommended behavior, but for compatibility reasons
|
||||
this is the default behaviour.
|
||||
*/
|
||||
void QGraphicsLayout::setInstantInvalidatePropagation(bool enable)
|
||||
{
|
||||
g_instantInvalidatePropagation = enable;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\since 4.8
|
||||
\see setInstantInvalidatePropagation
|
||||
|
||||
returns true if the complete widget/layout hierarchy is rearranged in one go.
|
||||
*/
|
||||
bool QGraphicsLayout::instantInvalidatePropagation()
|
||||
{
|
||||
return g_instantInvalidatePropagation;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif //QT_NO_GRAPHICSVIEW
|
||||
|
@ -76,6 +76,8 @@ public:
|
||||
virtual QGraphicsLayoutItem *itemAt(int i) const = 0;
|
||||
virtual void removeAt(int index) = 0;
|
||||
|
||||
static void setInstantInvalidatePropagation(bool enable);
|
||||
static bool instantInvalidatePropagation();
|
||||
protected:
|
||||
QGraphicsLayout(QGraphicsLayoutPrivate &, QGraphicsLayoutItem *);
|
||||
void addChildLayoutItem(QGraphicsLayoutItem *layoutItem);
|
||||
|
@ -180,9 +180,14 @@ void QGraphicsLayoutPrivate::activateRecursive(QGraphicsLayoutItem *item)
|
||||
{
|
||||
if (item->isLayout()) {
|
||||
QGraphicsLayout *layout = static_cast<QGraphicsLayout *>(item);
|
||||
if (layout->d_func()->activated)
|
||||
layout->invalidate();
|
||||
|
||||
if (layout->d_func()->activated) {
|
||||
if (QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
return;
|
||||
} else {
|
||||
layout->invalidate(); // ### LOOKS SUSPICIOUSLY WRONG!!???
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = layout->count() - 1; i >= 0; --i) {
|
||||
QGraphicsLayoutItem *childItem = layout->itemAt(i);
|
||||
if (childItem)
|
||||
|
@ -275,17 +275,13 @@ void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item)
|
||||
qWarning("QGraphicsLinearLayout::insertItem: cannot insert itself");
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(item);
|
||||
|
||||
//the order of the following instructions is very important because
|
||||
//invalidating the layout before adding the child item will make the layout happen
|
||||
//before we try to paint the item
|
||||
invalidate();
|
||||
d->addChildLayoutItem(item);
|
||||
|
||||
Q_ASSERT(item);
|
||||
d->fixIndex(&index);
|
||||
d->engine.insertRow(index, d->orientation);
|
||||
new QGridLayoutItem(&d->engine, item, d->gridRow(index), d->gridColumn(index), 1, 1, 0, index);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -354,8 +354,10 @@ void QGraphicsWidget::setGeometry(const QRectF &rect)
|
||||
newGeom = rect;
|
||||
newGeom.setSize(rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
|
||||
.boundedTo(effectiveSizeHint(Qt::MaximumSize)));
|
||||
if (newGeom == d->geom)
|
||||
return;
|
||||
|
||||
if (newGeom == d->geom) {
|
||||
goto relayoutChildrenAndReturn;
|
||||
}
|
||||
|
||||
// setPos triggers ItemPositionChange, which can adjust position
|
||||
wd->inSetGeometry = 1;
|
||||
@ -363,8 +365,9 @@ void QGraphicsWidget::setGeometry(const QRectF &rect)
|
||||
wd->inSetGeometry = 0;
|
||||
newGeom.moveTopLeft(pos());
|
||||
|
||||
if (newGeom == d->geom)
|
||||
return;
|
||||
if (newGeom == d->geom) {
|
||||
goto relayoutChildrenAndReturn;
|
||||
}
|
||||
|
||||
// Update and prepare to change the geometry (remove from index) if the size has changed.
|
||||
if (wd->scene) {
|
||||
@ -375,35 +378,54 @@ void QGraphicsWidget::setGeometry(const QRectF &rect)
|
||||
}
|
||||
|
||||
// Update the layout item geometry
|
||||
bool moved = oldPos != pos();
|
||||
if (moved) {
|
||||
// Send move event.
|
||||
QGraphicsSceneMoveEvent event;
|
||||
event.setOldPos(oldPos);
|
||||
event.setNewPos(pos());
|
||||
QApplication::sendEvent(this, &event);
|
||||
if (wd->inSetPos) {
|
||||
//set the new pos
|
||||
d->geom.moveTopLeft(pos());
|
||||
emit geometryChanged();
|
||||
return;
|
||||
{
|
||||
bool moved = oldPos != pos();
|
||||
if (moved) {
|
||||
// Send move event.
|
||||
QGraphicsSceneMoveEvent event;
|
||||
event.setOldPos(oldPos);
|
||||
event.setNewPos(pos());
|
||||
QApplication::sendEvent(this, &event);
|
||||
if (wd->inSetPos) {
|
||||
//set the new pos
|
||||
d->geom.moveTopLeft(pos());
|
||||
emit geometryChanged();
|
||||
goto relayoutChildrenAndReturn;
|
||||
}
|
||||
}
|
||||
QSizeF oldSize = size();
|
||||
QGraphicsLayoutItem::setGeometry(newGeom);
|
||||
// Send resize event
|
||||
bool resized = newGeom.size() != oldSize;
|
||||
if (resized) {
|
||||
QGraphicsSceneResizeEvent re;
|
||||
re.setOldSize(oldSize);
|
||||
re.setNewSize(newGeom.size());
|
||||
if (oldSize.width() != newGeom.size().width())
|
||||
emit widthChanged();
|
||||
if (oldSize.height() != newGeom.size().height())
|
||||
emit heightChanged();
|
||||
QGraphicsLayout *lay = wd->layout;
|
||||
if (QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
if (!lay || lay->isActivated()) {
|
||||
QApplication::sendEvent(this, &re);
|
||||
}
|
||||
} else {
|
||||
QApplication::sendEvent(this, &re);
|
||||
}
|
||||
}
|
||||
}
|
||||
QSizeF oldSize = size();
|
||||
QGraphicsLayoutItem::setGeometry(newGeom);
|
||||
// Send resize event
|
||||
bool resized = newGeom.size() != oldSize;
|
||||
if (resized) {
|
||||
QGraphicsSceneResizeEvent re;
|
||||
re.setOldSize(oldSize);
|
||||
re.setNewSize(newGeom.size());
|
||||
if (oldSize.width() != newGeom.size().width())
|
||||
emit widthChanged();
|
||||
if (oldSize.height() != newGeom.size().height())
|
||||
emit heightChanged();
|
||||
QApplication::sendEvent(this, &re);
|
||||
}
|
||||
|
||||
emit geometryChanged();
|
||||
relayoutChildrenAndReturn:
|
||||
if (QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
if (QGraphicsLayout *lay = wd->layout) {
|
||||
if (!lay->isActivated()) {
|
||||
QEvent layoutRequest(QEvent::LayoutRequest);
|
||||
QApplication::sendEvent(this, &layoutRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1052,16 +1074,31 @@ void QGraphicsWidget::updateGeometry()
|
||||
QGraphicsLayoutItem *parentItem = parentLayoutItem();
|
||||
|
||||
if (parentItem && parentItem->isLayout()) {
|
||||
parentItem->updateGeometry();
|
||||
if (QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
static_cast<QGraphicsLayout *>(parentItem)->invalidate();
|
||||
} else {
|
||||
parentItem->updateGeometry();
|
||||
}
|
||||
} else {
|
||||
if (parentItem) {
|
||||
// This is for custom layouting
|
||||
QGraphicsWidget *parentWid = parentWidget(); //###
|
||||
if (parentWid->isVisible())
|
||||
QApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest));
|
||||
} else {
|
||||
/**
|
||||
* If this is the topmost widget, post a LayoutRequest event to the widget.
|
||||
* When the event is received, it will start flowing all the way down to the leaf
|
||||
* widgets in one go. This will make a relayout flicker-free.
|
||||
*/
|
||||
if (QGraphicsLayout::instantInvalidatePropagation())
|
||||
QApplication::postEvent(static_cast<QGraphicsWidget *>(this), new QEvent(QEvent::LayoutRequest));
|
||||
}
|
||||
if (!QGraphicsLayout::instantInvalidatePropagation()) {
|
||||
bool wasResized = testAttribute(Qt::WA_Resized);
|
||||
resize(size()); // this will restrict the size
|
||||
setAttribute(Qt::WA_Resized, wasResized);
|
||||
}
|
||||
bool wasResized = testAttribute(Qt::WA_Resized);
|
||||
resize(size()); // this will restrict the size
|
||||
setAttribute(Qt::WA_Resized, wasResized);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,18 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
|
||||
** a KDAB Group company, info@kdab.com,
|
||||
** author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (info@qt.nokia.com)
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
@ -20,17 +25,17 @@
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
@ -1,13 +1,18 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
|
||||
** a KDAB Group company, info@kdab.com,
|
||||
** author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (info@qt.nokia.com)
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
@ -20,17 +25,17 @@
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
@ -6,6 +6,7 @@ HEADERS += \
|
||||
painting/qcolor.h \
|
||||
painting/qcolor_p.h \
|
||||
painting/qcolormap.h \
|
||||
painting/qcosmeticstroker_p.h \
|
||||
painting/qdrawutil.h \
|
||||
painting/qemulationpaintengine_p.h \
|
||||
painting/qgraphicssystem_p.h \
|
||||
@ -14,7 +15,7 @@ HEADERS += \
|
||||
painting/qoutlinemapper_p.h \
|
||||
painting/qpaintdevice.h \
|
||||
painting/qpaintengine.h \
|
||||
painting/qpaintengine_p.h \
|
||||
painting/qpaintengine_p.h \
|
||||
painting/qpaintengine_alpha_p.h \
|
||||
painting/qpaintengine_preview_p.h \
|
||||
painting/qpaintengineex_p.h \
|
||||
@ -53,6 +54,7 @@ SOURCES += \
|
||||
painting/qbrush.cpp \
|
||||
painting/qcolor.cpp \
|
||||
painting/qcolor_p.cpp \
|
||||
painting/qcosmeticstroker.cpp \
|
||||
painting/qcssutil.cpp \
|
||||
painting/qdrawutil.cpp \
|
||||
painting/qemulationpaintengine.cpp \
|
||||
|
958
src/gui/painting/qcosmeticstroker.cpp
Normal file
958
src/gui/painting/qcosmeticstroker.cpp
Normal file
@ -0,0 +1,958 @@
|
||||
#include "qcosmeticstroker_p.h"
|
||||
#include "private/qpainterpath_p.h"
|
||||
#include <qdebug.h>
|
||||
#include <math.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if 0
|
||||
inline QString capString(int caps)
|
||||
{
|
||||
QString str;
|
||||
if (caps & QCosmeticStroker::CapBegin) {
|
||||
str += "CapBegin ";
|
||||
}
|
||||
if (caps & QCosmeticStroker::CapEnd) {
|
||||
str += "CapEnd ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define toF26Dot6(x) ((int)((x)*64.))
|
||||
|
||||
static inline uint sourceOver(uint d, uint color)
|
||||
{
|
||||
return color + BYTE_MUL(d, qAlpha(~color));
|
||||
}
|
||||
|
||||
inline static int F16Dot16FixedDiv(int x, int y)
|
||||
{
|
||||
if (qAbs(x) > 0x7fff)
|
||||
return (((qlonglong)x) << 16) / y;
|
||||
return (x << 16) / y;
|
||||
}
|
||||
|
||||
typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage);
|
||||
|
||||
namespace {
|
||||
|
||||
struct Dasher {
|
||||
QCosmeticStroker *stroker;
|
||||
int *pattern;
|
||||
int offset;
|
||||
int dashIndex;
|
||||
int dashOn;
|
||||
|
||||
Dasher(QCosmeticStroker *s, bool reverse, int start, int stop)
|
||||
: stroker(s)
|
||||
{
|
||||
int delta = stop - start;
|
||||
if (reverse) {
|
||||
pattern = stroker->reversePattern;
|
||||
offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32);
|
||||
dashOn = 0;
|
||||
} else {
|
||||
pattern = stroker->pattern;
|
||||
offset = stroker->patternOffset - ((start & 63) - 32);
|
||||
dashOn = 1;
|
||||
}
|
||||
offset %= stroker->patternLength;
|
||||
if (offset < 0)
|
||||
offset += stroker->patternLength;
|
||||
|
||||
dashIndex = 0;
|
||||
while (offset>= pattern[dashIndex])
|
||||
++dashIndex;
|
||||
|
||||
// qDebug() << " dasher" << offset/64. << reverse << dashIndex;
|
||||
stroker->patternOffset += delta;
|
||||
stroker->patternOffset %= stroker->patternLength;
|
||||
}
|
||||
|
||||
bool on() const {
|
||||
return (dashIndex + dashOn) & 1;
|
||||
}
|
||||
void adjust() {
|
||||
offset += 64;
|
||||
if (offset >= pattern[dashIndex]) {
|
||||
++dashIndex;
|
||||
dashIndex %= stroker->patternSize;
|
||||
}
|
||||
offset %= stroker->patternLength;
|
||||
// qDebug() << "dasher.adjust" << offset/64. << dashIndex;
|
||||
}
|
||||
};
|
||||
|
||||
struct NoDasher {
|
||||
NoDasher(QCosmeticStroker *, bool, int, int) {}
|
||||
bool on() const { return true; }
|
||||
void adjust(int = 0) {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template<DrawPixel drawPixel, class Dasher>
|
||||
static void drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
|
||||
template<DrawPixel drawPixel, class Dasher>
|
||||
static void drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
|
||||
|
||||
inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
|
||||
{
|
||||
int lastx = stroker->spans[stroker->current_span-1].x + stroker->spans[stroker->current_span-1].len ;
|
||||
int lasty = stroker->spans[stroker->current_span-1].y;
|
||||
|
||||
if (stroker->current_span == QCosmeticStroker::NSPANS || y < lasty || (y == lasty && x < lastx)) {
|
||||
stroker->blend(stroker->current_span, stroker->spans, &stroker->state->penData);
|
||||
stroker->current_span = 0;
|
||||
}
|
||||
|
||||
stroker->spans[stroker->current_span].x = ushort(x);
|
||||
stroker->spans[stroker->current_span].len = 1;
|
||||
stroker->spans[stroker->current_span].y = y;
|
||||
stroker->spans[stroker->current_span].coverage = coverage*stroker->opacity >> 8;
|
||||
++stroker->current_span;
|
||||
}
|
||||
|
||||
inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage)
|
||||
{
|
||||
const QRect &cl = stroker->clip;
|
||||
if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
|
||||
return;
|
||||
|
||||
int offset = x + stroker->ppl*y;
|
||||
uint c = BYTE_MUL(stroker->color, coverage);
|
||||
stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c);
|
||||
}
|
||||
|
||||
inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int)
|
||||
{
|
||||
const QRect &cl = stroker->clip;
|
||||
if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
|
||||
return;
|
||||
|
||||
int offset = x + stroker->ppl*y;
|
||||
stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color);
|
||||
}
|
||||
|
||||
enum StrokeSelection {
|
||||
Aliased = 0,
|
||||
AntiAliased = 1,
|
||||
Solid = 0,
|
||||
Dashed = 2,
|
||||
RegularDraw = 0,
|
||||
FastDraw = 4
|
||||
};
|
||||
|
||||
static StrokeLine strokeLine(int strokeSelection)
|
||||
{
|
||||
StrokeLine stroke;
|
||||
|
||||
switch (strokeSelection) {
|
||||
case Aliased|Solid|RegularDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>;
|
||||
break;
|
||||
case Aliased|Solid|FastDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>;
|
||||
break;
|
||||
case Aliased|Dashed|RegularDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>;
|
||||
break;
|
||||
case Aliased|Dashed|FastDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>;
|
||||
break;
|
||||
case AntiAliased|Solid|RegularDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>;
|
||||
break;
|
||||
case AntiAliased|Solid|FastDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>;
|
||||
break;
|
||||
case AntiAliased|Dashed|RegularDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>;
|
||||
break;
|
||||
case AntiAliased|Dashed|FastDraw:
|
||||
stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>;
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
stroke = 0;
|
||||
}
|
||||
return stroke;
|
||||
}
|
||||
|
||||
void QCosmeticStroker::setup()
|
||||
{
|
||||
blend = state->penData.blend;
|
||||
if (state->clip && state->clip->enabled && state->clip->hasRectClip && !state->clip->clipRect.isEmpty()) {
|
||||
clip &= state->clip->clipRect;
|
||||
blend = state->penData.unclipped_blend;
|
||||
}
|
||||
|
||||
int strokeSelection = 0;
|
||||
if (blend == state->penData.unclipped_blend
|
||||
&& state->penData.type == QSpanData::Solid
|
||||
&& (state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
|
||||
|| state->penData.rasterBuffer->format == QImage::Format_RGB32)
|
||||
&& state->compositionMode() == QPainter::CompositionMode_SourceOver)
|
||||
strokeSelection |= FastDraw;
|
||||
|
||||
if (state->renderHints & QPainter::Antialiasing)
|
||||
strokeSelection |= AntiAliased;
|
||||
|
||||
const QVector<qreal> &penPattern = state->lastPen.dashPattern();
|
||||
if (penPattern.isEmpty()) {
|
||||
Q_ASSERT(!pattern && !reversePattern);
|
||||
pattern = 0;
|
||||
reversePattern = 0;
|
||||
patternLength = 0;
|
||||
patternSize = 0;
|
||||
} else {
|
||||
pattern = (int *)malloc(penPattern.size()*sizeof(int));
|
||||
reversePattern = (int *)malloc(penPattern.size()*sizeof(int));
|
||||
patternSize = penPattern.size();
|
||||
|
||||
patternLength = 0;
|
||||
for (int i = 0; i < patternSize; ++i) {
|
||||
patternLength += (int) qMax(1. , penPattern.at(i)*64.);
|
||||
pattern[i] = patternLength;
|
||||
}
|
||||
patternLength = 0;
|
||||
for (int i = 0; i < patternSize; ++i) {
|
||||
patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.);
|
||||
reversePattern[i] = patternLength;
|
||||
}
|
||||
strokeSelection |= Dashed;
|
||||
// qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.;
|
||||
}
|
||||
|
||||
stroke = strokeLine(strokeSelection);
|
||||
|
||||
qreal width = state->lastPen.widthF();
|
||||
if (width == 0)
|
||||
opacity = 256;
|
||||
else if (state->lastPen.isCosmetic())
|
||||
opacity = (int) 256*width;
|
||||
else
|
||||
opacity = (int) 256*width*state->txscale;
|
||||
opacity = qBound(0, opacity, 256);
|
||||
|
||||
drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
|
||||
|
||||
if (strokeSelection & FastDraw) {
|
||||
color = INTERPOLATE_PIXEL_256(state->penData.solid.color, opacity, 0, 0);
|
||||
QRasterBuffer *buffer = state->penData.rasterBuffer;
|
||||
pixels = (uint *)buffer->buffer();
|
||||
ppl = buffer->bytesPerLine()>>2;
|
||||
}
|
||||
|
||||
// setup FP clip bounds
|
||||
xmin = clip.left() - 1;
|
||||
xmax = clip.right() + 2;
|
||||
ymin = clip.top() - 1;
|
||||
ymax = clip.bottom() + 2;
|
||||
|
||||
lastPixel.x = -1;
|
||||
}
|
||||
|
||||
// returns true if the whole line gets clipped away
|
||||
bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
|
||||
{
|
||||
// basic/rough clipping is done in floating point coordinates to avoid
|
||||
// integer overflow problems.
|
||||
if (x1 < xmin) {
|
||||
if (x2 <= xmin)
|
||||
goto clipped;
|
||||
y1 += (y2 - y1)/(x2 - x1) * (xmin - x1);
|
||||
x1 = xmin;
|
||||
} else if (x1 > xmax) {
|
||||
if (x2 >= xmax)
|
||||
goto clipped;
|
||||
y1 += (y2 - y1)/(x2 - x1) * (xmax - x1);
|
||||
x1 = xmax;
|
||||
}
|
||||
if (x2 < xmin) {
|
||||
lastPixel.x = -1;
|
||||
y2 += (y2 - y1)/(x2 - x1) * (xmin - x2);
|
||||
x2 = xmin;
|
||||
} else if (x2 > xmax) {
|
||||
lastPixel.x = -1;
|
||||
y2 += (y2 - y1)/(x2 - x1) * (xmax - x2);
|
||||
x2 = xmax;
|
||||
}
|
||||
|
||||
if (y1 < ymin) {
|
||||
if (y2 <= ymin)
|
||||
goto clipped;
|
||||
x1 += (x2 - x1)/(y2 - y1) * (ymin - y1);
|
||||
y1 = ymin;
|
||||
} else if (y1 > ymax) {
|
||||
if (y2 >= ymax)
|
||||
goto clipped;
|
||||
x1 += (x2 - x1)/(y2 - y1) * (ymax - y1);
|
||||
y1 = ymax;
|
||||
}
|
||||
if (y2 < ymin) {
|
||||
lastPixel.x = -1;
|
||||
x2 += (x2 - x1)/(y2 - y1) * (ymin - y2);
|
||||
y2 = ymin;
|
||||
} else if (y2 > ymax) {
|
||||
lastPixel.x = -1;
|
||||
x2 += (x2 - x1)/(y2 - y1) * (ymax - y2);
|
||||
y2 = ymax;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
clipped:
|
||||
lastPixel.x = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2)
|
||||
{
|
||||
QPointF start = p1 * state->matrix;
|
||||
QPointF end = p2 * state->matrix;
|
||||
|
||||
patternOffset = state->lastPen.dashOffset()*64;
|
||||
lastPixel.x = -1;
|
||||
|
||||
stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0);
|
||||
|
||||
blend(current_span, spans, &state->penData);
|
||||
current_span = 0;
|
||||
}
|
||||
|
||||
void QCosmeticStroker::drawPoints(const QPoint *points, int num)
|
||||
{
|
||||
const QPoint *end = points + num;
|
||||
while (points < end) {
|
||||
QPointF p = QPointF(*points) * state->matrix;
|
||||
drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
|
||||
++points;
|
||||
}
|
||||
|
||||
blend(current_span, spans, &state->penData);
|
||||
current_span = 0;
|
||||
}
|
||||
|
||||
void QCosmeticStroker::drawPoints(const QPointF *points, int num)
|
||||
{
|
||||
const QPointF *end = points + num;
|
||||
while (points < end) {
|
||||
QPointF p = (*points) * state->matrix;
|
||||
drawPixel(this, qRound(p.x()), qRound(p.y()), 255);
|
||||
++points;
|
||||
}
|
||||
|
||||
blend(current_span, spans, &state->penData);
|
||||
current_span = 0;
|
||||
}
|
||||
|
||||
void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2)
|
||||
{
|
||||
// this is basically the same code as used in the aliased stroke method,
|
||||
// but it only determines the direction and last point of a line
|
||||
//
|
||||
// This is being used to have proper dropout control for closed contours
|
||||
// by calculating the direction and last pixel of the last segment in the contour.
|
||||
// the info is then used to perform dropout control when drawing the first line segment
|
||||
// of the contour
|
||||
lastPixel.x = -1;
|
||||
lastPixel.y = -1;
|
||||
|
||||
if (clipLine(rx1, ry1, rx2, ry2))
|
||||
return;
|
||||
|
||||
int x1 = toF26Dot6(rx1);
|
||||
int y1 = toF26Dot6(ry1);
|
||||
int x2 = toF26Dot6(rx2);
|
||||
int y2 = toF26Dot6(ry2);
|
||||
|
||||
int dx = qAbs(x2 - x1);
|
||||
int dy = qAbs(y2 - y1);
|
||||
|
||||
if (dx < dy) {
|
||||
// vertical
|
||||
bool swapped = false;
|
||||
if (y1 > y2) {
|
||||
swapped = true;
|
||||
qSwap(y1, y2);
|
||||
qSwap(x1, x2);
|
||||
}
|
||||
int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
|
||||
int x = x1 << 10;
|
||||
|
||||
int y = (y1+32) >> 6;
|
||||
int ys = (y2+32) >> 6;
|
||||
|
||||
if (y != ys) {
|
||||
x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
|
||||
|
||||
if (swapped) {
|
||||
lastPixel.x = x >> 16;
|
||||
lastPixel.y = y;
|
||||
lastDir = QCosmeticStroker::BottomToTop;
|
||||
} else {
|
||||
lastPixel.x = (x + (ys - y - 1)*xinc) >> 16;
|
||||
lastPixel.y = ys - 1;
|
||||
lastDir = QCosmeticStroker::TopToBottom;
|
||||
}
|
||||
lastAxisAligned = qAbs(xinc) < (1 << 14);
|
||||
}
|
||||
} else {
|
||||
// horizontal
|
||||
if (!dx)
|
||||
return;
|
||||
|
||||
bool swapped = false;
|
||||
if (x1 > x2) {
|
||||
swapped = true;
|
||||
qSwap(x1, x2);
|
||||
qSwap(y1, y2);
|
||||
}
|
||||
int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
|
||||
int y = y1 << 10;
|
||||
|
||||
int x = (x1+32) >> 6;
|
||||
int xs = (x2+32) >> 6;
|
||||
|
||||
if (x != xs) {
|
||||
y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
|
||||
|
||||
if (swapped) {
|
||||
lastPixel.x = x;
|
||||
lastPixel.y = y >> 16;
|
||||
lastDir = QCosmeticStroker::RightToLeft;
|
||||
} else {
|
||||
lastPixel.x = xs - 1;
|
||||
lastPixel.y = (y + (xs - x - 1)*yinc) >> 16;
|
||||
lastDir = QCosmeticStroker::LeftToRight;
|
||||
}
|
||||
lastAxisAligned = qAbs(yinc) < (1 << 14);
|
||||
}
|
||||
}
|
||||
// qDebug() << " moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir;
|
||||
}
|
||||
|
||||
static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end,
|
||||
const qreal *points, bool *closed)
|
||||
{
|
||||
const QPainterPath::ElementType *start = t;
|
||||
++t;
|
||||
|
||||
// find out if the subpath is closed
|
||||
while (t < end) {
|
||||
if (*t == QPainterPath::MoveToElement)
|
||||
break;
|
||||
++t;
|
||||
}
|
||||
|
||||
int offset = t - start - 1;
|
||||
// qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1];
|
||||
*closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void QCosmeticStroker::drawPath(const QVectorPath &path)
|
||||
{
|
||||
// qDebug() << ">>>> drawpath" << path.convertToPainterPath()
|
||||
// << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose();
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
const qreal *points = path.points();
|
||||
const QPainterPath::ElementType *type = path.elements();
|
||||
|
||||
if (type) {
|
||||
const QPainterPath::ElementType *end = type + path.elementCount();
|
||||
|
||||
while (type < end) {
|
||||
Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement);
|
||||
|
||||
QPointF p = QPointF(points[0], points[1]) * state->matrix;
|
||||
QPointF movedTo = p;
|
||||
patternOffset = state->lastPen.dashOffset()*64;
|
||||
lastPixel.x = -1;
|
||||
|
||||
bool closed;
|
||||
const QPainterPath::ElementType *e = subPath(type, end, points, &closed);
|
||||
if (closed) {
|
||||
const qreal *p = points + 2*(e-type);
|
||||
calculateLastPoint(p[-4], p[-3], p[-2], p[-1]);
|
||||
}
|
||||
int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
|
||||
// qDebug() << "closed =" << closed << capString(caps);
|
||||
|
||||
points += 2;
|
||||
++type;
|
||||
|
||||
while (type < e) {
|
||||
QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
|
||||
switch (*type) {
|
||||
case QPainterPath::MoveToElement:
|
||||
Q_ASSERT(!"Logic error");
|
||||
break;
|
||||
|
||||
case QPainterPath::LineToElement:
|
||||
if (!closed && drawCaps && type == e - 1)
|
||||
caps |= CapEnd;
|
||||
stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
|
||||
p = p2;
|
||||
points += 2;
|
||||
++type;
|
||||
break;
|
||||
|
||||
case QPainterPath::CurveToElement: {
|
||||
if (!closed && drawCaps && type == e - 3)
|
||||
caps |= CapEnd;
|
||||
QPointF p3 = QPointF(points[2], points[3]) * state->matrix;
|
||||
QPointF p4 = QPointF(points[4], points[5]) * state->matrix;
|
||||
renderCubic(p, p2, p3, p4, caps);
|
||||
p = p4;
|
||||
type += 3;
|
||||
points += 6;
|
||||
break;
|
||||
}
|
||||
case QPainterPath::CurveToDataElement:
|
||||
Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
|
||||
break;
|
||||
}
|
||||
caps = NoCaps;
|
||||
}
|
||||
}
|
||||
} else { // !type, simple polygon
|
||||
QPointF p = QPointF(points[0], points[1]) * state->matrix;
|
||||
QPointF movedTo = p;
|
||||
patternOffset = state->lastPen.dashOffset()*64;
|
||||
lastPixel.x = -1;
|
||||
|
||||
const qreal *end = points + 2*path.elementCount();
|
||||
// handle closed path case
|
||||
bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]);
|
||||
int caps = (!closed & drawCaps) ? CapBegin : NoCaps;
|
||||
if (closed)
|
||||
calculateLastPoint(end[-2], end[-1], points[0], points[1]);
|
||||
|
||||
points += 2;
|
||||
while (points < end) {
|
||||
QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
|
||||
|
||||
if (!closed && drawCaps && points == end - 2)
|
||||
caps |= CapEnd;
|
||||
|
||||
stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
|
||||
|
||||
p = p2;
|
||||
points += 2;
|
||||
caps = NoCaps;
|
||||
}
|
||||
if (path.hasImplicitClose())
|
||||
stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps);
|
||||
}
|
||||
|
||||
|
||||
blend(current_span, spans, &state->penData);
|
||||
current_span = 0;
|
||||
}
|
||||
|
||||
void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
|
||||
{
|
||||
// qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps);
|
||||
const int maxSubDivisions = 6;
|
||||
PointF points[3*maxSubDivisions + 4];
|
||||
|
||||
points[3].x = p1.x();
|
||||
points[3].y = p1.y();
|
||||
points[2].x = p2.x();
|
||||
points[2].y = p2.y();
|
||||
points[1].x = p3.x();
|
||||
points[1].y = p3.y();
|
||||
points[0].x = p4.x();
|
||||
points[0].y = p4.y();
|
||||
|
||||
PointF *p = points;
|
||||
int level = maxSubDivisions;
|
||||
|
||||
renderCubicSubdivision(p, level, caps);
|
||||
}
|
||||
|
||||
static void splitCubic(QCosmeticStroker::PointF *points)
|
||||
{
|
||||
const qreal half = .5;
|
||||
qreal a, b, c, d;
|
||||
|
||||
points[6].x = points[3].x;
|
||||
c = points[1].x;
|
||||
d = points[2].x;
|
||||
points[1].x = a = ( points[0].x + c ) * half;
|
||||
points[5].x = b = ( points[3].x + d ) * half;
|
||||
c = ( c + d ) * half;
|
||||
points[2].x = a = ( a + c ) * half;
|
||||
points[4].x = b = ( b + c ) * half;
|
||||
points[3].x = ( a + b ) * half;
|
||||
|
||||
points[6].y = points[3].y;
|
||||
c = points[1].y;
|
||||
d = points[2].y;
|
||||
points[1].y = a = ( points[0].y + c ) * half;
|
||||
points[5].y = b = ( points[3].y + d ) * half;
|
||||
c = ( c + d ) * half;
|
||||
points[2].y = a = ( a + c ) * half;
|
||||
points[4].y = b = ( b + c ) * half;
|
||||
points[3].y = ( a + b ) * half;
|
||||
}
|
||||
|
||||
void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps)
|
||||
{
|
||||
if (level) {
|
||||
qreal dx = points[3].x - points[0].x;
|
||||
qreal dy = points[3].y - points[0].y;
|
||||
qreal len = ((qreal).25) * (qAbs(dx) + qAbs(dy));
|
||||
|
||||
if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) > len ||
|
||||
qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) > len) {
|
||||
splitCubic(points);
|
||||
|
||||
--level;
|
||||
renderCubicSubdivision(points + 3, level, caps & CapBegin);
|
||||
renderCubicSubdivision(points, level, caps & CapEnd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps);
|
||||
}
|
||||
|
||||
static inline int swapCaps(int caps)
|
||||
{
|
||||
return ((caps & QCosmeticStroker::CapBegin) << 1) |
|
||||
((caps & QCosmeticStroker::CapEnd) >> 1);
|
||||
}
|
||||
|
||||
// adjust line by half a pixel
|
||||
static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
|
||||
{
|
||||
if (caps & QCosmeticStroker::CapBegin) {
|
||||
x1 -= 32;
|
||||
y -= yinc >> 1;
|
||||
}
|
||||
if (caps & QCosmeticStroker::CapEnd) {
|
||||
x2 += 32;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The hard part about this is dropout control and avoiding douple drawing of points when
|
||||
the drawing shifts from horizontal to vertical or back.
|
||||
*/
|
||||
template<DrawPixel drawPixel, class Dasher>
|
||||
static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
|
||||
{
|
||||
if (stroker->clipLine(rx1, ry1, rx2, ry2))
|
||||
return;
|
||||
|
||||
static const int half = 32;
|
||||
int x1 = toF26Dot6(rx1) + half;
|
||||
int y1 = toF26Dot6(ry1) + half;
|
||||
int x2 = toF26Dot6(rx2) + half;
|
||||
int y2 = toF26Dot6(ry2) + half;
|
||||
|
||||
int dx = qAbs(x2 - x1);
|
||||
int dy = qAbs(y2 - y1);
|
||||
|
||||
QCosmeticStroker::Point last = stroker->lastPixel;
|
||||
|
||||
// qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64. << capString(caps);
|
||||
|
||||
if (dx < dy) {
|
||||
// vertical
|
||||
|
||||
bool swapped = false;
|
||||
if (y1 > y2) {
|
||||
swapped = true;
|
||||
qSwap(y1, y2);
|
||||
qSwap(x1, x2);
|
||||
caps = swapCaps(caps);
|
||||
--x1; --x2; --y1; --y2;
|
||||
}
|
||||
int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
|
||||
int x = x1 << 10;
|
||||
|
||||
capAdjust(caps, y1, y2, x, xinc);
|
||||
|
||||
int y = (y1+32) >> 6;
|
||||
int ys = (y2+32) >> 6;
|
||||
|
||||
if (y != ys) {
|
||||
x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6;
|
||||
|
||||
// calculate first and last pixel and perform dropout control
|
||||
QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;
|
||||
QCosmeticStroker::Point first;
|
||||
first.x = x >> 16;
|
||||
first.y = y;
|
||||
last.x = (x + (ys - y - 1)*xinc) >> 16;
|
||||
last.y = ys - 1;
|
||||
if (swapped) {
|
||||
qSwap(first, last);
|
||||
dir = QCosmeticStroker::BottomToTop;
|
||||
}
|
||||
bool axisAligned = qAbs(xinc) < (1 << 14);
|
||||
if (stroker->lastPixel.x >= 0) {
|
||||
if (first.x == stroker->lastPixel.x &&
|
||||
first.y == stroker->lastPixel.y) {
|
||||
// remove duplicated pixel
|
||||
if (swapped) {
|
||||
--ys;
|
||||
} else {
|
||||
++y;
|
||||
x += xinc;
|
||||
}
|
||||
} else if (stroker->lastDir != dir &&
|
||||
(((axisAligned && stroker->lastAxisAligned) &&
|
||||
stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
|
||||
(qAbs(stroker->lastPixel.x - first.x) > 1 &&
|
||||
qAbs(stroker->lastPixel.y - first.y) > 1))) {
|
||||
// have a missing pixel, insert it
|
||||
if (swapped) {
|
||||
++ys;
|
||||
} else {
|
||||
--y;
|
||||
x -= xinc;
|
||||
}
|
||||
}
|
||||
}
|
||||
stroker->lastDir = dir;
|
||||
stroker->lastAxisAligned = axisAligned;
|
||||
|
||||
Dasher dasher(stroker, swapped, y << 6, ys << 6);
|
||||
|
||||
do {
|
||||
if (dasher.on())
|
||||
drawPixel(stroker, x >> 16, y, 255);
|
||||
dasher.adjust();
|
||||
x += xinc;
|
||||
} while (++y < ys);
|
||||
}
|
||||
} else {
|
||||
// horizontal
|
||||
if (!dx)
|
||||
return;
|
||||
|
||||
bool swapped = false;
|
||||
if (x1 > x2) {
|
||||
swapped = true;
|
||||
qSwap(x1, x2);
|
||||
qSwap(y1, y2);
|
||||
caps = swapCaps(caps);
|
||||
--x1; --x2; --y1; --y2;
|
||||
}
|
||||
int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
|
||||
int y = y1 << 10;
|
||||
|
||||
capAdjust(caps, x1, x2, y, yinc);
|
||||
|
||||
int x = (x1+32) >> 6;
|
||||
int xs = (x2+32) >> 6;
|
||||
|
||||
|
||||
if (x != xs) {
|
||||
y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6;
|
||||
|
||||
// calculate first and last pixel to perform dropout control
|
||||
QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;
|
||||
QCosmeticStroker::Point first;
|
||||
first.x = x;
|
||||
first.y = y >> 16;
|
||||
last.x = xs - 1;
|
||||
last.y = (y + (xs - x - 1)*yinc) >> 16;
|
||||
if (swapped) {
|
||||
qSwap(first, last);
|
||||
dir = QCosmeticStroker::RightToLeft;
|
||||
}
|
||||
bool axisAligned = qAbs(yinc) < (1 << 14);
|
||||
if (stroker->lastPixel.x >= 0) {
|
||||
if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
|
||||
// remove duplicated pixel
|
||||
if (swapped) {
|
||||
--xs;
|
||||
} else {
|
||||
++x;
|
||||
y += yinc;
|
||||
}
|
||||
} else if (stroker->lastDir != dir &&
|
||||
(((axisAligned && stroker->lastAxisAligned) &&
|
||||
stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
|
||||
(qAbs(stroker->lastPixel.x - first.x) > 1 &&
|
||||
qAbs(stroker->lastPixel.y - first.y) > 1))) {
|
||||
// have a missing pixel, insert it
|
||||
if (swapped) {
|
||||
++xs;
|
||||
} else {
|
||||
--x;
|
||||
y -= yinc;
|
||||
}
|
||||
}
|
||||
}
|
||||
stroker->lastDir = dir;
|
||||
stroker->lastAxisAligned = axisAligned;
|
||||
|
||||
Dasher dasher(stroker, swapped, x << 6, xs << 6);
|
||||
|
||||
do {
|
||||
if (dasher.on())
|
||||
drawPixel(stroker, x, y >> 16, 255);
|
||||
dasher.adjust();
|
||||
y += yinc;
|
||||
} while (++x < xs);
|
||||
}
|
||||
}
|
||||
stroker->lastPixel = last;
|
||||
}
|
||||
|
||||
|
||||
template<DrawPixel drawPixel, class Dasher>
|
||||
static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
|
||||
{
|
||||
if (stroker->clipLine(rx1, ry1, rx2, ry2))
|
||||
return;
|
||||
|
||||
int x1 = toF26Dot6(rx1);
|
||||
int y1 = toF26Dot6(ry1);
|
||||
int x2 = toF26Dot6(rx2);
|
||||
int y2 = toF26Dot6(ry2);
|
||||
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
|
||||
if (qAbs(dx) < qAbs(dy)) {
|
||||
// vertical
|
||||
|
||||
int xinc = F16Dot16FixedDiv(dx, dy);
|
||||
|
||||
bool swapped = false;
|
||||
if (y1 > y2) {
|
||||
qSwap(y1, y2);
|
||||
qSwap(x1, x2);
|
||||
swapped = true;
|
||||
caps = swapCaps(caps);
|
||||
}
|
||||
|
||||
int x = (x1 - 32) << 10;
|
||||
x -= ( ((y1 & 63) - 32) * xinc ) >> 6;
|
||||
|
||||
capAdjust(caps, y1, y2, x, xinc);
|
||||
|
||||
Dasher dasher(stroker, swapped, y1, y2);
|
||||
|
||||
int y = y1 >> 6;
|
||||
int ys = y2 >> 6;
|
||||
|
||||
int alphaStart, alphaEnd;
|
||||
if (y == ys) {
|
||||
alphaStart = y2 - y1;
|
||||
Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
|
||||
alphaEnd = 0;
|
||||
} else {
|
||||
alphaStart = 64 - (y1 & 63);
|
||||
alphaEnd = (y2 & 63);
|
||||
}
|
||||
// qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.;
|
||||
// qDebug() << " x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys;
|
||||
|
||||
// draw first pixel
|
||||
if (dasher.on()) {
|
||||
uint alpha = (quint8)(x >> 8);
|
||||
drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6);
|
||||
drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6);
|
||||
}
|
||||
dasher.adjust();
|
||||
x += xinc;
|
||||
++y;
|
||||
if (y < ys) {
|
||||
do {
|
||||
if (dasher.on()) {
|
||||
uint alpha = (quint8)(x >> 8);
|
||||
drawPixel(stroker, x>>16, y, (255-alpha));
|
||||
drawPixel(stroker, (x>>16) + 1, y, alpha);
|
||||
}
|
||||
dasher.adjust();
|
||||
x += xinc;
|
||||
} while (++y < ys);
|
||||
}
|
||||
// draw last pixel
|
||||
if (alphaEnd && dasher.on()) {
|
||||
uint alpha = (quint8)(x >> 8);
|
||||
drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6);
|
||||
drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6);
|
||||
}
|
||||
} else {
|
||||
// horizontal
|
||||
if (!dx)
|
||||
return;
|
||||
|
||||
int yinc = F16Dot16FixedDiv(dy, dx);
|
||||
|
||||
bool swapped = false;
|
||||
if (x1 > x2) {
|
||||
qSwap(x1, x2);
|
||||
qSwap(y1, y2);
|
||||
swapped = true;
|
||||
caps = swapCaps(caps);
|
||||
}
|
||||
|
||||
int y = (y1 - 32) << 10;
|
||||
y -= ( ((x1 & 63) - 32) * yinc ) >> 6;
|
||||
|
||||
capAdjust(caps, x1, x2, y, yinc);
|
||||
|
||||
Dasher dasher(stroker, swapped, x1, x2);
|
||||
|
||||
int x = x1 >> 6;
|
||||
int xs = x2 >> 6;
|
||||
|
||||
// qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.;
|
||||
// qDebug() << " y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16);
|
||||
int alphaStart, alphaEnd;
|
||||
if (x == xs) {
|
||||
alphaStart = x2 - x1;
|
||||
Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
|
||||
alphaEnd = 0;
|
||||
} else {
|
||||
alphaStart = 64 - (x1 & 63);
|
||||
alphaEnd = (x2 & 63);
|
||||
}
|
||||
|
||||
// draw first pixel
|
||||
if (dasher.on()) {
|
||||
uint alpha = (quint8)(y >> 8);
|
||||
drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6);
|
||||
drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6);
|
||||
}
|
||||
dasher.adjust();
|
||||
y += yinc;
|
||||
++x;
|
||||
// draw line
|
||||
if (x < xs) {
|
||||
do {
|
||||
if (dasher.on()) {
|
||||
uint alpha = (quint8)(y >> 8);
|
||||
drawPixel(stroker, x, y>>16, (255-alpha));
|
||||
drawPixel(stroker, x, (y>>16) + 1, alpha);
|
||||
}
|
||||
dasher.adjust();
|
||||
y += yinc;
|
||||
} while (++x < xs);
|
||||
}
|
||||
// draw last pixel
|
||||
if (alphaEnd && dasher.on()) {
|
||||
uint alpha = (quint8)(y >> 8);
|
||||
drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6);
|
||||
drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
111
src/gui/painting/qcosmeticstroker_p.h
Normal file
111
src/gui/painting/qcosmeticstroker_p.h
Normal file
@ -0,0 +1,111 @@
|
||||
#ifndef QCOSMETICSTROKER_P_H
|
||||
#define QCOSMETICSTROKER_P_H
|
||||
|
||||
#include <private/qdrawhelper_p.h>
|
||||
#include <private/qvectorpath_p.h>
|
||||
#include <private/qpaintengine_raster_p.h>
|
||||
#include <qpen.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Gui)
|
||||
|
||||
class QCosmeticStroker;
|
||||
|
||||
|
||||
typedef void (*StrokeLine)(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
|
||||
|
||||
class QCosmeticStroker
|
||||
{
|
||||
public:
|
||||
struct Point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
struct PointF {
|
||||
qreal x;
|
||||
qreal y;
|
||||
};
|
||||
|
||||
enum Caps {
|
||||
NoCaps = 0,
|
||||
CapBegin = 0x1,
|
||||
CapEnd = 0x2,
|
||||
};
|
||||
|
||||
// used to avoid drop outs or duplicated points
|
||||
enum Direction {
|
||||
TopToBottom,
|
||||
BottomToTop,
|
||||
LeftToRight,
|
||||
RightToLeft
|
||||
};
|
||||
|
||||
QCosmeticStroker(QRasterPaintEngineState *s, const QRect &dr)
|
||||
: state(s),
|
||||
clip(dr),
|
||||
pattern(0),
|
||||
reversePattern(0),
|
||||
patternSize(0),
|
||||
patternLength(0),
|
||||
patternOffset(0),
|
||||
current_span(0),
|
||||
lastDir(LeftToRight),
|
||||
lastAxisAligned(false)
|
||||
{ setup(); }
|
||||
~QCosmeticStroker() { free(pattern); free(reversePattern); }
|
||||
void drawLine(const QPointF &p1, const QPointF &p2);
|
||||
void drawPath(const QVectorPath &path);
|
||||
void drawPoints(const QPoint *points, int num);
|
||||
void drawPoints(const QPointF *points, int num);
|
||||
|
||||
|
||||
QRasterPaintEngineState *state;
|
||||
QRect clip;
|
||||
// clip bounds in real
|
||||
qreal xmin, xmax;
|
||||
qreal ymin, ymax;
|
||||
|
||||
StrokeLine stroke;
|
||||
bool drawCaps;
|
||||
|
||||
int *pattern;
|
||||
int *reversePattern;
|
||||
int patternSize;
|
||||
int patternLength;
|
||||
int patternOffset;
|
||||
|
||||
enum { NSPANS = 255 };
|
||||
QT_FT_Span spans[NSPANS];
|
||||
int current_span;
|
||||
ProcessSpans blend;
|
||||
|
||||
int opacity;
|
||||
|
||||
uint color;
|
||||
uint *pixels;
|
||||
int ppl;
|
||||
|
||||
Direction lastDir;
|
||||
Point lastPixel;
|
||||
bool lastAxisAligned;
|
||||
|
||||
private:
|
||||
void setup();
|
||||
|
||||
void renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps);
|
||||
void renderCubicSubdivision(PointF *points, int level, int caps);
|
||||
// used for closed subpaths
|
||||
void calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2);
|
||||
|
||||
public:
|
||||
bool clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QCOSMETICLINE_H
|
File diff suppressed because it is too large
Load Diff
@ -196,9 +196,6 @@ public:
|
||||
void stroke(const QVectorPath &path, const QPen &pen);
|
||||
void fill(const QVectorPath &path, const QBrush &brush);
|
||||
|
||||
void strokePolygonCosmetic(const QPoint *pts, int pointCount, PolygonDrawMode mode);
|
||||
void strokePolygonCosmetic(const QPointF *pt, int pointCount, PolygonDrawMode mode);
|
||||
|
||||
void clip(const QVectorPath &path, Qt::ClipOperation op);
|
||||
void clip(const QRect &rect, Qt::ClipOperation op);
|
||||
void clip(const QRegion ®ion, Qt::ClipOperation op);
|
||||
@ -328,8 +325,6 @@ public:
|
||||
bool isUnclipped_normalized(const QRect &rect) const;
|
||||
bool isUnclipped(const QRect &rect, int penWidth) const;
|
||||
bool isUnclipped(const QRectF &rect, int penWidth) const;
|
||||
ProcessSpans getPenFunc(const QRect &rect, const QSpanData *data) const;
|
||||
ProcessSpans getPenFunc(const QRectF &rect, const QSpanData *data) const;
|
||||
ProcessSpans getBrushFunc(const QRect &rect, const QSpanData *data) const;
|
||||
ProcessSpans getBrushFunc(const QRectF &rect, const QSpanData *data) const;
|
||||
|
||||
|
@ -831,7 +831,7 @@ void QPaintEngineEx::drawEllipse(const QRectF &r)
|
||||
|
||||
int point_count = 0;
|
||||
x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count);
|
||||
QVectorPath vp((qreal *) pts, point_count, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
|
||||
QVectorPath vp((qreal *) pts, point_count + 1, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
|
||||
draw(vp);
|
||||
}
|
||||
|
||||
|
@ -651,7 +651,12 @@ void QLineControl::internalSetText(const QString &txt, int pos, bool edited)
|
||||
m_modifiedState = m_undoState = 0;
|
||||
m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
|
||||
m_textDirty = (oldText != m_text);
|
||||
finishChange(-1, true, edited);
|
||||
bool changed = finishChange(-1, true, edited);
|
||||
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
if (changed)
|
||||
QAccessible::updateAccessibility(parent(), 0, QAccessible::TextUpdated);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -1238,6 +1243,9 @@ void QLineControl::emitCursorPositionChanged()
|
||||
const int oldLast = m_lastCursorPos;
|
||||
m_lastCursorPos = m_cursor;
|
||||
cursorPositionChanged(oldLast, m_cursor);
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
QAccessible::updateAccessibility(parent(), 0, QAccessible::TextCaretMoved);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,10 +61,10 @@
|
||||
#include "QtGui/qtextlayout.h"
|
||||
#include "QtGui/qstyleoption.h"
|
||||
#include "QtCore/qpointer.h"
|
||||
#include "QtGui/qlineedit.h"
|
||||
#include "QtGui/qclipboard.h"
|
||||
#include "QtCore/qpoint.h"
|
||||
#include "QtGui/qcompleter.h"
|
||||
#include "QtGui/qaccessible.h"
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
|
@ -153,6 +153,7 @@ void QLineEditPrivate::init(const QString& txt)
|
||||
{
|
||||
Q_Q(QLineEdit);
|
||||
control = new QLineControl(txt);
|
||||
control->setParent(q);
|
||||
control->setFont(q->font());
|
||||
QObject::connect(control, SIGNAL(textChanged(QString)),
|
||||
q, SIGNAL(textChanged(QString)));
|
||||
|
@ -84,7 +84,6 @@ public:
|
||||
|
||||
~QLineEditPrivate()
|
||||
{
|
||||
delete control;
|
||||
}
|
||||
|
||||
QLineControl *control;
|
||||
|
@ -103,7 +103,7 @@ SUBDIRS=\
|
||||
qlistview \
|
||||
qlistwidget \
|
||||
qlocale \
|
||||
#qlocalsocket \ # FIXME: uses qtscript, shouldn't be in qtbase
|
||||
#qlocalsocket \ # FIXME: uses qtscript (QTBUG-19242)
|
||||
qmacstyle \
|
||||
qmainwindow \
|
||||
qmatrixnxn \
|
||||
|
@ -3,7 +3,7 @@ SUBDIRS=\
|
||||
compiler \
|
||||
headersclean \
|
||||
maketestselftest \
|
||||
#moc \ # FIXME: cannot be built as part of qtbase, since it depends on qtsvg
|
||||
#moc \ # FIXME: uses qtsvg (QTBUG-19243)
|
||||
uic \
|
||||
qmake \
|
||||
rcc \
|
||||
|
@ -10,8 +10,7 @@ SUBDIRS=\
|
||||
qalgorithms \
|
||||
qcombobox \
|
||||
qcssparser \
|
||||
#qdatastream \ # FIXME: cannot be enabled by default in qtbase,
|
||||
# since it depends on qtsvg
|
||||
#qdatastream \ # FIXME: uses qtsvg (QTBUG-19244)
|
||||
qdir \
|
||||
qfocusevent \
|
||||
qimage \
|
||||
|
@ -1091,6 +1091,9 @@ void tst_QGraphicsAnchorLayout::setSpacing()
|
||||
#ifdef Q_WS_MAC
|
||||
QTest::qWait(200);
|
||||
#endif
|
||||
|
||||
// 21x21
|
||||
QCOMPARE(p->size(), QSizeF(41, 41));
|
||||
QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20));
|
||||
QCOMPARE(b->geometry(), QRectF(21, 0, 20, 20));
|
||||
QCOMPARE(c->geometry(), QRectF(0, 21, 41, 20));
|
||||
|
@ -62,6 +62,7 @@ private slots:
|
||||
void compressLayoutRequest();
|
||||
void automaticReparenting();
|
||||
void verifyActivate();
|
||||
void invalidate();
|
||||
void constructors();
|
||||
void alternativeLayoutItems();
|
||||
void ownership();
|
||||
@ -95,6 +96,14 @@ void tst_QGraphicsLayout::sizeHints()
|
||||
|
||||
}
|
||||
|
||||
enum FunctionType {
|
||||
SetGeometry = 0,
|
||||
Invalidate,
|
||||
NumFunctionTypes
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TestGraphicsWidget : public QGraphicsWidget {
|
||||
public:
|
||||
TestGraphicsWidget(QGraphicsWidget *parent = 0) : QGraphicsWidget(parent)
|
||||
@ -108,9 +117,28 @@ public:
|
||||
int eventCount(QEvent::Type type) {
|
||||
return m_eventCount.value(int(type));
|
||||
}
|
||||
|
||||
void clearEventCount() {
|
||||
m_eventCount.clear();
|
||||
}
|
||||
|
||||
void clearCounters() {
|
||||
m_eventCount.clear();
|
||||
functionCount.clear();
|
||||
}
|
||||
|
||||
void setGeometry(const QRectF &rect)
|
||||
{
|
||||
QGraphicsWidget::setGeometry(rect);
|
||||
++(functionCount[SetGeometry]);
|
||||
}
|
||||
|
||||
void callUpdateGeometry()
|
||||
{
|
||||
// updateGeometry() is protected
|
||||
QGraphicsWidget::updateGeometry();
|
||||
}
|
||||
QMap<FunctionType, int> functionCount;
|
||||
private:
|
||||
QMap<int, int> m_eventCount;
|
||||
};
|
||||
@ -122,6 +150,8 @@ void tst_QGraphicsLayout::compressLayoutRequest()
|
||||
TestGraphicsWidget *tw = new TestGraphicsWidget();
|
||||
scene.addItem(tw);
|
||||
view.show();
|
||||
|
||||
QTest::qWaitForWindowShown(&view);
|
||||
QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(tw);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QGraphicsWidget *gw = new QGraphicsWidget(tw);
|
||||
@ -217,17 +247,27 @@ class TestLayout : public QGraphicsLinearLayout
|
||||
TestLayout(QGraphicsLayoutItem *parent = 0)
|
||||
: QGraphicsLinearLayout(parent)
|
||||
{
|
||||
m_count = 0;
|
||||
setContentsMargins(0,0,0,0);
|
||||
setSpacing(0);
|
||||
}
|
||||
|
||||
void setGeometry(const QRectF &rect) {
|
||||
|
||||
++m_count;
|
||||
void setGeometry(const QRectF &rect)
|
||||
{
|
||||
++(functionCount[SetGeometry]);
|
||||
QGraphicsLinearLayout::setGeometry(rect);
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
++(functionCount[Invalidate]);
|
||||
QGraphicsLinearLayout::invalidate();
|
||||
}
|
||||
|
||||
int m_count;
|
||||
void clearCounters() {
|
||||
functionCount.clear();
|
||||
}
|
||||
|
||||
QMap<FunctionType, int> functionCount;
|
||||
};
|
||||
|
||||
void tst_QGraphicsLayout::verifyActivate()
|
||||
@ -242,15 +282,280 @@ void tst_QGraphicsLayout::verifyActivate()
|
||||
lout->addItem(w);
|
||||
window->setLayout(lout);
|
||||
|
||||
QCOMPARE(lout->m_count, 0);
|
||||
QCOMPARE(lout->functionCount[SetGeometry], 0);
|
||||
window->setVisible(false);
|
||||
QCOMPARE(lout->m_count, 0);
|
||||
QCOMPARE(lout->functionCount[SetGeometry], 0);
|
||||
window->setVisible(true);
|
||||
// on polish or the first time a widget is shown, the widget is resized.
|
||||
QCOMPARE(lout->m_count, 1);
|
||||
QCOMPARE(lout->functionCount[SetGeometry], 1);
|
||||
|
||||
}
|
||||
|
||||
static void clearAllCounters(TestGraphicsWidget *widget)
|
||||
{
|
||||
if (!widget)
|
||||
return;
|
||||
widget->clearCounters();
|
||||
TestLayout *layout = static_cast<TestLayout *>(widget->layout());
|
||||
if (layout) {
|
||||
layout->clearCounters();
|
||||
for (int i = layout->count() - 1; i >=0; --i) {
|
||||
QGraphicsLayoutItem *item = layout->itemAt(i);
|
||||
if (item->isLayout()) {
|
||||
// ### Not used ATM
|
||||
//TestLayout *lay = static_cast<TestLayout*>(static_cast<QGraphicsLayout*>(item));
|
||||
//clearAllCounters(lay);
|
||||
} else {
|
||||
TestGraphicsWidget *wid = static_cast<TestGraphicsWidget *>(item);
|
||||
clearAllCounters(wid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void activateAndReset(TestGraphicsWidget *widget)
|
||||
{
|
||||
QApplication::sendPostedEvents();
|
||||
QApplication::processEvents();
|
||||
if (widget->layout())
|
||||
widget->layout()->activate();
|
||||
clearAllCounters(widget);
|
||||
}
|
||||
|
||||
|
||||
void tst_QGraphicsLayout::invalidate()
|
||||
{
|
||||
QGraphicsLayout::setInstantInvalidatePropagation(true);
|
||||
QGraphicsScene scene;
|
||||
QGraphicsView view(&scene);
|
||||
|
||||
TestGraphicsWidget *a = new TestGraphicsWidget;
|
||||
a->setData(0, QString("a"));
|
||||
scene.addItem(a);
|
||||
TestLayout *alay = new TestLayout(a);
|
||||
TestGraphicsWidget *b = new TestGraphicsWidget;
|
||||
b->setData(0, QString("b"));
|
||||
alay->addItem(b);
|
||||
TestLayout *blay = new TestLayout(b);
|
||||
TestGraphicsWidget *e = new TestGraphicsWidget;
|
||||
e->setData(0, QString("e"));
|
||||
blay->addItem(e);
|
||||
|
||||
|
||||
TestGraphicsWidget *c = new TestGraphicsWidget;
|
||||
c->setData(0, QString("c"));
|
||||
alay->addItem(c);
|
||||
TestLayout *clay = new TestLayout(c);
|
||||
TestGraphicsWidget *f = new TestGraphicsWidget;
|
||||
f->setData(0, QString("f"));
|
||||
clay->addItem(f);
|
||||
|
||||
TestGraphicsWidget *d = new TestGraphicsWidget;
|
||||
d->setData(0, QString("d"));
|
||||
alay->addItem(d);
|
||||
TestLayout *dlay = new TestLayout(d);
|
||||
TestGraphicsWidget *g = new TestGraphicsWidget;
|
||||
g->setData(0, QString("g"));
|
||||
dlay->addItem(g);
|
||||
|
||||
view.show();
|
||||
|
||||
{
|
||||
clearAllCounters(a);
|
||||
|
||||
QCoreApplication::sendPostedEvents();
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
alay->activate();
|
||||
QCOMPARE(alay->isActivated(), true);
|
||||
QCOMPARE(blay->isActivated(), true);
|
||||
QCOMPARE(clay->isActivated(), true);
|
||||
QCOMPARE(dlay->isActivated(), true);
|
||||
}
|
||||
|
||||
{
|
||||
clearAllCounters(a);
|
||||
e->callUpdateGeometry();
|
||||
QCOMPARE(alay->isActivated(), false);
|
||||
QCOMPARE(blay->isActivated(), false);
|
||||
QCOMPARE(clay->isActivated(), true);
|
||||
QCOMPARE(dlay->isActivated(), true);
|
||||
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
|
||||
|
||||
// should only invalidate ascendants of e
|
||||
QCOMPARE(blay->functionCount[Invalidate], 1);
|
||||
QCOMPARE(alay->functionCount[Invalidate], 1);
|
||||
// not siblings
|
||||
QCOMPARE(clay->functionCount[Invalidate], 0);
|
||||
QCOMPARE(dlay->functionCount[Invalidate], 0);
|
||||
|
||||
QApplication::sendPostedEvents();
|
||||
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
activateAndReset(a);
|
||||
f->callUpdateGeometry();
|
||||
QCOMPARE(alay->isActivated(), false);
|
||||
QCOMPARE(blay->isActivated(), true);
|
||||
QCOMPARE(clay->isActivated(), false);
|
||||
QCOMPARE(dlay->isActivated(), true);
|
||||
|
||||
QCoreApplication::sendPostedEvents();
|
||||
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
|
||||
|
||||
QCOMPARE(a->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(alay->functionCount[SetGeometry], 1);
|
||||
|
||||
QCOMPARE(b->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(c->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(d->functionCount[SetGeometry], 1);
|
||||
// Since nothing really changed, blay and dlay don't need
|
||||
// to be resized.
|
||||
QCOMPARE(blay->functionCount[SetGeometry], 0);
|
||||
QCOMPARE(clay->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(dlay->functionCount[SetGeometry], 0);
|
||||
|
||||
QCOMPARE(f->functionCount[SetGeometry], 1);
|
||||
|
||||
QCOMPARE(a->size(), QSizeF(150, 50));
|
||||
}
|
||||
|
||||
{
|
||||
activateAndReset(a);
|
||||
f->setPreferredSize(QSizeF(60,50));
|
||||
QCOMPARE(alay->isActivated(), false);
|
||||
QCOMPARE(blay->isActivated(), true);
|
||||
QCOMPARE(clay->isActivated(), false);
|
||||
QCOMPARE(dlay->isActivated(), true);
|
||||
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCoreApplication::sendPostedEvents();
|
||||
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
|
||||
|
||||
QCOMPARE(a->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(alay->functionCount[SetGeometry], 1);
|
||||
|
||||
QCOMPARE(b->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(c->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(d->functionCount[SetGeometry], 1);
|
||||
// f actually got wider, need to rearrange its siblings
|
||||
QCOMPARE(blay->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(clay->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(dlay->functionCount[SetGeometry], 1);
|
||||
|
||||
QCOMPARE(e->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(f->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(g->functionCount[SetGeometry], 1);
|
||||
|
||||
QVERIFY(e->size().width() < f->size().width());
|
||||
QVERIFY(g->size().width() < f->size().width());
|
||||
}
|
||||
|
||||
{
|
||||
// resize f so much that it'll force a resize of the top widget
|
||||
// this will currently generate two setGeometry() calls on the child layout
|
||||
// of the top widget.
|
||||
activateAndReset(a);
|
||||
f->setPreferredSize(QSizeF());
|
||||
f->setMinimumSize(QSizeF(200,50));
|
||||
QCOMPARE(alay->isActivated(), false);
|
||||
QCOMPARE(blay->isActivated(), true);
|
||||
QCOMPARE(clay->isActivated(), false);
|
||||
QCOMPARE(dlay->isActivated(), true);
|
||||
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCoreApplication::sendPostedEvents();
|
||||
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
|
||||
|
||||
QCOMPARE(a->functionCount[SetGeometry], 1);
|
||||
|
||||
/* well, ideally one call to setGeometry(), but it will currently
|
||||
* get two calls to setGeometry():
|
||||
* 1. The first LayoutRequest will call activate() - that will call
|
||||
* setGeometry() on the layout. This geometry will be based on
|
||||
* the widget geometry which is not correct at this moment.
|
||||
* (it is still 150 wide)
|
||||
* 2. Next, we check if the widget is top level, and then we call
|
||||
* parentWidget->resize(parentWidget->size());
|
||||
* This will be adjusted to be minimum 200 pixels wide.
|
||||
* The new size will then be propagated down to the layout
|
||||
*
|
||||
*/
|
||||
QCOMPARE(alay->functionCount[SetGeometry], 2);
|
||||
|
||||
QCOMPARE(b->functionCount[SetGeometry], 2);
|
||||
QCOMPARE(c->functionCount[SetGeometry], 2);
|
||||
QCOMPARE(d->functionCount[SetGeometry], 2);
|
||||
// f actually got wider, need to rearrange its siblings
|
||||
QCOMPARE(blay->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(clay->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(dlay->functionCount[SetGeometry], 1);
|
||||
|
||||
QCOMPARE(e->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(f->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(g->functionCount[SetGeometry], 1);
|
||||
|
||||
QVERIFY(e->size().width() < f->size().width());
|
||||
QVERIFY(g->size().width() < f->size().width());
|
||||
}
|
||||
|
||||
{
|
||||
f->setPreferredSize(QSizeF());
|
||||
f->setMinimumSize(QSizeF());
|
||||
a->adjustSize();
|
||||
activateAndReset(a);
|
||||
// update two different leaf widgets,
|
||||
// eventCount and functionCount should never be >= 2
|
||||
e->callUpdateGeometry();
|
||||
g->callUpdateGeometry();
|
||||
QCOMPARE(alay->isActivated(), false);
|
||||
QCOMPARE(blay->isActivated(), false);
|
||||
QCOMPARE(clay->isActivated(), true);
|
||||
QCOMPARE(dlay->isActivated(), false);
|
||||
|
||||
QCoreApplication::sendPostedEvents();
|
||||
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
|
||||
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
|
||||
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 1);
|
||||
|
||||
QCOMPARE(a->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(alay->functionCount[SetGeometry], 1);
|
||||
|
||||
QCOMPARE(b->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(c->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(d->functionCount[SetGeometry], 1);
|
||||
// f actually got wider, need to rearrange its siblings
|
||||
QCOMPARE(blay->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(clay->functionCount[SetGeometry], 0);
|
||||
QCOMPARE(dlay->functionCount[SetGeometry], 1);
|
||||
|
||||
QCOMPARE(e->functionCount[SetGeometry], 1);
|
||||
QCOMPARE(f->functionCount[SetGeometry], 0);
|
||||
QCOMPARE(g->functionCount[SetGeometry], 1);
|
||||
|
||||
}
|
||||
|
||||
QGraphicsLayout::setInstantInvalidatePropagation(false);
|
||||
}
|
||||
|
||||
class Layout : public QGraphicsLayout
|
||||
{
|
||||
public:
|
||||
|
@ -94,6 +94,7 @@ private slots:
|
||||
void itemSpacing();
|
||||
void setStretchFactor_data();
|
||||
void setStretchFactor();
|
||||
void testStretch();
|
||||
void defaultStretchFactors_data();
|
||||
void defaultStretchFactors();
|
||||
void sizeHint_data();
|
||||
@ -667,6 +668,10 @@ void tst_QGraphicsLinearLayout::invalidate()
|
||||
layout.setContentsMargins(0, 0, 0, 0);
|
||||
view.show();
|
||||
widget->show();
|
||||
//QTest::qWait(1000);
|
||||
QTest::qWaitForWindowShown(&view);
|
||||
qApp->processEvents();
|
||||
layout.layoutRequest = 0;
|
||||
|
||||
layout.setContentsMargins(1, 2, 3, 4);
|
||||
QApplication::sendPostedEvents(0, 0);
|
||||
@ -1130,6 +1135,41 @@ void tst_QGraphicsLinearLayout::setStretchFactor()
|
||||
delete widget;
|
||||
}
|
||||
|
||||
void tst_QGraphicsLinearLayout::testStretch()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
QGraphicsView *view = new QGraphicsView(&scene);
|
||||
QGraphicsWidget *form = new QGraphicsWidget(0, Qt::Window);
|
||||
|
||||
scene.addItem(form);
|
||||
form->setMinimumSize(600, 600);
|
||||
form->setMaximumSize(600, 600);
|
||||
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Horizontal, form);
|
||||
QGraphicsWidget *w1 = new RectWidget;
|
||||
w1->setPreferredSize(100,100);
|
||||
w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
QGraphicsWidget *w2 = new RectWidget;
|
||||
w2->setPreferredSize(200,200);
|
||||
w2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
layout->setSpacing(0);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addItem(w1);
|
||||
layout->addStretch(2);
|
||||
layout->addItem(w2);
|
||||
QCOMPARE(layout->count(), 2);
|
||||
QVERIFY(layout->itemAt(0) == w1);
|
||||
QVERIFY(layout->itemAt(1) == w2);
|
||||
layout->activate();
|
||||
|
||||
//view->setSceneRect(-50, -50, 800, 800);
|
||||
//view->show();
|
||||
//QTest::qWaitForWindowShown(view);
|
||||
//QTest::qWait(5000);
|
||||
QCOMPARE(form->geometry().size(), QSizeF(600,600));
|
||||
QCOMPARE(w1->geometry(), QRectF(0, 0, 100, 100));
|
||||
QCOMPARE(w2->geometry(), QRectF(400, 0, 200, 200));
|
||||
}
|
||||
|
||||
void tst_QGraphicsLinearLayout::defaultStretchFactors_data()
|
||||
{
|
||||
QTest::addColumn<Qt::Orientation>("orientation");
|
||||
|
@ -186,7 +186,6 @@ private slots:
|
||||
void task250119_shortcutContext();
|
||||
void QT_BUG_6544_tabFocusFirstUnsetWhenRemovingItems();
|
||||
void QT_BUG_12056_tabFocusFirstUnsetWhenRemovingItems();
|
||||
void QT_BUG_13865_doublePaintWhenAddingASubItem();
|
||||
};
|
||||
|
||||
|
||||
@ -3367,46 +3366,6 @@ void tst_QGraphicsWidget::QT_BUG_12056_tabFocusFirstUnsetWhenRemovingItems()
|
||||
//This should not crash
|
||||
}
|
||||
|
||||
|
||||
struct GreenWidget : public QGraphicsWidget
|
||||
{
|
||||
GreenWidget() : count(0)
|
||||
{
|
||||
}
|
||||
|
||||
void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * )
|
||||
{
|
||||
count++;
|
||||
painter->setPen(Qt::green);
|
||||
painter->drawRect(option->rect.adjusted(0,0,-1,-1));
|
||||
}
|
||||
|
||||
int count;
|
||||
};
|
||||
|
||||
void tst_QGraphicsWidget::QT_BUG_13865_doublePaintWhenAddingASubItem()
|
||||
{
|
||||
QGraphicsScene scene;
|
||||
QGraphicsView view(&scene);
|
||||
QGraphicsWidget *widget = new QGraphicsWidget;
|
||||
widget->resize(100, 100);
|
||||
scene.addItem(widget);
|
||||
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(widget);
|
||||
|
||||
view.show();
|
||||
QTest::qWaitForWindowShown(&view);
|
||||
QApplication::processEvents();
|
||||
|
||||
|
||||
GreenWidget *sub = new GreenWidget;
|
||||
layout->addItem(sub);
|
||||
|
||||
QTest::qWait(100);
|
||||
QCOMPARE(sub->count, 1); //it should only be painted once
|
||||
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QGraphicsWidget)
|
||||
#include "tst_qgraphicswidget.moc"
|
||||
|
||||
|
@ -1,13 +1,18 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB,
|
||||
** a KDAB Group company, info@kdab.com,
|
||||
** author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com>
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (info@qt.nokia.com)
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
@ -20,17 +25,17 @@
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
3
tests/auto/qmake/testdata/subdir_via_pro_file_extra_target/simple/main.cpp
vendored
Normal file
3
tests/auto/qmake/testdata/subdir_via_pro_file_extra_target/simple/main.cpp
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
int main(int,char**)
|
||||
{
|
||||
}
|
5
tests/auto/qmake/testdata/subdir_via_pro_file_extra_target/simple/simple.pro
vendored
Normal file
5
tests/auto/qmake/testdata/subdir_via_pro_file_extra_target/simple/simple.pro
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
TEMPLATE = app
|
||||
SOURCES = main.cpp
|
||||
|
||||
extratarget.commands = @echo extra target worked OK
|
||||
QMAKE_EXTRA_TARGETS += extratarget
|
7
tests/auto/qmake/testdata/subdir_via_pro_file_extra_target/subdir.pro
vendored
Normal file
7
tests/auto/qmake/testdata/subdir_via_pro_file_extra_target/subdir.pro
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = simple
|
||||
|
||||
extratarget.CONFIG = recursive
|
||||
extratarget.recurse = $$SUBDIRS
|
||||
extratarget.recurse_target = extratarget
|
||||
QMAKE_EXTRA_TARGETS += extratarget
|
@ -0,0 +1,7 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = subdir.pro
|
||||
|
||||
extratarget.CONFIG = recursive
|
||||
extratarget.recurse = $$SUBDIRS
|
||||
extratarget.recurse_target = extratarget
|
||||
QMAKE_EXTRA_TARGETS += extratarget
|
@ -69,6 +69,7 @@ private slots:
|
||||
void simple_lib();
|
||||
void simple_dll();
|
||||
void subdirs();
|
||||
void subdir_via_pro_file_extra_target();
|
||||
void functions();
|
||||
void operators();
|
||||
void variables();
|
||||
@ -234,6 +235,19 @@ void tst_qmake::subdirs()
|
||||
QVERIFY( test_compiler.removeMakefile( workDir ) );
|
||||
}
|
||||
|
||||
void tst_qmake::subdir_via_pro_file_extra_target()
|
||||
{
|
||||
QString workDir = base_path + "/testdata/subdir_via_pro_file_extra_target";
|
||||
|
||||
QDir D;
|
||||
D.remove( workDir + "/Makefile");
|
||||
D.remove( workDir + "/Makefile.subdir");
|
||||
D.remove( workDir + "/simple/Makefile");
|
||||
D.remove( workDir + "/simple/Makefile.subdir");
|
||||
QVERIFY( test_compiler.qmake( workDir, "subdir_via_pro_file_extra_target" ));
|
||||
QVERIFY( test_compiler.make( workDir, "extratarget" ));
|
||||
}
|
||||
|
||||
void tst_qmake::functions()
|
||||
{
|
||||
QString workDir = base_path + "/testdata/functions";
|
||||
|
Loading…
Reference in New Issue
Block a user