diff demos/deform/pathdeform.d @ 310:5bcfe9e7db7f lifetime

Committing local changes
author maxter <spambox@d-coding.com>
date Wed, 23 Dec 2009 16:10:46 +0200
parents 0f23cdea249a
children
line wrap: on
line diff
--- a/demos/deform/pathdeform.d	Tue Sep 22 15:22:37 2009 +0000
+++ b/demos/deform/pathdeform.d	Wed Dec 23 16:10:46 2009 +0200
@@ -1,744 +1,744 @@
-/****************************************************************************
-**
-** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: Qt Software Information (qt-info@nokia.com)
-**
-** This file is part of the demonstration applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial Usage
-** Licensees holding valid Qt Commercial licenses may use this file in
-** accordance with the Qt Commercial License Agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Nokia.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain
-** additional rights. These rights are described in the Nokia Qt LGPL
-** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
-** package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-** If you are unsure which license is appropriate for your use, please
-** contact the sales department at qt-sales@nokia.com.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-module pathdeform;
-
-
-import arthurwidgets,
-	qt.gui.QPainterPath,
-	qt.gui.QApplication,
-	qt.gui.QMouseEvent,
-	qt.core.QDateTime,
-	qt.core.QTimerEvent,
-	qt.core.QBasicTimer,
-	qt.gui.QLayout,
-	qt.gui.QLineEdit,
-	qt.gui.QPainter,
-	qt.gui.QSlider,
-	qt.gui.QLabel,
-	qt.gui.QDesktopWidget,
-	qt.gui.QGroupBox,
-	qt.gui.QPushButton,
-	qt.gui.QVBoxLayout,
-	qt.gui.QGridLayout,
-	qt.gui.QHBoxLayout,
-	qt.gui.QRadialGradient,
-	qt.opengl.QGLFormat,
-	tango.math.Math;
-
-
-class PathDeformControls : QWidget
-{
-	private PathDeformRenderer m_renderer;
-
-	mixin Signal!("okPressed");
-	mixin Signal!("quitPressed");
-
-	this(QWidget parent, PathDeformRenderer renderer, bool smallScreen)
-	{
-		super(parent);
-		m_renderer = renderer;
-
-		if (smallScreen)
-			layoutForSmallScreen();
-		else
-			layoutForDesktop();
-	}
-
-	void layoutForDesktop()
-	{
-		QGroupBox mainGroup = new QGroupBox(this);
-		mainGroup.setTitle(tr("Controls"));
-
-		QGroupBox radiusGroup = new QGroupBox(mainGroup);
-		radiusGroup.setTitle(tr("Lens Radius"));
-		QSlider radiusSlider = new QSlider(Qt.Horizontal, radiusGroup);
-		radiusSlider.setRange(15, 150);
-		radiusSlider.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed);
-
-		QGroupBox deformGroup = new QGroupBox(mainGroup);
-		deformGroup.setTitle(tr("Deformation"));
-		QSlider deformSlider = new QSlider(Qt.Horizontal, deformGroup);
-		deformSlider.setRange(-100, 100);
-		deformSlider.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed);
-
-		QGroupBox fontSizeGroup = new QGroupBox(mainGroup);
-		fontSizeGroup.setTitle(tr("Font Size"));
-		QSlider fontSizeSlider = new QSlider(Qt.Horizontal, fontSizeGroup);
-		fontSizeSlider.setRange(16, 200);
-		fontSizeSlider.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed);
-
-		QGroupBox textGroup = new QGroupBox(mainGroup);
-		textGroup.setTitle(tr("Text"));
-		QLineEdit textInput = new QLineEdit(textGroup);
-
-		QPushButton animateButton = new QPushButton(mainGroup);
-		animateButton.setText(tr("Animated"));
-		animateButton.setCheckable(true);
-
-		QPushButton showSourceButton = new QPushButton(mainGroup);
-		showSourceButton.setText(tr("Show Source"));
-
-		version (QT_OPENGL_SUPPORT)
-		{
-			QPushButton enableOpenGLButton = new QPushButton(mainGroup);
-			enableOpenGLButton.setText(tr("Use OpenGL"));
-			enableOpenGLButton.setCheckable(true);
-			enableOpenGLButton.setChecked(m_renderer.usesOpenGL());
-			if (!QGLFormat.hasOpenGL())
-				enableOpenGLButton.hide();
-		}
-
-		QPushButton whatsThisButton = new QPushButton(mainGroup);
-		whatsThisButton.setText(tr("What's This?"));
-		whatsThisButton.setCheckable(true);
-
-		mainGroup.setFixedWidth(180);
-
-		QVBoxLayout mainGroupLayout = new QVBoxLayout(mainGroup);
-		mainGroupLayout.addWidget(radiusGroup);
-		mainGroupLayout.addWidget(deformGroup);
-		mainGroupLayout.addWidget(fontSizeGroup);
-		mainGroupLayout.addWidget(textGroup);
-		mainGroupLayout.addWidget(animateButton);
-		mainGroupLayout.addStretch(1);
-		version (QT_OPENGL_SUPPORT)
-		{
-			mainGroupLayout.addWidget(enableOpenGLButton);
-		}
-		mainGroupLayout.addWidget(showSourceButton);
-		mainGroupLayout.addWidget(whatsThisButton);
-
-		QVBoxLayout radiusGroupLayout = new QVBoxLayout(radiusGroup);
-		radiusGroupLayout.addWidget(radiusSlider);
-
-		QVBoxLayout deformGroupLayout = new QVBoxLayout(deformGroup);
-		deformGroupLayout.addWidget(deformSlider);
-
-		QVBoxLayout fontSizeGroupLayout = new QVBoxLayout(fontSizeGroup);
-		fontSizeGroupLayout.addWidget(fontSizeSlider);
-
-		QVBoxLayout textGroupLayout = new QVBoxLayout(textGroup);
-		textGroupLayout.addWidget(textInput);
-
-		QVBoxLayout mainLayout = new QVBoxLayout(this);
-		mainLayout.addWidget(mainGroup);
-		mainLayout.setMargin(0);
-
-		radiusSlider.valueChanged.connect(&m_renderer.setRadius);
-		deformSlider.valueChanged.connect(&m_renderer.setIntensity);
-		fontSizeSlider.valueChanged.connect(&m_renderer.setFontSize);
-		animateButton.clicked.connect(&m_renderer.setAnimated);
-		version (QT_OPENGL_SUPPORT)
-		{
-			enableOpenGLButton.clicked.connect(&m_renderer.enableOpenGL);
-		}
-
-		textInput.textChanged.connect(&m_renderer.setText);
-		m_renderer.descriptionEnabledChanged.connect(&whatsThisButton.setChecked);
-		whatsThisButton.clicked.connect(&m_renderer.setDescriptionEnabled);
-		showSourceButton.clicked.connect(&m_renderer.showSource);
-
-		animateButton.animateClick();
-		deformSlider.setValue(80);
-		fontSizeSlider.setValue(120);
-		radiusSlider.setValue(100);
-		textInput.setText(tr("Qt"));
-	}
-
-	void layoutForSmallScreen()
-	{
-		QGroupBox mainGroup = new QGroupBox(this);
-		mainGroup.setTitle(tr("Controls"));
-
-		QLabel radiusLabel = new QLabel(mainGroup);
-		radiusLabel.setText(tr("Lens Radius:"));
-		QSlider radiusSlider = new QSlider(Qt.Horizontal, mainGroup);
-		radiusSlider.setRange(15, 150);
-		radiusSlider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed);
-
-		QLabel deformLabel = new QLabel(mainGroup);
-		deformLabel.setText(tr("Deformation:"));
-		QSlider deformSlider = new QSlider(Qt.Horizontal, mainGroup);
-		deformSlider.setRange(-100, 100);
-		deformSlider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed);
-
-		QLabel fontSizeLabel = new QLabel(mainGroup);
-		fontSizeLabel.setText(tr("Font Size:"));
-		QSlider fontSizeSlider = new QSlider(Qt.Horizontal, mainGroup);
-		fontSizeSlider.setRange(16, 200);
-		fontSizeSlider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed);
-
-		QPushButton animateButton = new QPushButton(tr("Animated"), mainGroup);
-		animateButton.setCheckable(true);
-
-		version (QT_OPENGL_SUPPORT)
-		{
-			QPushButton enableOpenGLButton = new QPushButton(mainGroup);
-			enableOpenGLButton.setText(tr("Use OpenGL"));
-			enableOpenGLButton.setCheckable(true);
-			enableOpenGLButton.setChecked(m_renderer.usesOpenGL());
-			if (!QGLFormat.hasOpenGL())
-				enableOpenGLButton.hide();
-		}
-
-		QPushButton quitButton = new QPushButton(tr("Quit"), mainGroup);
-		QPushButton okButton = new QPushButton(tr("OK"), mainGroup);
-
-
-		QGridLayout mainGroupLayout = new QGridLayout(mainGroup);
-		mainGroupLayout.setMargin(0);
-		mainGroupLayout.addWidget(radiusLabel, 0, 0, Qt.AlignRight);
-		mainGroupLayout.addWidget(radiusSlider, 0, 1);
-		mainGroupLayout.addWidget(deformLabel, 1, 0, Qt.AlignRight);
-		mainGroupLayout.addWidget(deformSlider, 1, 1);
-		mainGroupLayout.addWidget(fontSizeLabel, 2, 0, Qt.AlignRight);
-		mainGroupLayout.addWidget(fontSizeSlider, 2, 1);
-		mainGroupLayout.addWidget(animateButton, 3,0, 1,2);
-		version (QT_OPENGL_SUPPORT)
-		{
-			mainGroupLayout.addWidget(enableOpenGLButton, 4,0, 1,2);
-		}
-
-		QVBoxLayout mainLayout = new QVBoxLayout(this);
-		mainLayout.addWidget(mainGroup);
-		mainLayout.addStretch(1);
-		mainLayout.addWidget(okButton);
-		mainLayout.addWidget(quitButton);
-
-		quitButton.clicked.connect(&emitQuitSignal);
-		okButton.clicked.connect(&emitOkSignal);
-		radiusSlider.valueChanged.connect(&m_renderer.setRadius);
-		deformSlider.valueChanged.connect(&m_renderer.setIntensity);
-		fontSizeSlider.valueChanged.connect(&m_renderer.setFontSize);
-		animateButton.clicked.connect(&m_renderer.setAnimated);
-		version (QT_OPENGL_SUPPORT)
-		{
-			enableOpenGLButton.clicked.connect(&m_renderer.enableOpenGL);
-		}
-
-		animateButton.animateClick();
-		deformSlider.setValue(80);
-		fontSizeSlider.setValue(120);
-
-		QRect screen_size = QApplication.desktop().screenGeometry();
-		radiusSlider.setValue(qMin(screen_size.width(), screen_size.height())/5);
-		m_renderer.setText(tr("Qt"));
-	}
-
-	void emitQuitSignal()
-	{
-		quitPressed.emit;
-	}
-	
-	void emitOkSignal()
-	{
-		okPressed.emit;
-	}
-}
-
-
-class PathDeformWidget : QWidget
-{
-private:
-
-	PathDeformRenderer m_renderer;
-	PathDeformControls m_controls;
-
-public:
-
-	this(QWidget parent, bool smallScreen)
-	{
-		super(parent);
-
-		setWindowTitle(tr("Vector Deformation"));
-
-		m_renderer = new PathDeformRenderer(this, smallScreen);
-		m_renderer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding);
-
-		// Layouts
-		QHBoxLayout mainLayout = new QHBoxLayout(this);
-		mainLayout.addWidget(m_renderer);
-
-		m_controls = new PathDeformControls(null, m_renderer, smallScreen);
-		m_controls.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum);
-
-		if (!smallScreen)
-		mainLayout.addWidget(m_controls);
-
-		m_renderer.loadSourceFile(":res/deform/pathdeform.d");
-		m_renderer.loadDescription(":res/deform/pathdeform.html");
-		m_renderer.setDescriptionEnabled(false);
-
-		m_renderer.clicked.connect(&showControls);
-		m_controls.okPressed.connect(&hideControls);
-
-		m_controls.quitPressed.connect(&QApplication.quit);
-	}
-
-	void showControls()
-	{
-		m_controls.showFullScreen;
-	}
-
-	void hideControls()
-	{
-		m_controls.hide;
-	}
-
-	void setWidgetStyle(QStyle style) // TODO: QWidget.setStyle is not virtual
-	{
-		super.setStyle(style);
-		if (m_controls)
-		{
-			m_controls.setStyle(style);
-
-			QWidget[] widgets = m_controls.findChildren!(QWidget);
-			foreach (w; widgets)
-				w.setStyle(style);
-		}
-	}
-}
-
-private QRect circle_bounds(QPointF center, qreal radius, qreal compensation)
-{
-	return QRect(qRound(center.x() - radius - compensation),
-		qRound(center.y() - radius - compensation),
-		qRound((radius + compensation) * 2),
-		qRound((radius + compensation) * 2));
-}
-
-enum
-{
-	LENS_EXTENT = 10
-}
-
-class PathDeformRenderer : ArthurFrame
-{
-private:
-
-	QBasicTimer m_repaintTimer;
-	//     QBasicTimer m_fpsTimer;
-	//     int m_fpsCounter;
-	QTime m_repaintTracker;
-
-	QPainterPath[] m_paths;
-	QPointF[] m_advances;
-	QRectF m_pathBounds;
-	string m_text;
-
-	QPixmap m_lens_pixmap;
-	QImage m_lens_image;
-
-	int m_fontSize;
-	bool m_animated;
-
-	qreal m_intensity;
-	qreal m_radius;
-	QPointF m_pos;
-	QPointF m_offset;
-	QPointF m_direction;
-	QPointF m_mousePress;
-	bool m_mouseDrag;
-	bool m_smallScreen;
-
-public:
-
-	mixin Signal!("clicked");
-
-	this(QWidget widget, bool smallScreen)
-	{
-		super(widget);
-		m_radius = 100;
-		m_pos = QPointF(m_radius, m_radius);
-		m_direction = QPointF(1, 1);
-		m_fontSize = 24;
-		m_animated = true;
-		m_repaintTimer.start(25, this);
-		m_repaintTracker.start();
-		m_intensity = 100;
-		m_smallScreen = smallScreen;
-
-		//     m_fpsTimer.start(1000, this);
-		//     m_fpsCounter = 0;
-
-		generateLensPixmap();
-	}
-
-	void setFontSize(int fontSize)
-	{
-		m_fontSize = fontSize;
-		setText(m_text);
-	}
-
-	override QSize sizeHint() { return QSize(600, 500); }
-
-	bool animated() { return m_animated; }
-	int radius() { return cast(int) m_radius; }
-	int fontSize() { return m_fontSize; }
-	int intensity() { return cast(int) m_intensity; }
-	string text() { return m_text; }
-
-	void setText(string text)
-	{
-		m_text = text;
-
-		auto f = new QFont("times new roman,utopia");
-		f.setStyleStrategy(QFont.ForceOutline);
-		f.setPointSize(m_fontSize);
-		f.setStyleHint(QFont.Times);
-
-		m_paths = null;
-		m_pathBounds = QRectF();
-
-		QPointF advance;
-		
-		bool do_quick = true;
-		for (int i = 0; i < text.length; ++i) {
-			if (text[i].unicode() >= 0x4ff && text.at(i).unicode() <= 0x1e00) {
-				do_quick = false;
-				break;
-			}
-		}
-
-		if (do_quick) {
-			for (int i = 0; i < text.length; ++i) {
-				QPainterPath path = new QPainterPath;
-				path.addText(advance, f, text.mid(i, 1));
-				m_pathBounds |= path.boundingRect();
-				m_paths ~= path;
-				advance += QPointF(fm.width(text.mid(i, 1)), 0);
-			}
-		} else {
-			QPainterPath path = new QPainterPath;
-			path.addText(advance, f, text);
-			m_pathBounds |= path.boundingRect();
-			m_paths ~= path;
-		}
-
-		for (int i = 0; i < m_paths.length; ++i)
-			m_paths[i] = m_paths[i] * (new QMatrix(1, 0, 0, 1, -m_pathBounds.x(), -m_pathBounds.y()));
-
-		update();
-	}
-
-	void generateLensPixmap()
-	{
-		qreal rad = m_radius + LENS_EXTENT;
-
-		QRect bounds = circle_bounds(QPointF(), rad, 0);
-
-		QPainter painter = new QPainter;
-
-		if (preferImage()) {
-			m_lens_image = new QImage(bounds.size(), QImage.Format_ARGB32_Premultiplied);
-			m_lens_image.fill(0);
-			painter.begin(m_lens_image);
-		} else {
-			m_lens_pixmap = new QPixmap(bounds.size());
-			m_lens_pixmap.fill(new QColor(Qt.transparent));
-			painter.begin(m_lens_pixmap);
-		}
-
-		auto gr = new QRadialGradient(rad, rad, rad, 3 * rad / 5, 3 * rad / 5);
-		gr.setColorAt(0.0, new QColor(255, 255, 255, 191));
-		gr.setColorAt(0.2, new QColor(255, 255, 127, 191));
-		gr.setColorAt(0.9, new QColor(150, 150, 200, 63));
-		gr.setColorAt(0.95, new QColor(0, 0, 0, 127));
-		gr.setColorAt(1, new QColor(0, 0, 0, 0));
-		painter.setRenderHint(QPainter.Antialiasing);
-		painter.setBrush(gr);
-		painter.setPen(Qt.NoPen);
-		painter.drawEllipse(0, 0, bounds.width(), bounds.height());
-	}
-
-	void setAnimated(bool animated)
-	{
-		m_animated = animated;
-
-		if (m_animated) {
-			//         m_fpsTimer.start(1000, this);
-			//         m_fpsCounter = 0;
-			m_repaintTimer.start(25, this);
-			m_repaintTracker.start();
-		} else {
-			//         m_fpsTimer.stop();
-			m_repaintTimer.stop();
-		}
-	}
-
-	override void timerEvent(QTimerEvent e)
-	{
-		if (e.timerId == m_repaintTimer.timerId) 
-		{
-			if ((QLineF(QPointF(0,0), m_direction)).length() > 1)
-				m_direction *= 0.995;
-			qreal time = m_repaintTracker.restart();
-
-			QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
-
-			qreal dx = m_direction.x();
-			qreal dy = m_direction.y();
-			if (time > 0) {
-				dx = dx * time * .1;
-				dy = dy * time * .1;
-			}
-
-			m_pos += QPointF(dx, dy);
-
-			if (m_pos.x() - m_radius < 0) {
-				m_direction.x = -m_direction.x;
-				m_pos.x = m_radius;
-			} else if (m_pos.x + m_radius > width) {
-				m_direction.x = -m_direction.x;
-				m_pos.x = width - m_radius;
-			}
-
-			if (m_pos.y - m_radius < 0) {
-				m_direction.y = -m_direction.y;
-				m_pos.y = m_radius;
-			} else if (m_pos.y + m_radius > height) {
-				m_direction.y = -m_direction.y;
-				m_pos.y = height - m_radius;
-			}
-
-			void noGLUpdate()
-			{
-				QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
-				update(rectAfter.united(rectBefore));
-				QApplication.syncX();
-			}
-
-			version (QT_OPENGL_SUPPORT)
-			{
-				if (usesOpenGL()) {
-					update;
-				}
-				else
-					noGLUpdate;
-			}
-			else
-				noGLUpdate;
-		}
-		//     else if (e.timerId() == m_fpsTimer.timerId()) {
-		//         printf("fps: %d\n", m_fpsCounter);
-		//         emit frameRate(m_fpsCounter);
-		//         m_fpsCounter = 0;
-
-		//     }
-	}
-
-	override void mousePressEvent(QMouseEvent e)
-	{
-		setDescriptionEnabled(false);
-
-		m_repaintTimer.stop();
-		m_offset = QPointF();
-		if ((QLineF(m_pos, QPointF(e.pos))).length <= m_radius)
-			m_offset = m_pos - QPointF(e.pos);
-
-		m_mousePress = QPointF(e.pos);
-
-		// If we're not running in small screen mode, always assume we're dragging
-		m_mouseDrag = !m_smallScreen;
-
-		mouseMoveEvent(e);
-	}
-
-	override void mouseReleaseEvent(QMouseEvent e)
-	{
-		if (e.buttons() == Qt.NoButton && m_animated) {
-			m_repaintTimer.start(10, this);
-			m_repaintTracker.start();
-		}
-
-		if (!m_mouseDrag && m_smallScreen)
-			clicked.emit;
-	}
-
-	override void mouseMoveEvent(QMouseEvent e)
-	{
-		auto epos = QPointF(e.pos);
-
-		if (!m_mouseDrag && (QLineF(m_mousePress, QPointF(e.pos))).length() > 25.0)
-		m_mouseDrag = true;
-
-		if (m_mouseDrag)
-		{
-			QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
-			if (e.type() == QEvent.MouseMove) {
-				QLineF line = QLineF(m_pos, epos + m_offset);
-				line.setLength(line.length() * .1);
-				auto dir = QPointF(line.dx(), line.dy());
-				m_direction = (m_direction + dir) / 2;
-			}
-			m_pos = epos + m_offset;
-
-			void noGLUpdate()
-			{
-				QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
-				update(rectBefore.united(rectAfter));
-			}
-
-			version (QT_OPENGL_SUPPORT)
-			{
-				if (usesOpenGL()) {
-					update;
-				} else
-					noGLUpdate;
-			}
-			else
-				noGLUpdate;
-		}
-	}
-
-	QPainterPath lensDeform(QPainterPath source, QPointF offset)
-	{
-		auto path = new QPainterPath;
-		path.addPath(source);
-
-		qreal flip = m_intensity / 100.0;
-
-		for (int i=0; i<path.elementCount; ++i)
-		{
-			auto e = path.elementAt(i);
-
-			qreal x = e.x + offset.x();
-			qreal y = e.y + offset.y();
-
-			qreal dx = x - m_pos.x();
-			qreal dy = y - m_pos.y();
-			qreal len = m_radius - sqrt(dx * dx + dy * dy);
-
-			if (len > 0) {
-				path.setElementPositionAt(i,
-					  x + flip * dx * len / m_radius,
-					  y + flip * dy * len / m_radius);
-			} else {
-				path.setElementPositionAt(i, x, y);
-			}
-		}
-
-		return path;
-	}
-
-	override void paint(QPainter painter)
-	{
-		int pad_x = 5;
-		int pad_y = 5;
-
-		int skip_x = qRound(m_pathBounds.width() + pad_x + m_fontSize/2);
-		int skip_y = qRound(m_pathBounds.height() + pad_y);
-
-		painter.setPen(Qt.NoPen);
-		painter.setBrush(new QColor(Qt.black));
-
-		auto clip = painter.clipPath().boundingRect();
-
-		int overlap = pad_x / 2;
-
-		for (int start_y=0; start_y < height(); start_y += skip_y)
-		{
-			if (start_y > clip.bottom())
-			break;
-
-			int start_x = -overlap;
-			for (; start_x < width(); start_x += skip_x)
-			{
-				if (start_y + skip_y >= clip.top() &&
-					start_x + skip_x >= clip.left() &&
-					start_x <= clip.right())
-				{
-					for (int i=0; i<m_paths.length; ++i) {
-						QPainterPath path = lensDeform(m_paths[i], QPointF(start_x, start_y));
-						painter.drawPath(path);
-					}
-				}
-			}
-			overlap = skip_x - (start_x - width());
-		}
-
-		if (preferImage) {
-			painter.drawImage(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT), m_lens_image);
-		} else {
-			painter.drawPixmap(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT), m_lens_pixmap);
-		}
-	}
-
-	void setRadius(int radius)
-	{
-		qreal max = max(m_radius, cast(qreal)radius);
-		m_radius = radius;
-		generateLensPixmap();
-		if (!m_animated || m_radius < max)
-		{
-			auto noGLUpdate = (){ update(circle_bounds(m_pos, max, m_fontSize)); };
-
-			version (QT_OPENGL_SUPPORT)
-			{
-				if (usesOpenGL())
-					update();
-				else
-					noGLUpdate();
-			}
-			else
-				noGLUpdate();
-		}
-	}
-
-	void setIntensity(int intensity)
-	{
-		m_intensity = intensity;
-		if (!m_animated)
-		{
-			auto noGLUpdate = (){ update(circle_bounds(m_pos, m_radius, m_fontSize)); };
-
-			version (QT_OPENGL_SUPPORT)
-			{
-				if (usesOpenGL()) {
-					update();
-				} else
-					noGLUpdate();
-			}
-			else
-				noGLUpdate();
-		}
-	}
-}
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+module pathdeform;
+
+
+import arthurwidgets,
+	qt.gui.QPainterPath,
+	qt.gui.QApplication,
+	qt.gui.QMouseEvent,
+	qt.core.QDateTime,
+	qt.core.QTimerEvent,
+	qt.core.QBasicTimer,
+	qt.gui.QLayout,
+	qt.gui.QLineEdit,
+	qt.gui.QPainter,
+	qt.gui.QSlider,
+	qt.gui.QLabel,
+	qt.gui.QDesktopWidget,
+	qt.gui.QGroupBox,
+	qt.gui.QPushButton,
+	qt.gui.QVBoxLayout,
+	qt.gui.QGridLayout,
+	qt.gui.QHBoxLayout,
+	qt.gui.QRadialGradient,
+	qt.opengl.QGLFormat,
+	tango.math.Math;
+
+
+class PathDeformControls : QWidget
+{
+	private PathDeformRenderer m_renderer;
+
+	mixin Signal!("okPressed");
+	mixin Signal!("quitPressed");
+
+	this(QWidget parent, PathDeformRenderer renderer, bool smallScreen)
+	{
+		super(parent);
+		m_renderer = renderer;
+
+		if (smallScreen)
+			layoutForSmallScreen();
+		else
+			layoutForDesktop();
+	}
+
+	void layoutForDesktop()
+	{
+		QGroupBox mainGroup = new QGroupBox(this);
+		mainGroup.setTitle(tr("Controls"));
+
+		QGroupBox radiusGroup = new QGroupBox(mainGroup);
+		radiusGroup.setTitle(tr("Lens Radius"));
+		QSlider radiusSlider = new QSlider(Qt.Horizontal, radiusGroup);
+		radiusSlider.setRange(15, 150);
+		radiusSlider.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed);
+
+		QGroupBox deformGroup = new QGroupBox(mainGroup);
+		deformGroup.setTitle(tr("Deformation"));
+		QSlider deformSlider = new QSlider(Qt.Horizontal, deformGroup);
+		deformSlider.setRange(-100, 100);
+		deformSlider.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed);
+
+		QGroupBox fontSizeGroup = new QGroupBox(mainGroup);
+		fontSizeGroup.setTitle(tr("Font Size"));
+		QSlider fontSizeSlider = new QSlider(Qt.Horizontal, fontSizeGroup);
+		fontSizeSlider.setRange(16, 200);
+		fontSizeSlider.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed);
+
+		QGroupBox textGroup = new QGroupBox(mainGroup);
+		textGroup.setTitle(tr("Text"));
+		QLineEdit textInput = new QLineEdit(textGroup);
+
+		QPushButton animateButton = new QPushButton(mainGroup);
+		animateButton.setText(tr("Animated"));
+		animateButton.setCheckable(true);
+
+		QPushButton showSourceButton = new QPushButton(mainGroup);
+		showSourceButton.setText(tr("Show Source"));
+
+		version (QT_OPENGL_SUPPORT)
+		{
+			QPushButton enableOpenGLButton = new QPushButton(mainGroup);
+			enableOpenGLButton.setText(tr("Use OpenGL"));
+			enableOpenGLButton.setCheckable(true);
+			enableOpenGLButton.setChecked(m_renderer.usesOpenGL());
+			if (!QGLFormat.hasOpenGL())
+				enableOpenGLButton.hide();
+		}
+
+		QPushButton whatsThisButton = new QPushButton(mainGroup);
+		whatsThisButton.setText(tr("What's This?"));
+		whatsThisButton.setCheckable(true);
+
+		mainGroup.setFixedWidth(180);
+
+		QVBoxLayout mainGroupLayout = new QVBoxLayout(mainGroup);
+		mainGroupLayout.addWidget(radiusGroup);
+		mainGroupLayout.addWidget(deformGroup);
+		mainGroupLayout.addWidget(fontSizeGroup);
+		mainGroupLayout.addWidget(textGroup);
+		mainGroupLayout.addWidget(animateButton);
+		mainGroupLayout.addStretch(1);
+		version (QT_OPENGL_SUPPORT)
+		{
+			mainGroupLayout.addWidget(enableOpenGLButton);
+		}
+		mainGroupLayout.addWidget(showSourceButton);
+		mainGroupLayout.addWidget(whatsThisButton);
+
+		QVBoxLayout radiusGroupLayout = new QVBoxLayout(radiusGroup);
+		radiusGroupLayout.addWidget(radiusSlider);
+
+		QVBoxLayout deformGroupLayout = new QVBoxLayout(deformGroup);
+		deformGroupLayout.addWidget(deformSlider);
+
+		QVBoxLayout fontSizeGroupLayout = new QVBoxLayout(fontSizeGroup);
+		fontSizeGroupLayout.addWidget(fontSizeSlider);
+
+		QVBoxLayout textGroupLayout = new QVBoxLayout(textGroup);
+		textGroupLayout.addWidget(textInput);
+
+		QVBoxLayout mainLayout = new QVBoxLayout(this);
+		mainLayout.addWidget(mainGroup);
+		mainLayout.setMargin(0);
+
+		radiusSlider.valueChanged.connect(&m_renderer.setRadius);
+		deformSlider.valueChanged.connect(&m_renderer.setIntensity);
+		fontSizeSlider.valueChanged.connect(&m_renderer.setFontSize);
+		animateButton.clicked.connect(&m_renderer.setAnimated);
+		version (QT_OPENGL_SUPPORT)
+		{
+			enableOpenGLButton.clicked.connect(&m_renderer.enableOpenGL);
+		}
+
+		textInput.textChanged.connect(&m_renderer.setText);
+		m_renderer.descriptionEnabledChanged.connect(&whatsThisButton.setChecked);
+		whatsThisButton.clicked.connect(&m_renderer.setDescriptionEnabled);
+		showSourceButton.clicked.connect(&m_renderer.showSource);
+
+		animateButton.animateClick();
+		deformSlider.setValue(80);
+		fontSizeSlider.setValue(120);
+		radiusSlider.setValue(100);
+		textInput.setText(tr("Qt"));
+	}
+
+	void layoutForSmallScreen()
+	{
+		QGroupBox mainGroup = new QGroupBox(this);
+		mainGroup.setTitle(tr("Controls"));
+
+		QLabel radiusLabel = new QLabel(mainGroup);
+		radiusLabel.setText(tr("Lens Radius:"));
+		QSlider radiusSlider = new QSlider(Qt.Horizontal, mainGroup);
+		radiusSlider.setRange(15, 150);
+		radiusSlider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed);
+
+		QLabel deformLabel = new QLabel(mainGroup);
+		deformLabel.setText(tr("Deformation:"));
+		QSlider deformSlider = new QSlider(Qt.Horizontal, mainGroup);
+		deformSlider.setRange(-100, 100);
+		deformSlider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed);
+
+		QLabel fontSizeLabel = new QLabel(mainGroup);
+		fontSizeLabel.setText(tr("Font Size:"));
+		QSlider fontSizeSlider = new QSlider(Qt.Horizontal, mainGroup);
+		fontSizeSlider.setRange(16, 200);
+		fontSizeSlider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed);
+
+		QPushButton animateButton = new QPushButton(tr("Animated"), mainGroup);
+		animateButton.setCheckable(true);
+
+		version (QT_OPENGL_SUPPORT)
+		{
+			QPushButton enableOpenGLButton = new QPushButton(mainGroup);
+			enableOpenGLButton.setText(tr("Use OpenGL"));
+			enableOpenGLButton.setCheckable(true);
+			enableOpenGLButton.setChecked(m_renderer.usesOpenGL());
+			if (!QGLFormat.hasOpenGL())
+				enableOpenGLButton.hide();
+		}
+
+		QPushButton quitButton = new QPushButton(tr("Quit"), mainGroup);
+		QPushButton okButton = new QPushButton(tr("OK"), mainGroup);
+
+
+		QGridLayout mainGroupLayout = new QGridLayout(mainGroup);
+		mainGroupLayout.setMargin(0);
+		mainGroupLayout.addWidget(radiusLabel, 0, 0, Qt.AlignRight);
+		mainGroupLayout.addWidget(radiusSlider, 0, 1);
+		mainGroupLayout.addWidget(deformLabel, 1, 0, Qt.AlignRight);
+		mainGroupLayout.addWidget(deformSlider, 1, 1);
+		mainGroupLayout.addWidget(fontSizeLabel, 2, 0, Qt.AlignRight);
+		mainGroupLayout.addWidget(fontSizeSlider, 2, 1);
+		mainGroupLayout.addWidget(animateButton, 3,0, 1,2);
+		version (QT_OPENGL_SUPPORT)
+		{
+			mainGroupLayout.addWidget(enableOpenGLButton, 4,0, 1,2);
+		}
+
+		QVBoxLayout mainLayout = new QVBoxLayout(this);
+		mainLayout.addWidget(mainGroup);
+		mainLayout.addStretch(1);
+		mainLayout.addWidget(okButton);
+		mainLayout.addWidget(quitButton);
+
+		quitButton.clicked.connect(&emitQuitSignal);
+		okButton.clicked.connect(&emitOkSignal);
+		radiusSlider.valueChanged.connect(&m_renderer.setRadius);
+		deformSlider.valueChanged.connect(&m_renderer.setIntensity);
+		fontSizeSlider.valueChanged.connect(&m_renderer.setFontSize);
+		animateButton.clicked.connect(&m_renderer.setAnimated);
+		version (QT_OPENGL_SUPPORT)
+		{
+			enableOpenGLButton.clicked.connect(&m_renderer.enableOpenGL);
+		}
+
+		animateButton.animateClick();
+		deformSlider.setValue(80);
+		fontSizeSlider.setValue(120);
+
+		QRect screen_size = QApplication.desktop().screenGeometry();
+		radiusSlider.setValue(qMin(screen_size.width(), screen_size.height())/5);
+		m_renderer.setText(tr("Qt"));
+	}
+
+	void emitQuitSignal()
+	{
+		quitPressed.emit;
+	}
+	
+	void emitOkSignal()
+	{
+		okPressed.emit;
+	}
+}
+
+
+class PathDeformWidget : QWidget
+{
+private:
+
+	PathDeformRenderer m_renderer;
+	PathDeformControls m_controls;
+
+public:
+
+	this(QWidget parent, bool smallScreen)
+	{
+		super(parent);
+
+		setWindowTitle(tr("Vector Deformation"));
+
+		m_renderer = new PathDeformRenderer(this, smallScreen);
+		m_renderer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding);
+
+		// Layouts
+		QHBoxLayout mainLayout = new QHBoxLayout(this);
+		mainLayout.addWidget(m_renderer);
+
+		m_controls = new PathDeformControls(null, m_renderer, smallScreen);
+		m_controls.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum);
+
+		if (!smallScreen)
+		mainLayout.addWidget(m_controls);
+
+		m_renderer.loadSourceFile(":res/deform/pathdeform.d");
+		m_renderer.loadDescription(":res/deform/pathdeform.html");
+		m_renderer.setDescriptionEnabled(false);
+
+		m_renderer.clicked.connect(&showControls);
+		m_controls.okPressed.connect(&hideControls);
+
+		m_controls.quitPressed.connect(&QApplication.quit);
+	}
+
+	void showControls()
+	{
+		m_controls.showFullScreen;
+	}
+
+	void hideControls()
+	{
+		m_controls.hide;
+	}
+
+	void setWidgetStyle(QStyle style) // TODO: QWidget.setStyle is not virtual
+	{
+		super.setStyle(style);
+		if (m_controls)
+		{
+			m_controls.setStyle(style);
+
+			QWidget[] widgets = m_controls.findChildren!(QWidget);
+			foreach (w; widgets)
+				w.setStyle(style);
+		}
+	}
+}
+
+private QRect circle_bounds(QPointF center, qreal radius, qreal compensation)
+{
+	return QRect(qRound(center.x() - radius - compensation),
+		qRound(center.y() - radius - compensation),
+		qRound((radius + compensation) * 2),
+		qRound((radius + compensation) * 2));
+}
+
+enum
+{
+	LENS_EXTENT = 10
+}
+
+class PathDeformRenderer : ArthurFrame
+{
+private:
+
+	QBasicTimer m_repaintTimer;
+	//     QBasicTimer m_fpsTimer;
+	//     int m_fpsCounter;
+	QTime m_repaintTracker;
+
+	QPainterPath[] m_paths;
+	QPointF[] m_advances;
+	QRectF m_pathBounds;
+	string m_text;
+
+	QPixmap m_lens_pixmap;
+	QImage m_lens_image;
+
+	int m_fontSize;
+	bool m_animated;
+
+	qreal m_intensity;
+	qreal m_radius;
+	QPointF m_pos;
+	QPointF m_offset;
+	QPointF m_direction;
+	QPointF m_mousePress;
+	bool m_mouseDrag;
+	bool m_smallScreen;
+
+public:
+
+	mixin Signal!("clicked");
+
+	this(QWidget widget, bool smallScreen)
+	{
+		super(widget);
+		m_radius = 100;
+		m_pos = QPointF(m_radius, m_radius);
+		m_direction = QPointF(1, 1);
+		m_fontSize = 24;
+		m_animated = true;
+		m_repaintTimer.start(25, this);
+		m_repaintTracker.start();
+		m_intensity = 100;
+		m_smallScreen = smallScreen;
+
+		//     m_fpsTimer.start(1000, this);
+		//     m_fpsCounter = 0;
+
+		generateLensPixmap();
+	}
+
+	void setFontSize(int fontSize)
+	{
+		m_fontSize = fontSize;
+		setText(m_text);
+	}
+
+	override QSize sizeHint() { return QSize(600, 500); }
+
+	bool animated() { return m_animated; }
+	int radius() { return cast(int) m_radius; }
+	int fontSize() { return m_fontSize; }
+	int intensity() { return cast(int) m_intensity; }
+	string text() { return m_text; }
+
+	void setText(string text)
+	{
+		m_text = text;
+
+		auto f = new QFont("times new roman,utopia");
+		f.setStyleStrategy(QFont.ForceOutline);
+		f.setPointSize(m_fontSize);
+		f.setStyleHint(QFont.Times);
+
+		m_paths = null;
+		m_pathBounds = QRectF();
+
+		QPointF advance;
+		
+		bool do_quick = true;
+		for (int i = 0; i < text.length; ++i) {
+			if (text[i].unicode() >= 0x4ff && text.at(i).unicode() <= 0x1e00) {
+				do_quick = false;
+				break;
+			}
+		}
+
+		if (do_quick) {
+			for (int i = 0; i < text.length; ++i) {
+				QPainterPath path = new QPainterPath;
+				path.addText(advance, f, text.mid(i, 1));
+				m_pathBounds |= path.boundingRect();
+				m_paths ~= path;
+				advance += QPointF(fm.width(text.mid(i, 1)), 0);
+			}
+		} else {
+			QPainterPath path = new QPainterPath;
+			path.addText(advance, f, text);
+			m_pathBounds |= path.boundingRect();
+			m_paths ~= path;
+		}
+
+		for (int i = 0; i < m_paths.length; ++i)
+			m_paths[i] = m_paths[i] * (new QMatrix(1, 0, 0, 1, -m_pathBounds.x(), -m_pathBounds.y()));
+
+		update();
+	}
+
+	void generateLensPixmap()
+	{
+		qreal rad = m_radius + LENS_EXTENT;
+
+		QRect bounds = circle_bounds(QPointF(), rad, 0);
+
+		QPainter painter = new QPainter;
+
+		if (preferImage()) {
+			m_lens_image = new QImage(bounds.size(), QImage.Format_ARGB32_Premultiplied);
+			m_lens_image.fill(0);
+			painter.begin(m_lens_image);
+		} else {
+			m_lens_pixmap = new QPixmap(bounds.size());
+			m_lens_pixmap.fill(new QColor(Qt.transparent));
+			painter.begin(m_lens_pixmap);
+		}
+
+		auto gr = new QRadialGradient(rad, rad, rad, 3 * rad / 5, 3 * rad / 5);
+		gr.setColorAt(0.0, new QColor(255, 255, 255, 191));
+		gr.setColorAt(0.2, new QColor(255, 255, 127, 191));
+		gr.setColorAt(0.9, new QColor(150, 150, 200, 63));
+		gr.setColorAt(0.95, new QColor(0, 0, 0, 127));
+		gr.setColorAt(1, new QColor(0, 0, 0, 0));
+		painter.setRenderHint(QPainter.Antialiasing);
+		painter.setBrush(gr);
+		painter.setPen(Qt.NoPen);
+		painter.drawEllipse(0, 0, bounds.width(), bounds.height());
+	}
+
+	void setAnimated(bool animated)
+	{
+		m_animated = animated;
+
+		if (m_animated) {
+			//         m_fpsTimer.start(1000, this);
+			//         m_fpsCounter = 0;
+			m_repaintTimer.start(25, this);
+			m_repaintTracker.start();
+		} else {
+			//         m_fpsTimer.stop();
+			m_repaintTimer.stop();
+		}
+	}
+
+	override void timerEvent(QTimerEvent e)
+	{
+		if (e.timerId == m_repaintTimer.timerId) 
+		{
+			if ((QLineF(QPointF(0,0), m_direction)).length() > 1)
+				m_direction *= 0.995;
+			qreal time = m_repaintTracker.restart();
+
+			QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
+
+			qreal dx = m_direction.x();
+			qreal dy = m_direction.y();
+			if (time > 0) {
+				dx = dx * time * .1;
+				dy = dy * time * .1;
+			}
+
+			m_pos += QPointF(dx, dy);
+
+			if (m_pos.x() - m_radius < 0) {
+				m_direction.x = -m_direction.x;
+				m_pos.x = m_radius;
+			} else if (m_pos.x + m_radius > width) {
+				m_direction.x = -m_direction.x;
+				m_pos.x = width - m_radius;
+			}
+
+			if (m_pos.y - m_radius < 0) {
+				m_direction.y = -m_direction.y;
+				m_pos.y = m_radius;
+			} else if (m_pos.y + m_radius > height) {
+				m_direction.y = -m_direction.y;
+				m_pos.y = height - m_radius;
+			}
+
+			void noGLUpdate()
+			{
+				QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
+				update(rectAfter.united(rectBefore));
+				QApplication.syncX();
+			}
+
+			version (QT_OPENGL_SUPPORT)
+			{
+				if (usesOpenGL()) {
+					update;
+				}
+				else
+					noGLUpdate;
+			}
+			else
+				noGLUpdate;
+		}
+		//     else if (e.timerId() == m_fpsTimer.timerId()) {
+		//         printf("fps: %d\n", m_fpsCounter);
+		//         emit frameRate(m_fpsCounter);
+		//         m_fpsCounter = 0;
+
+		//     }
+	}
+
+	override void mousePressEvent(QMouseEvent e)
+	{
+		setDescriptionEnabled(false);
+
+		m_repaintTimer.stop();
+		m_offset = QPointF();
+		if ((QLineF(m_pos, QPointF(e.pos))).length <= m_radius)
+			m_offset = m_pos - QPointF(e.pos);
+
+		m_mousePress = QPointF(e.pos);
+
+		// If we're not running in small screen mode, always assume we're dragging
+		m_mouseDrag = !m_smallScreen;
+
+		mouseMoveEvent(e);
+	}
+
+	override void mouseReleaseEvent(QMouseEvent e)
+	{
+		if (e.buttons() == Qt.NoButton && m_animated) {
+			m_repaintTimer.start(10, this);
+			m_repaintTracker.start();
+		}
+
+		if (!m_mouseDrag && m_smallScreen)
+			clicked.emit;
+	}
+
+	override void mouseMoveEvent(QMouseEvent e)
+	{
+		auto epos = QPointF(e.pos);
+
+		if (!m_mouseDrag && (QLineF(m_mousePress, QPointF(e.pos))).length() > 25.0)
+		m_mouseDrag = true;
+
+		if (m_mouseDrag)
+		{
+			QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
+			if (e.type() == QEvent.MouseMove) {
+				QLineF line = QLineF(m_pos, epos + m_offset);
+				line.setLength(line.length() * .1);
+				auto dir = QPointF(line.dx(), line.dy());
+				m_direction = (m_direction + dir) / 2;
+			}
+			m_pos = epos + m_offset;
+
+			void noGLUpdate()
+			{
+				QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
+				update(rectBefore.united(rectAfter));
+			}
+
+			version (QT_OPENGL_SUPPORT)
+			{
+				if (usesOpenGL()) {
+					update;
+				} else
+					noGLUpdate;
+			}
+			else
+				noGLUpdate;
+		}
+	}
+
+	QPainterPath lensDeform(QPainterPath source, QPointF offset)
+	{
+		auto path = new QPainterPath;
+		path.addPath(source);
+
+		qreal flip = m_intensity / 100.0;
+
+		for (int i=0; i<path.elementCount; ++i)
+		{
+			auto e = path.elementAt(i);
+
+			qreal x = e.x + offset.x();
+			qreal y = e.y + offset.y();
+
+			qreal dx = x - m_pos.x();
+			qreal dy = y - m_pos.y();
+			qreal len = m_radius - sqrt(dx * dx + dy * dy);
+
+			if (len > 0) {
+				path.setElementPositionAt(i,
+					  x + flip * dx * len / m_radius,
+					  y + flip * dy * len / m_radius);
+			} else {
+				path.setElementPositionAt(i, x, y);
+			}
+		}
+
+		return path;
+	}
+
+	override void paint(QPainter painter)
+	{
+		int pad_x = 5;
+		int pad_y = 5;
+
+		int skip_x = qRound(m_pathBounds.width() + pad_x + m_fontSize/2);
+		int skip_y = qRound(m_pathBounds.height() + pad_y);
+
+		painter.setPen(Qt.NoPen);
+		painter.setBrush(new QColor(Qt.black));
+
+		auto clip = painter.clipPath().boundingRect();
+
+		int overlap = pad_x / 2;
+
+		for (int start_y=0; start_y < height(); start_y += skip_y)
+		{
+			if (start_y > clip.bottom())
+			break;
+
+			int start_x = -overlap;
+			for (; start_x < width(); start_x += skip_x)
+			{
+				if (start_y + skip_y >= clip.top() &&
+					start_x + skip_x >= clip.left() &&
+					start_x <= clip.right())
+				{
+					for (int i=0; i<m_paths.length; ++i) {
+						QPainterPath path = lensDeform(m_paths[i], QPointF(start_x, start_y));
+						painter.drawPath(path);
+					}
+				}
+			}
+			overlap = skip_x - (start_x - width());
+		}
+
+		if (preferImage) {
+			painter.drawImage(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT), m_lens_image);
+		} else {
+			painter.drawPixmap(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT), m_lens_pixmap);
+		}
+	}
+
+	void setRadius(int radius)
+	{
+		qreal max = max(m_radius, cast(qreal)radius);
+		m_radius = radius;
+		generateLensPixmap();
+		if (!m_animated || m_radius < max)
+		{
+			auto noGLUpdate = (){ update(circle_bounds(m_pos, max, m_fontSize)); };
+
+			version (QT_OPENGL_SUPPORT)
+			{
+				if (usesOpenGL())
+					update();
+				else
+					noGLUpdate();
+			}
+			else
+				noGLUpdate();
+		}
+	}
+
+	void setIntensity(int intensity)
+	{
+		m_intensity = intensity;
+		if (!m_animated)
+		{
+			auto noGLUpdate = (){ update(circle_bounds(m_pos, m_radius, m_fontSize)); };
+
+			version (QT_OPENGL_SUPPORT)
+			{
+				if (usesOpenGL()) {
+					update();
+				} else
+					noGLUpdate();
+			}
+			else
+				noGLUpdate();
+		}
+	}
+}