[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdev-qmljs] /: Implement the QML color picker in QML
From:       Denis Steckelmacher <steckdenis () yahoo ! fr>
Date:       2014-06-30 18:45:30
Message-ID: E1X1ga2-0002tu-Vs () scm ! kde ! org
[Download RAW message or body]

Git commit 63acd629e627ce5618d2e8c8b50ad5708a8c3874 by Denis Steckelmacher.
Committed on 30/06/2014 at 18:43.
Pushed by dsteckelmacher into branch 'master'.

Implement the QML color picker in QML

REVIEW: 119042

M  +0    -1    CMakeLists.txt
M  +1    -65   kdevqmljsplugin.cpp
M  +0    -1    kdevqmljsplugin.h
D  +0    -157  navigation/colorchooser.cpp
D  +0    -78   navigation/colorchooser.h
M  +1    -0    navigation/propertypreviewwidget.cpp
A  +188  -0    navigation/propertywidgets/ColorPicker.qml     [License: GPL (v2+)]

http://commits.kde.org/kdev-qmljs/63acd629e627ce5618d2e8c8b50ad5708a8c3874

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 036ec6f..4ea69c4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -46,7 +46,6 @@ kde4_add_plugin(kdevqmljslanguagesupport
     qmljsparsejob.cpp
     qmljshighlighting.cpp
     kdevqmljsplugin.cpp
-    navigation/colorchooser.cpp
     navigation/propertypreviewwidget.cpp
 )
 target_link_libraries(kdevqmljslanguagesupport
diff --git a/kdevqmljsplugin.cpp b/kdevqmljsplugin.cpp
index 3bf0145..a917bf2 100644
--- a/kdevqmljsplugin.cpp
+++ b/kdevqmljsplugin.cpp
@@ -24,8 +24,6 @@
 #include "qmljshighlighting.h"
 #include "version.h"
 #include "codecompletion/model.h"
-
-#include "navigation/colorchooser.h"
 #include "navigation/propertypreviewwidget.h"
 
 #include <KPluginFactory>
@@ -97,30 +95,6 @@ ContextMenuExtension \
KDevQmlJsPlugin::contextMenuExtension(Context* context)  return cm;
 }
 
-const QColor stringToColor(const QString& text) {
-    if ( text.size() < 20 ) {
-        QColor color;
-
-        if (text.startsWith(QLatin1Char('#')) && text.length() == 9) {
-            // #AARRGGBB color, not supported by QColor::setNamedColor
-            QByteArray values = QByteArray::fromHex(text.mid(1).toAscii());
-
-            if (values.length() == 4) {
-                color.setRgba(qRgba(values[1], values[2], values[3], values[0]));
-            }
-        } else {
-            // Named color, #RRGGBB or #RGB, supported by QColor::setNamedColor
-            color.setNamedColor(text);
-        }
-
-        if ( color.isValid() ) {
-            qDebug() << color;
-            return color;
-        }
-    }
-    return QColor();
-}
-
 const QString textFromDoc(const IDocument* doc, const SimpleRange& range) {
     return doc->textDocument()->line(range.start.line).mid(range.start.column, \
range.end.column-range.start.column);  };
@@ -175,49 +149,11 @@ const QPair<SimpleRange, SimpleRange> parseProperty(const \
QString& line, const S  return QPair<SimpleRange, SimpleRange>(keyRange, valueRange);
 };
 
-SimpleRange KDevQmlJsPlugin::specialLanguageObjectRange(const KUrl& url, const \
                SimpleCursor& position)
-{
-    // The object range is only determined for colors. There is not really a point \
                of doing this for
-    // the key/value stuff; it'll just highlight the whole line, which is more \
                annoying
-    // than useful.
-    IDocument* doc = ICore::self()->documentController()->documentForUrl(url);
-    if ( doc && doc->textDocument() ) {
-        const QString& line = doc->textDocument()->line(position.line);
-        int start = position.column, end = position.column;
-        // search for the " or ' delmiters of the color to the right and to the left
-        for ( QString::const_iterator it = line.begin() + position.column; it != \
                line.end(); it++ ){
-            end += 1;
-            if ( *it == '"' || *it == '\'' ) break;
-        }
-        for ( QString::const_iterator it = line.begin() + position.column; it != \
                line.end(); it-- ){
-            start -= 1;
-            if ( *it == '"' || *it == '\'' ) break;
-        }
-        SimpleRange range(position.line, start+2, position.line, end-1);
-        // check if the encompassed string is actually a valid color string (this \
                also
-        // matches "red" or "blue") and if yes, return its range for highlighting.
-        if ( range.isValid() && start+2 > 0 && stringToColor(textFromDoc(doc, \
                range)).isValid() ) {
-            return range;
-        }
-    }
-    // Otherwise, no special highlighting shall take place.
-    return KDevelop::ILanguageSupport::specialLanguageObjectRange(url, position);
-}
-
 QWidget* KDevQmlJsPlugin::specialLanguageObjectNavigationWidget(const KUrl& url, \
const SimpleCursor& position)  {
-    SimpleRange range = specialLanguageObjectRange(url, position);
     IDocument* doc = ICore::self()->documentController()->documentForUrl(url);
     if ( doc && doc->textDocument() ) {
-        // use the above function to check if the requested range contains a color,
-        // and if yes, return a color picker widget to display.
-        QString text = textFromDoc(doc, range);
-        QColor color = stringToColor(text);
-        if ( color.isValid() ) {
-            return new ColorChooser(color, doc->textDocument(), range);
-        }
-
-        // If it's not a color, check for a QML property, and construct a property \
preview widget +        // Check for a QML property, and construct a property preview \
widget  // if the property key is listed in the supported properties.
         QPair<SimpleRange, SimpleRange> property = \
parseProperty(doc->textDocument()->line(position.line), position);  if ( \
                property.first.isValid() && property.second.isValid() ) {
diff --git a/kdevqmljsplugin.h b/kdevqmljsplugin.h
index d1afff5..d88d932 100644
--- a/kdevqmljsplugin.h
+++ b/kdevqmljsplugin.h
@@ -37,7 +37,6 @@ public:
     virtual KDevelop::BasicRefactoring* refactoring() const;
 
     virtual KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* \
                context);
-    virtual KDevelop::SimpleRange specialLanguageObjectRange(const KUrl& url, const \
                KDevelop::SimpleCursor& position);
     virtual QWidget* specialLanguageObjectNavigationWidget(const KUrl& url, const \
KDevelop::SimpleCursor& position);  
 private:
diff --git a/navigation/colorchooser.cpp b/navigation/colorchooser.cpp
deleted file mode 100644
index e17b18a..0000000
--- a/navigation/colorchooser.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/*************************************************************************************
                
- *  Copyright (C) 2013 by Sven Brauch <svenbrauch@gmail.com>                         \
                *
- *                                                                                   \
                *
- *  This program is free software; you can redistribute it and/or                    \
                *
- *  modify it under the terms of the GNU General Public License                      \
                *
- *  as published by the Free Software Foundation; either version 2                   \
                *
- *  of the License, or (at your option) any later version.                           \
                *
- *                                                                                   \
                *
- *  This program is distributed in the hope that it will be useful,                  \
                *
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   \
                *
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    \
                *
- *  GNU General Public License for more details.                                     \
                *
- *                                                                                   \
                *
- *  You should have received a copy of the GNU General Public License                \
                *
- *  along with this program; if not, write to the Free Software                      \
                *
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   \
                *
- *************************************************************************************/
                
-
-
-#include "colorchooser.h"
-#include <interfaces/icore.h>
-#include <interfaces/idocumentcontroller.h>
-#include <interfaces/iplugincontroller.h>
-#include <KTextEditor/View>
-
-#include <KColorValueSelector>
-#include <KHueSaturationSelector>
-
-ColorChooser::ColorChooser(const QColor& startColor, KTextEditor::Document* \
                document, const KDevelop::SimpleRange& range, QWidget* parent)
-    : QWidget(parent)
-    , document(document)
-    , range(range)
-    , size(70)
-    , valueSelector(new KColorValueSelector)
-    , hueSaturationSelector(new KHueSaturationSelector)
-    , wasModified(false)
-    , selectedColorPixmap(size, size)
-    , selectedColorLabel(new QLabel)
-{
-    // ChooserValue is is the default mode which is used in kcolorchooser,
-    // so use it here too
-    hueSaturationSelector->setChooserMode(ChooserValue);
-    selectedColorLabel->setFixedSize(size/2, size/4);
-    setFocusPolicy(Qt::NoFocus);
-    // see docstring for ILanguageSupport::specialLanguageObjectNavigationWidget
-    setProperty("DoNotCloseOnCursorMove", true);
-    setLayout(new QHBoxLayout);
-
-    hueSaturationSelector->setFixedSize(size, size);
-    // both widgets are given all color values, because their display style
-    // also depends on the color components they are not responsible to select
-    hueSaturationSelector->setHue(startColor.hsvHue());
-    hueSaturationSelector->setSaturation(startColor.hsvSaturation());
-    hueSaturationSelector->setColorValue(startColor.value());
-    valueSelector->setFixedHeight(size + 8);
-    valueSelector->setFixedWidth(20);
-    valueSelector->setColorValue(startColor.value());
-    valueSelector->setHue(startColor.hsvHue());
-    valueSelector->setSaturation(startColor.hsvSaturation());
-    valueSelector->setValue(startColor.value());
-
-    layout()->addWidget(hueSaturationSelector);
-    layout()->addWidget(valueSelector);
-    layout()->addWidget(selectedColorLabel);
-    // whenever any value changes,
-    //  * update the preview color
-    //  * synchronize the changes with the other widget
-    //  * update the color in the text document
-    connect(hueSaturationSelector, SIGNAL(valueChanged(int,int)), this, \
                SLOT(updatePreview()));
-    connect(valueSelector, SIGNAL(valueChanged(int)), this, SLOT(updatePreview()));
-    connect(hueSaturationSelector, SIGNAL(valueChanged(int,int)), this, \
                SLOT(hueValueChanged(int,int)));
-    connect(valueSelector, SIGNAL(valueChanged(int)), this, \
                SLOT(valueChanged(int)));
-    updatePreview();
-    connect(hueSaturationSelector, SIGNAL(valueChanged(int,int)), this, \
                SLOT(updateText()));
-    connect(valueSelector, SIGNAL(valueChanged(int)), this, SLOT(updateText()));
-};
-
-void ColorChooser::hueValueChanged(int hue, int saturation)
-{
-    if ( ! wasModified ) {
-        // all modifications are done in *one* editing step, because
-        // the user can then undo selecting a different color in the widget
-        // by using "Undo" once. Without this, you'd need one "undo" operation
-        // per pixel your mouse moved, which is annoying.
-        // TODO: which kind of bad things can happen when the document
-        // is in editing mode for several seconds?
-        // One thing that happens for sure is that if the user types stuff while
-        // the widget is open, highlighting doesn't update.
-        document->startEditing();
-        wasModified = true;
-    }
-    hueSaturationSelector->setHue(hue);
-    hueSaturationSelector->setSaturation(saturation);
-    valueSelector->setHue(hue);
-    valueSelector->setSaturation(saturation);
-}
-
-void ColorChooser::valueChanged(int value)
-{
-    if ( ! wasModified ) {
-        // see above
-        document->startEditing();
-        wasModified = true;
-    }
-    valueSelector->setColorValue(value);
-    hueSaturationSelector->setColorValue(value);
-}
-
-void ColorChooser::updatePreview()
-{
-    hueSaturationSelector->updateContents();
-    valueSelector->updateContents();
-    hueSaturationSelector->repaint();
-    valueSelector->repaint();
-    selectedColorPixmap.fill(getSelectedColor());
-    selectedColorLabel->setPixmap(selectedColorPixmap);
-}
-
-QColor ColorChooser::getSelectedColor()
-{
-    return QColor::fromHsv(hueSaturationSelector->hue(), \
                hueSaturationSelector->saturation(), valueSelector->colorValue());
-}
-
-void ColorChooser::updateText()
-{
-    // don't change anything if the user did not modify the color.
-    // this prevents mesing up code in case something goes wrong
-    if ( ! wasModified ) {
-        return;
-    }
-    document->activeView()->setCursorPosition(range.textRange().start());
-    QColor newColor = getSelectedColor();
-    // could translate back to readable name,  but it's not worth it
-    // (would need to be done manually with a lookup table)
-    const QString name = newColor.toRgb().name();
-    if ( range.end.column - range.start.column == name.size() ) {
-        document->replaceText(range.textRange(), name);
-    }
-    else {
-        document->removeText(range.textRange());
-        document->insertText(range.textRange().start(), name);
-        range.end.column = range.start.column + name.size();
-        // workaround to update the highlighting -- TODO does it make sense \
                usability-wise?
-        document->endEditing();
-        document->startEditing();
-    }
-};
-
-bool ColorChooser::event(QEvent* event)
-{
-    if ( event->type() == QEvent::Hide && wasModified ) {
-        // when the widget is hidden, update the text for one last time, then commit \
                and close.
-        updateText();
-        document->endEditing();
-    }
-    return QWidget::event(event);
-};
diff --git a/navigation/colorchooser.h b/navigation/colorchooser.h
deleted file mode 100644
index 1fa6e32..0000000
--- a/navigation/colorchooser.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*************************************************************************************
                
- *  Copyright (C) 2013 by Sven Brauch <svenbrauch@gmail.com>                         \
                *
- *                                                                                   \
                *
- *  This program is free software; you can redistribute it and/or                    \
                *
- *  modify it under the terms of the GNU General Public License                      \
                *
- *  as published by the Free Software Foundation; either version 2                   \
                *
- *  of the License, or (at your option) any later version.                           \
                *
- *                                                                                   \
                *
- *  This program is distributed in the hope that it will be useful,                  \
                *
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   \
                *
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    \
                *
- *  GNU General Public License for more details.                                     \
                *
- *                                                                                   \
                *
- *  You should have received a copy of the GNU General Public License                \
                *
- *  along with this program; if not, write to the Free Software                      \
                *
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   \
                *
- *************************************************************************************/
                
-
-
-#ifndef COLORCHOOSER_H
-#define COLORCHOOSER_H
-
-#include <QWidget>
-#include <QLabel>
-#include <QSlider>
-#include <QLayout>
-#include <QPainter>
-#include <QMouseEvent>
-
-#include <KTextEditor/Document>
-#include <language/editor/simplerange.h>
-
-#include <KColorValueSelector>
-#include <KHueSaturationSelector>
-
-// This class is a simple color chooser based on the KDE color hue/saturation
-// and value selectors. It is used for the popup widget which appears when the mouse
-// is moved over a color identifier.
-class ColorChooser : public QWidget {
-Q_OBJECT
-public:
-    ColorChooser(const QColor& startColor, KTextEditor::Document* document, const \
                KDevelop::SimpleRange& range,
-                 QWidget* parent = 0);
-    // returns the currently selected color
-    QColor getSelectedColor();
-
-private slots:
-    // update the text in the document
-    void updateText();
-    // refill the preview pixmap with the currently selected color
-    void updatePreview();
-    // update hue and value of both widgets when the user changes the hue/value
-    void hueValueChanged(int hue, int saturation);
-    // update color value on both widgets when the user changes the value
-    void valueChanged(int value);
-
-private:
-    virtual bool event(QEvent* event);
-    // the document replacements should be done in
-    KTextEditor::Document* const document;
-    // the range of the text which should be replaced
-    KDevelop::SimpleRange range;
-    // the width/height of the xy selector, and the height of the y selector
-    const int size;
-    // the slider for the color value
-    KColorValueSelector* valueSelector;
-    // the xy selector for the color hue / saturation
-    KHueSaturationSelector* hueSaturationSelector;
-    // true if the color was changed by the user in the widget sice it has been \
                opened
-    bool wasModified;
-    // pixmap filled with the preview color
-    // TODO could we do this without a pixmap?
-    QPixmap selectedColorPixmap;
-    // label for the selected color, displays the pixmap
-    QLabel* selectedColorLabel;
-};
-
-#endif
\ No newline at end of file
diff --git a/navigation/propertypreviewwidget.cpp \
b/navigation/propertypreviewwidget.cpp index 083ab65..ee5bdf8 100644
--- a/navigation/propertypreviewwidget.cpp
+++ b/navigation/propertypreviewwidget.cpp
@@ -60,6 +60,7 @@ QWidget* \
                PropertyPreviewWidget::constructIfPossible(KTextEditor::Document* \
                doc,
         supportedProperties["font.family"] = SupportedProperty(QUrl(base + \
                "FontFamily.qml"));
         supportedProperties["font.pointSize"] = SupportedProperty(QUrl(base + \
                "FontSize.qml"));
         supportedProperties["model"] = SupportedProperty(QUrl(base + "Repeat.qml"));
+        supportedProperties["color"] = SupportedProperty(QUrl(base + \
"ColorPicker.qml"));  }
 
     QHash<QString, SupportedProperty>::iterator item = \
                supportedProperties.find(key);
diff --git a/navigation/propertywidgets/ColorPicker.qml \
b/navigation/propertywidgets/ColorPicker.qml new file mode 100644
index 0000000..8b46ab9
--- /dev/null
+++ b/navigation/propertywidgets/ColorPicker.qml
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2014 Denis Steckelmacher <steckdenis@yahoo.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
+ */
+import QtQuick 1.1
+
+PropertyWidget {
+    id: picker
+    width: 214
+    height: 140
+
+    property real hue
+    property real saturation
+    property real lightness
+    property bool __reactOnChange: updateColor(value)
+
+    function currentColor() {
+        return Qt.hsla(picker.hue, picker.saturation, picker.lightness, 1.0)
+    }
+
+    function updateColor(clr) {
+        // QML does not expose any way of getting the components of a color
+        // parsed by Qt, thus we have to to the parsing ourselves (this breaks
+        // named colors)
+        // TODO: QtQuick2 exposes the r, g and b attributes of color.
+        if (clr[0] == '"') {
+            clr = clr.slice(1, 8);
+        }
+        if (clr[0] == '#') {
+            clr = clr.slice(1);
+        }
+
+        var r = parseInt(clr.slice(0, 2), 16) / 255;
+        var g = parseInt(clr.slice(2, 4), 16) / 255;
+        var b = parseInt(clr.slice(4, 6), 16) / 255;
+
+        // Formulae taken from ColorPicker.qml, Plasma Workspace,
+        // Copyright 2013 Marco Martin <mart@kde.org>
+        var min = Math.min(r, Math.min(g, b))
+        var max = Math.max(r, Math.max(g, b))
+        var c = max - min
+        var h
+
+        if (c == 0) {
+            h = 0
+        } else if (max == r) {
+            h = ((g - b) / c) % 6
+        } else if (max == g) {
+            h = ((b - r) / c) + 2
+        } else if (max == b) {
+            h = ((r - g) / c) + 4
+        }
+
+        picker.hue = (1/6) * h
+        picker.saturation = c / (1 - Math.abs(2 * ((max+min)/2) - 1))
+        picker.lightness = (max + min)/2
+
+        return true;
+    }
+
+    // Rectangle that displays the hue and the saturation of the color
+    MouseArea {
+        id: rectangle
+        width: 140
+        height: 140
+
+        onPositionChanged: {
+            picker.hue = mouse.x/width
+            picker.saturation = 1 - mouse.y/height
+
+            updateTimer.restart()
+        }
+        // Display the colors
+        Rectangle {
+            anchors.fill: parent
+            z: 0
+            rotation: 270
+
+            gradient: Gradient {
+                GradientStop { position: 0.0/6.0; color: Qt.hsla(0.0/6.0, 1, \
picker.lightness, 1) } +                GradientStop { position: 1.0/6.0; color: \
Qt.hsla(1.0/6.0, 1, picker.lightness, 1) } +                GradientStop { position: \
2.0/6.0; color: Qt.hsla(2.0/6.0, 1, picker.lightness, 1) } +                \
GradientStop { position: 3.0/6.0; color: Qt.hsla(3.0/6.0, 1, picker.lightness, 1) } + \
GradientStop { position: 4.0/6.0; color: Qt.hsla(4.0/6.0, 1, picker.lightness, 1) } + \
GradientStop { position: 5.0/6.0; color: Qt.hsla(5.0/6.0, 1, picker.lightness, 1) } + \
GradientStop { position: 6.0/6.0; color: Qt.hsla(6.0/6.0, 1, picker.lightness, 1) } + \
} +        }
+
+        // Display the saturation
+        Rectangle {
+            anchors.fill: parent
+            z: 1
+
+            gradient: Gradient {
+                GradientStop { position: 0.0; color: Qt.hsla(0, 0, picker.lightness, \
0) } +                GradientStop { position: 1.0; color: Qt.hsla(0, 0, \
picker.lightness, 1) } +            }
+        }
+
+        // Marker
+        Rectangle {
+            id: hsMarker
+            width: 5
+            height: 5
+            x: picker.hue * rectangle.width - 2
+            y: rectangle.height * (1.0 - picker.saturation)
+
+            color: "black"
+            border {
+                color: "white"
+                width: 1
+            }
+        }
+    }
+
+    // Vertical bar that displays the lightness of the color
+    MouseArea {
+        id: bar
+        width: 20
+        height: 140
+        anchors.left: rectangle.right
+        anchors.leftMargin: 7
+
+        onPositionChanged: {
+            picker.lightness = 1 - mouse.y/height
+
+            updateTimer.restart()
+        }
+
+        Rectangle {
+            anchors.fill: parent
+
+            gradient: Gradient {
+                GradientStop { position: 0.0; color: Qt.hsla(picker.hue, \
picker.saturation, 1, 1) } +                GradientStop { position: 0.5; color: \
Qt.hsla(picker.hue, picker.saturation, 0.5, 1) } +                GradientStop { \
position: 1.0; color: Qt.hsla(picker.hue, picker.saturation, 0, 1) } +            }
+        }
+        Rectangle {
+            id: vMarker
+            width: 19
+            height: 5
+            y: bar.height * (1 - picker.lightness)
+
+            color: "black"
+            border {
+                color: "white"
+                width: 1
+            }
+        }
+    }
+
+    // Preview of the color
+    Rectangle {
+        id: preview
+        anchors.left: bar.right
+        anchors.verticalCenter: bar.verticalCenter
+        anchors.leftMargin: 7
+        width: 40
+        height: 30
+        color: picker.currentColor()
+    }
+
+    // Timer to update the value only 4 times per second
+    Timer {
+        id: updateTimer
+        interval: 250
+        repeat: false
+
+        onTriggered: {
+            picker.valueChanged('"' + currentColor().toString() + '"');
+        }
+    }
+}
\ No newline at end of file


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic