# HG changeset patch # User maxter # Date 1242564074 0 # Node ID 691e68637348b56417912d54544734e265abcc49 # Parent a5cc4ada07f54cb595dcecefe4ee9529f0294b7d non-working deform example diff -r a5cc4ada07f5 -r 691e68637348 demos/deform/main.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/demos/deform/main.d Sun May 17 12:41:14 2009 +0000 @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import + pathdeform, + arthurstyle, + qt.gui.QApplication; + + +int main(string[] args) +{ + //Q_INIT_RESOURCE(deform); + + scope app = new QApplication(args); + + bool smallScreen = false; + foreach (arg; args) + { + if (arg == "-small-screen") + smallScreen = true; + } + + scope deformWidget = new PathDeformWidget(null, smallScreen); + + QStyle arthurStyle = new ArthurStyle(); + deformWidget.setWidgetStyle(arthurStyle); + auto widgets = deformWidget.findChildren!(QWidget); + foreach (w; widgets) + w.setStyle(arthurStyle); + + if (smallScreen) + deformWidget.showFullScreen(); + else + deformWidget.show(); + + return app.exec(); +} diff -r a5cc4ada07f5 -r 691e68637348 demos/deform/pathdeform.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/demos/deform/pathdeform.d Sun May 17 12:41:14 2009 +0000 @@ -0,0 +1,722 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +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); + + auto widgets = m_controls.findChildren!(QWidget); + foreach (w; widgets) + w.setStyle(style); + } + } +} + +private QRect circle_bounds(QPointF center, qreal radius, qreal compensation) +{ + return new 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 = new QRectF(); + + QPointF advance; + + auto path = new QPainterPath; + path.addText(advance, f, text); + m_pathBounds = m_pathBounds.united(path.boundingRect); + m_paths ~= path; + + foreach (ref p; m_paths) + p = (new QMatrix(1, 0, 0, 1, -m_pathBounds.x(), -m_pathBounds.y())).map(path); + + 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 ((new 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 ((new QLineF(m_pos, QPointF(e.pos))).length <= m_radius) + m_offset = m_pos - QPointF(e.pos); + + m_mousePress = 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 && (new 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 = new 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 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 + +//extern QPixmap cached(const QString &img); + +version (QT_OPENGL_SUPPORT) +{ + import qt.opengl.QGLWidget; + class GLWidget : QGLWidget + { + this(QWidget parent) + { + super(new QGLFormat(QGL.SampleBuffers), parent); + } + + void disableAutoBufferSwap() { setAutoBufferSwap(false); } + override void paintEvent(QPaintEvent) { parentWidget().update(); } + } +} + +class ArthurFrame : QWidget +{ +protected: + + version(QT_OPENGL_SUPPORT) + { + GLWidget glw; + bool m_use_opengl; + } + + QPixmap m_tile; + + bool m_show_doc; + bool m_prefer_image; + QTextDocument m_document; + + string m_sourceFileName; + +public: + mixin Signal!("descriptionEnabledChanged", bool); + + bool preferImage() { return m_prefer_image; } + + void paint(QPainter) {} + + this(QWidget parent) + { + super(parent); + + version (QT_OPENGL_SUPPORT) + { + QGLFormat f = QGLFormat.defaultFormat(); + f.setSampleBuffers(true); + f.setStencil(true); + f.setAlpha(true); + f.setAlphaBufferSize(8); + QGLFormat.setDefaultFormat(f); + } + + m_tile = new QPixmap(128, 128); + m_tile.fill(new QColor(Qt.white)); + scope pt = new QPainter(m_tile); + auto color = new QColor(230, 230, 230); + pt.fillRect(0, 0, 64, 64, color); + pt.fillRect(64, 64, 64, 64, color); + pt.end(); + + // QPalette pal = palette(); + // pal.setBrush(backgroundRole(), m_tile); + // setPalette(pal); + + version (Q_WS_X11) + { + auto xRenderPixmap = new QPixmap(1, 1); + m_prefer_image = xRenderPixmap.pixmapData().classId() == QPixmapData.X11Class && !xRenderPixmap.x11PictureHandle(); + } + } + + version (QT_OPENGL_SUPPORT) + { + void enableOpenGL(bool use_opengl) + { + m_use_opengl = use_opengl; + + if (!glw) { + glw = new GLWidget(this); + glw.setAutoFillBackground(false); + glw.disableAutoBufferSwap(); + QApplication.postEvent(this, new QResizeEvent(size(), size())); + } + + if (use_opengl) { + glw.show(); + } else { + glw.hide(); + } + + update(); + } + + bool usesOpenGL() { return m_use_opengl; } + QGLWidget glWidget(){ return glw; } + } + + override void paintEvent(QPaintEvent e) + { + version (Q_WS_QWS) + static QPixmap static_image; + else + static QImage static_image; + + auto painter = new QPainter; + + version (QT_OPENGL_SUPPORT) + auto prefImage = preferImage && !m_use_opengl; + else + auto prefImage = preferImage; + + if (prefImage) { + if (!static_image || static_image.size() != size()) { + delete static_image; + version (Q_WS_QWS) + static_image = new QPixmap(size()); + else + static_image = new QImage(size(), QImage.Format_RGB32); + } + painter.begin(static_image); + + int o = 10; + + QBrush bg = palette().brush(QPalette.Window); + painter.fillRect(0, 0, o, o, bg); + painter.fillRect(width() - o, 0, o, o, bg); + painter.fillRect(0, height() - o, o, o, bg); + painter.fillRect(width() - o, height() - o, o, o, bg); + } else { + version (QT_OPENGL_SUPPORT) + { + if (m_use_opengl) { + painter.begin(glw); + painter.fillRect(new QRectF(0, 0, glw.width(), glw.height()), palette().color(backgroundRole())); + } else { + painter.begin(this); + } + } + else + painter.begin(this); + } + + painter.setClipRect(e.rect()); + + painter.setRenderHint(QPainter.Antialiasing); + + auto clipPath = new QPainterPath; + + QRect r = rect(); + qreal left = r.x() + 1; + qreal top = r.y() + 1; + qreal right = r.right(); + qreal bottom = r.bottom(); + qreal radius2 = 8 * 2; + + clipPath.moveTo(right - radius2, top); + clipPath.arcTo(right - radius2, top, radius2, radius2, 90, -90); + clipPath.arcTo(right - radius2, bottom - radius2, radius2, radius2, 0, -90); + clipPath.arcTo(left, bottom - radius2, radius2, radius2, 270, -90); + clipPath.arcTo(left, top, radius2, radius2, 180, -90); + clipPath.closeSubpath(); + + painter.save(); + painter.setClipPath(clipPath, Qt.IntersectClip); + + painter.drawTiledPixmap(rect(), m_tile); + + // client painting + + paint(painter); + painter.restore(); + + painter.save(); + if (m_show_doc) + paintDescription(painter); + painter.restore(); + + int level = 180; + painter.setPen(new QPen(new QBrush(new QColor(level, level, level)), 2)); + painter.setBrush(Qt.NoBrush); + painter.drawPath(clipPath); + + if (prefImage) { + painter.end(); + painter.begin(this); + version (Q_WS_QWS) + painter.drawPixmap(e.rect(), static_image, e.rect()); + else + painter.drawImage(e.rect(), static_image, e.rect()); + } + + // TODO: this sucks + version (QT_OPENGL_SUPPORT) { + if (m_use_opengl && (inherits("PathDeformRenderer") || inherits("PathStrokeRenderer") || inherits("CompositionRenderer") || m_show_doc)) + glw.swapBuffers(); + } + } + + void resizeEvent(QResizeEvent e) + { + version (QT_OPENGL_SUPPORT) + { + if (glw) + glw.setGeometry(0, 0, e.size().width()-1, e.size().height()-1); + } + super.resizeEvent(e); + } + + void setDescriptionEnabled(bool enabled) + { + if (m_show_doc != enabled) { + m_show_doc = enabled; + descriptionEnabledChanged.emit(m_show_doc); + update(); + } + } + + void loadDescription(string fileName) + { + auto textFile = new QFile(fileName); + string text; + if (!textFile.open(QFile.ReadOnly)) + text = "Unable to load resource file: " ~ fileName; + else + text = textFile.readAll().toString; // TODO: excessive copying + setDescription(text); + } + + void setDescription(string text) + { + m_document = new QTextDocument(this); + m_document.setHtml(text); + } + + void paintDescription(QPainter painter) + { + if (!m_document) + return; + + int pageWidth = qMax(width() - 100, 100); + int pageHeight = qMax(height() - 100, 100); + if (pageWidth != m_document.pageSize().width()) { + m_document.setPageSize(QSizeF(pageWidth, pageHeight)); + } + + auto textRect = new QRect(width() / 2 - pageWidth / 2, + height() / 2 - pageHeight / 2, + pageWidth, + pageHeight); + int pad = 10; + QRect clearRect = textRect.adjusted(-pad, -pad, pad, pad); + painter.setPen(Qt.NoPen); + painter.setBrush(new QColor(0, 0, 0, 63)); + int shade = 10; + painter.drawRect(clearRect.x() + clearRect.width() + 1, + clearRect.y() + shade, + shade, + clearRect.height() + 1); + painter.drawRect(clearRect.x() + shade, + clearRect.y() + clearRect.height() + 1, + clearRect.width() - shade + 1, + shade); + + painter.setRenderHint(QPainter.Antialiasing, false); + painter.setBrush(new QColor(255, 255, 255, 220)); + painter.setPen(new QColor(Qt.black)); + painter.drawRect(clearRect); + + painter.setClipRect(textRect, Qt.IntersectClip); + painter.translate(textRect.topLeft()); + + auto ctx = new QAbstractTextDocumentLayout_PaintContext; + + auto g = new QLinearGradient(0, 0, 0, textRect.height()); + g.setColorAt(0, new QColor(Qt.black)); + g.setColorAt(0.9, new QColor(Qt.black)); + g.setColorAt(1, new QColor(Qt.transparent)); + + QPalette pal = palette(); + pal.setBrush(QPalette.Text, new QBrush(g)); + + ctx.setPalette(pal); + ctx.setClip(new QRectF(0, 0, textRect.width(), textRect.height())); + m_document.documentLayout().draw(painter, ctx); + } + + void loadSourceFile(string sourceName) + { + m_sourceFileName = sourceName; + } + + void showSource() + { + // Check for existing source + if (findChild!(QTextBrowser)) + return; + + string contents; + if (!m_sourceFileName.length) { + contents = "No source for widget: " ~ objectName(); + } else { + auto f = new QFile(m_sourceFileName); + if (!f.open(QFile.ReadOnly)) + contents = "Could not open file: " ~ m_sourceFileName; + else + contents = f.readAll.toString; + } + + contents = contents.substitute("&", "&"); + contents = contents.substitute("<", "<"); + contents = contents.substitute(">", ">"); + + static const string[] keywords = + ["for ", "if ", "switch ", " int ", "#include ", "const" + , "void ", "uint ", "case ", "double ", "#define ", "static" + , "new", "this"]; + + foreach (keyword; keywords) + contents = contents.substitute(keyword, "" ~ keyword ~ ""); + contents = contents.substitute("(int ", "(int "); + + static const string[] ppKeywords = + ["#ifdef", "#ifndef", "#if", "#endif", "#else"]; + + foreach (keyword; ppKeywords) + contents = contents.substitute(keyword, "" ~ keyword ~ ""); + + auto ddRe = new Regex("(\\d\\d?)"); + contents = ddRe.replaceAll(contents, "\\1"); + + auto commentRe = new Regex("(//.+?)\\n"); + contents = commentRe.replaceAll(contents, "\\1\n"); + + auto stringLiteralRe = new Regex("(\".+?\")"); + contents = stringLiteralRe.replaceAll(contents, "\\1"); + + auto html = contents.dup; + html = "
" ~ html ~ "
"; + + QTextBrowser sourceViewer = new QTextBrowser(null); + sourceViewer.setWindowTitle("Source: " ~ m_sourceFileName[5..$]); + sourceViewer.setParent(this, Qt.Dialog); + sourceViewer.setAttribute(Qt.WA_DeleteOnClose); + sourceViewer.setLineWrapMode(QTextEdit.NoWrap); + sourceViewer.setHtml(html); + sourceViewer.resize(600, 600); + sourceViewer.show(); + } +} diff -r a5cc4ada07f5 -r 691e68637348 demos/shared/hoverpoints.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/demos/shared/hoverpoints.d Sun May 17 12:41:14 2009 +0000 @@ -0,0 +1,439 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +version (QT_OPENGL_SUPPORT) + import qt.opengl.QGLWidget; + +version (D_Version2) {} +else + import tango.core.Array : sort; + +import + qt.gui.QWidget, + qt.qtd.Array, + arthurwidgets; + +final class HoverPoints : QObject +{ +public: + enum PointShape { + CircleShape, + RectangleShape + } + + enum LockType { + LockToLeft = 0x01, + LockToRight = 0x02, + LockToTop = 0x04, + LockToBottom = 0x08 + } + + enum SortType { + NoSort, + XSort, + YSort + } + + enum ConnectionType { + NoConnection, + LineConnection, + CurveConnection + } + +private: + QWidget m_widget; + + QPolygonF m_points; + QRectF m_bounds; + PointShape m_shape; + SortType m_sortType; + ConnectionType m_connectionType; + + uint[] m_locks; + + QSizeF m_pointSize; + int m_currentIndex; + bool m_editable; + bool m_enabled; + + QPen m_pointPen; + QBrush m_pointBrush; + QPen m_connectionPen; + +public: + mixin Signal!("pointsChanged", QPolygonF /*points*/); + + this(QWidget widget, PointShape shape) + { + super(widget); + + m_widget = widget; + widget.installEventFilter(this); + + m_connectionType = ConnectionType.CurveConnection; + m_sortType = SortType.NoSort; + m_shape = shape; + m_pointPen = new QPen(new QBrush(new QColor(255, 255, 255, 191)), 1); + m_connectionPen = new QPen(new QBrush(new QColor(255, 255, 255, 127)), 2); + m_pointBrush = new QBrush(new QColor(191, 191, 191, 127)); + m_pointSize = QSizeF(11, 11); + m_currentIndex = -1; + m_editable = true; + m_enabled = true; + + pointsChanged.connect(&m_widget.update); + } + + void setBoundingRect(QRectF boundingRect) { m_bounds = boundingRect; } + + + QRectF pointBoundingRect(int i) + { + QPointF p = m_points.at(i); + qreal w = m_pointSize.width(); + qreal h = m_pointSize.height(); + qreal x = p.x() - w / 2; + qreal y = p.y() - h / 2; + return new QRectF(x, y, w, h); + } + + QRectF boundingRect() + { + if (m_bounds.isEmpty()) + return new QRectF(m_widget.rect()); + else + return m_bounds; + } + + QPolygonF points() { return m_points; } + + QSizeF pointSize() { return m_pointSize; } + void setPointSize(QSizeF size) { m_pointSize = size; } + + SortType sortType() { return m_sortType; } + void setSortType(SortType sortType) { m_sortType = sortType; } + + ConnectionType connectionType() { return m_connectionType; } + void setConnectionType(ConnectionType connectionType) { m_connectionType = connectionType; } + + void setConnectionPen(QPen pen) { m_connectionPen = pen; } + void setShapePen(QPen pen) { m_pointPen = pen; } + void setShapeBrush(QBrush brush) { m_pointBrush = brush; } + + void setPointLock(int pos, LockType lock) { m_locks[pos] = lock; } + + void setEditable(bool editable) { m_editable = editable; } + bool editable() { return m_editable; } + + void setEnabled(bool enabled) + { + if (m_enabled != enabled) { + m_enabled = enabled; + m_widget.update(); + } + } + + + override bool eventFilter(QObject object, QEvent event) + { + if ((object == m_widget) && m_enabled) { + switch (event.type()) { + + case QEvent.MouseButtonPress: + { + QMouseEvent me = cast(QMouseEvent) event; + + QPointF clickPos = me.pos(); + int index = -1; + for (int i=0; i clickPos.x()) { + pos = i; + break; + } + } else if (m_sortType == SortType.YSort) { + for (int i=0; i clickPos.y()) { + pos = i; + break; + } + } + + // TODO: implement QPoligon(F).insert + auto tmpPoints = m_points.toList; + tmpPoints.insert(pos, clickPos); + m_points = new QPolygonF(tmpPoints); + + m_locks.insert(pos, 0u); + m_currentIndex = pos; + firePointChange(); + } else { + m_currentIndex = index; + } + return true; + + } else if (me.button() == Qt.RightButton) { + if ((index >= 0) && m_editable) { + if (m_locks[index] == 0) { + m_locks.removeAt(index); + m_points.remove(index); + } + firePointChange(); + return true; + } + } + + } + break; + + case QEvent.MouseButtonRelease: + m_currentIndex = -1; + break; + + case QEvent.MouseMove: + if (m_currentIndex >= 0) + movePoint(m_currentIndex, QPointF((cast(QMouseEvent)event).pos)); + break; + + case QEvent.Resize: + { + QResizeEvent e = cast(QResizeEvent) event; + if (e.oldSize().width() == 0 || e.oldSize().height() == 0) + break; + qreal stretch_x = e.size().width() / cast(qreal)e.oldSize.width; + qreal stretch_y = e.size().height() / cast(qreal)e.oldSize.height; + for (int i=0; i 0) { + m_locks.length = m_points.size; + + m_locks[] = 0; + } + } + + void movePoint(int index, QPointF point, bool emitUpdate = true) + { + m_points.replace(index, bound_point(point, boundingRect(), m_locks[index])); + if (emitUpdate) + firePointChange(); + } + + void firePointChange() + { + // printf("HoverPoints.firePointChange(), current=%d\n", m_currentIndex); + + if (m_sortType != SortType.NoSort) { + + QPointF oldCurrent; + if (m_currentIndex != -1) { + oldCurrent = m_points.at(m_currentIndex); + } + + if (m_sortType == SortType.XSort) + { + auto tmpPoints = m_points.toList; + sort(tmpPoints, &x_less_than); + m_points = new QPolygonF(tmpPoints); + } + else if (m_sortType == SortType.YSort) + { + auto tmpPoints = m_points.toList; + sort(tmpPoints, &y_less_than); + m_points = new QPolygonF(tmpPoints); + } + + // Compensate for changed order... + if (m_currentIndex != -1) { + for (int i=0; i right || (lock & HoverPoints.LockType.LockToRight)) p.x = right; + + if (p.y() < top || (lock & HoverPoints.LockType.LockToTop)) p.y = top; + else if (p.y() > bottom || (lock & HoverPoints.LockType.LockToBottom)) p.y = bottom; + + return p; +} + +private bool x_less_than(QPointF p1, QPointF p2) +{ + return p1.x() < p2.x(); +} + +private bool y_less_than(QPointF p1, QPointF p2) +{ + return p1.y() < p2.y(); +}