view demos/deform/pathdeform.d @ 55:63c31e221118

CMake: Add forgotten files to install.
author SokoL_SD
date Mon, 18 May 2009 19:03:06 +0000
parents 691e68637348
children 849b66609571
line wrap: on
line source

/****************************************************************************
**
** 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<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();
        }
    }
}