[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