Compound palette widget (Work in progress)

parent 5dec7a2c
......@@ -31,11 +31,12 @@ endif()
# Sources
include_directories("${PROJECT_SOURCE_DIR}/include")
# TODO: Use file lists instead of globbing
file(GLOB_RECURSE SOURCES src/*.cpp src/*.qrc)
file(GLOB_RECURSE HEADERS include/*.hpp)
qt5_wrap_cpp(SOURCES ${HEADERS})
qt5_wrap_ui(SOURCES src/color_dialog.ui)
qt5_wrap_ui(SOURCES src/color_dialog.ui src/color_palette_widget.ui)
qt5_add_resources(SOURCES src/pattern.qrc)
# Library
......
......@@ -31,6 +31,7 @@ SOURCES += \
$$PWD/src/abstract_widget_list.cpp \
$$PWD/src/color_palette.cpp \
$$PWD/src/color_palette_model.cpp \
$$PWD/src/color_palette_widget.cpp \
$$PWD/src/swatch.cpp
HEADERS += \
......@@ -47,10 +48,12 @@ HEADERS += \
$$PWD/include/colorwidgets_global.hpp \
$$PWD/include/color_palette.hpp \
$$PWD/include/color_palette_model.hpp \
$$PWD/include/color_widget.hpp \
$$PWD/include/swatch.hpp
FORMS += \
$$PWD/src/color_dialog.ui
$$PWD/src/color_dialog.ui \
$$PWD/src/color_palette_widget.ui
RESOURCES += \
$$PWD/src/pattern.qrc
......
......@@ -72,10 +72,8 @@ public:
ColorPalette(const ColorPalette& other);
ColorPalette& operator=(const ColorPalette& other);
~ColorPalette();
#ifdef Q_COMPILER_RVALUE_REFS
ColorPalette(ColorPalette&& other);
ColorPalette& operator=(ColorPalette&& other);
#endif // Q_COMPILER_RVALUE_REFS
/**
* \brief Color at the given index
......@@ -162,10 +160,29 @@ public slots:
void setDirty(bool dirty);
signals:
/**
* \brief Emitted when all the colors have changed
*/
void colorsChanged(const QVector<QPair<QColor,QString> >&);
void columnsChanged(int);
void nameChanged(const QString&);
void fileNameChanged(const QString&);
/**
* \brief Emitted when the color or the name at the given index has been modified
*/
void colorChanged(int index);
/**
* \brief Emitted when the color at the given index has been removed
*/
void colorRemoved(int index);
/**
* \brief Emitted when a single color has been added
*/
void colorAdded(int index);
/**
* \brief Emitted when the colors have been modified with a simple operation (set, append etc.)
*/
void colorsUpdated(const QVector<QPair<QColor,QString>>&);
private:
/**
......@@ -173,6 +190,11 @@ private:
*/
QString unnamed(const QString& name = QString()) const;
/**
* \brief Emit all the necessary signals when the palette has been completely overwritten
*/
void emitUpdate();
class Private;
Private *p;
};
......
......@@ -77,6 +77,7 @@ public:
/**
* \brief Get the palette at the given index (row)
* \pre 0 <= index < rowCount()
* \todo Maybe it's unsafe to be able to modify them outside the model
*/
ColorPalette& palette(int index);
......
/**
@author Mattia Basaglia
@section License
Copyright (C) 2015 Mattia Basaglia
This software 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 3 of the License, or
(at your option) any later version.
This software 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 Color Widgets. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COLOR_WIDGETS_COLOR_PALETTE_WIDGET_HPP
#define COLOR_WIDGETS_COLOR_PALETTE_WIDGET_HPP
#include <memory>
#include <QWidget>
#include "color_palette_model.hpp"
#include "swatch.hpp"
namespace color_widgets {
/**
* \brief A widget to use and modify palettes
*/
class ColorPaletteWidget : public QWidget
{
Q_OBJECT
/**
* \brief Model used to store the palettes
*/
Q_PROPERTY(ColorPaletteModel* model READ model WRITE setModel NOTIFY modelChanged)
/**
* \brief Size of a single color in the swatch widget
*/
Q_PROPERTY(QSize colorSize READ colorSize WRITE setColorSize NOTIFY colorSizeChanged)
/**
* \brief Policy for colorSize
**/
Q_PROPERTY(Swatch::ColorSizePolicy colorSizePolicy READ colorSizePolicy WRITE setColorSizePolicy NOTIFY colorSizePolicyChanged)
/**
* \brief Forces the Swatch to display that many rows of colors
*
* If there are too few elements, the widget will display less than this
* many rows.
*
* A value of0 means that the number of rows is automatic.
*
* \note Conflicts with forcedColumns
*/
Q_PROPERTY(int forcedRows READ forcedRows WRITE setForcedRows NOTIFY forcedRowsChanged)
/**
* \brief Forces the Swatch to display that many columns of colors
*
* If there are too few elements, the widget will display less than this
* many columns.
*
* A value of 0 means that the number of columns is automatic.
*
* \note Conflicts with forcedRows
*/
Q_PROPERTY(int forcedColumns READ forcedColumns WRITE setForcedColumns NOTIFY forcedColumnsChanged)
/**
* \brief Whether the palettes can be modified via user interaction
*/
Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly NOTIFY readOnlyChanged)
/**
* \brief Currently selected color
*/
Q_PROPERTY(QColor currentColor READ currentColor WRITE setCurrentColor NOTIFY currentColorChanged)
public:
ColorPaletteWidget(QWidget* parent = nullptr);
~ColorPaletteWidget();
ColorPaletteModel* model() const;
/**
* \brief Currently selected palette
* \pre model() != nullptr and there is a selected palette
*/
const ColorPalette& currentPalette() const;
QSize colorSize() const;
Swatch::ColorSizePolicy colorSizePolicy() const;
int forcedRows() const;
int forcedColumns() const;
bool readOnly() const;
QColor currentColor() const;
public slots:
void setModel(ColorPaletteModel* model);
void setColorSize(const QSize& colorSize);
void setColorSizePolicy(Swatch::ColorSizePolicy colorSizePolicy);
void setForcedRows(int forcedRows);
void setForcedColumns(int forcedColumns);
void setReadOnly(bool readOnly);
/**
* \brief Attempt to select a color
*
* If the given color is available in the current palete, it will be selected
* \return \b true on success
*/
bool setCurrentColor(const QColor& color);
/**
* \brief Attempt to select a color by name
*
* If the given color is available in the current palete, it will be selected
* \return \b true on success
*/
bool setCurrentColor(const QString& name);
signals:
void modelChanged(ColorPaletteModel* model);
void colorSizeChanged(const QSize& colorSize);
void colorSizePolicyChanged(Swatch::ColorSizePolicy colorSizePolicy);
void forcedRowsChanged(int forcedRows);
void forcedColumnsChanged(int forcedColumns);
void readOnlyChanged(bool readOnly);
void currentColorChanged(const QColor& currentColor);
private slots:
void on_palette_list_currentIndexChanged(int index);
void on_swatch_doubleClicked(int index);
private:
class Private;
std::unique_ptr<Private> p;
};
} // namespace color_widgets
#endif // COLOR_WIDGETS_COLOR_PALETTE_WIDGET_HPP
......@@ -73,17 +73,16 @@ ColorPalette::ColorPalette(const QVector<QPair<QColor,QString> >& colors,
setColors(colors);
p->dirty = false;
}
ColorPalette::ColorPalette(const ColorPalette& other)
: p ( new Private(*other.p) )
{
}
ColorPalette& ColorPalette::operator=(const ColorPalette& other)
{
*p = *other.p;
emit colorsChanged(p->colors);
emit columnsChanged(p->columns);
emit nameChanged(p->name);
emit fileNameChanged(p->fileName);
emitUpdate();
return *this;
}
......@@ -92,7 +91,6 @@ ColorPalette::~ColorPalette()
delete p;
}
#ifdef Q_COMPILER_RVALUE_REFS
ColorPalette::ColorPalette(ColorPalette&& other)
: p ( other.p )
{
......@@ -101,13 +99,17 @@ ColorPalette::ColorPalette(ColorPalette&& other)
ColorPalette& ColorPalette::operator=(ColorPalette&& other)
{
std::swap(p, other.p);
emitUpdate();
return *this;
}
void ColorPalette::emitUpdate()
{
emit colorsChanged(p->colors);
emit columnsChanged(p->columns);
emit nameChanged(p->name);
emit fileNameChanged(p->fileName);
return *this;
}
#endif // Q_COMPILER_RVALUE_REFS
QColor ColorPalette::colorAt(int index) const
{
......@@ -142,7 +144,6 @@ QString ColorPalette::name() const
bool ColorPalette::load(const QString& name)
{
p->fileName = name;
emit fileNameChanged(p->fileName);
p->colors.clear();
p->columns = 0;
p->dirty = false;
......@@ -150,14 +151,19 @@ bool ColorPalette::load(const QString& name)
QFile file(name);
/// \todo emit signals on return false
if ( !file.open(QFile::ReadOnly|QFile::Text) )
{
emitUpdate();
return false;
}
QTextStream stream( &file );
if ( stream.readLine() != "GIMP Palette" )
{
emitUpdate();
return false;
}
QString line;
......@@ -171,25 +177,27 @@ bool ColorPalette::load(const QString& name)
if ( line[0] == '#' )
break;
int colon = line.indexOf(':');
if ( colon != -1 )
properties[line.left(colon).toLower()] =
line.right(line.size() - colon - 1).trimmed();
if ( colon == -1 )
break;
properties[line.left(colon).toLower()] =
line.right(line.size() - colon - 1).trimmed();
}
/// \todo Store extra properties in the palette object
setName(properties["name"]);
setColumns(properties["columns"].toInt());
// Skip comments
while( !stream.atEnd() )
{
qint64 pos = stream.pos();
line = stream.readLine();
if ( !line.isEmpty() && line[0] != '#' )
if ( !stream.atEnd() && line[0] == '#' )
while( !stream.atEnd() )
{
stream.seek(pos);
break;
qint64 pos = stream.pos();
line = stream.readLine();
if ( !line.isEmpty() && line[0] != '#' )
{
stream.seek(pos);
break;
}
}
}
while( !stream.atEnd() )
{
......@@ -300,7 +308,8 @@ void ColorPalette::setColorAt(int index, const QColor& color)
p->colors[index].first = color;
p->dirty = true;
emit colorsChanged(p->colors);
emit colorChanged(index);
emit colorsUpdated(p->colors);
}
void ColorPalette::setColorAt(int index, const QColor& color, const QString& name)
......@@ -311,7 +320,8 @@ void ColorPalette::setColorAt(int index, const QColor& color, const QString& nam
p->colors[index].first = color;
p->colors[index].second = name;
p->dirty = true;
emit colorsChanged(p->colors);
emit colorChanged(index);
emit colorsUpdated(p->colors);
}
void ColorPalette::setNameAt(int index, const QString& name)
......@@ -322,7 +332,8 @@ void ColorPalette::setNameAt(int index, const QString& name)
p->colors[index].second = name;
p->dirty = true;
emit colorsChanged(p->colors);
emit colorChanged(index);
emit colorsUpdated(p->colors);
}
......@@ -330,7 +341,8 @@ void ColorPalette::appendColor(const QColor& color, const QString& name)
{
p->colors.push_back(qMakePair(color,name));
p->dirty = true;
emit colorsChanged(p->colors);
emit colorAdded(p->colors.size()-1);
emit colorsUpdated(p->colors);
}
void ColorPalette::insertColor(int index, const QColor& color, const QString& name)
......@@ -341,7 +353,8 @@ void ColorPalette::insertColor(int index, const QColor& color, const QString& na
p->colors.insert(index, qMakePair(color, name));
p->dirty = true;
emit colorsChanged(p->colors);
emit colorAdded(index);
emit colorsUpdated(p->colors);
}
void ColorPalette::eraseColor(int index)
......@@ -352,7 +365,8 @@ void ColorPalette::eraseColor(int index)
p->colors.remove(index);
p->dirty = true;
emit colorsChanged(p->colors);
emit colorRemoved(index);
emit colorsUpdated(p->colors);
}
void ColorPalette::setName(const QString& name)
......
/**
@author Mattia Basaglia
@section License
Copyright (C) 2015 Mattia Basaglia
This software 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 3 of the License, or
(at your option) any later version.
This software 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 Color Widgets. If not, see <http://www.gnu.org/licenses/>.
*/
#include "color_palette_widget.hpp"
#include "ui_color_palette_widget.h"
#include "color_dialog.hpp"
namespace color_widgets {
class ColorPaletteWidget::Private : public Ui::ColorPaletteWidget
{
public:
ColorPaletteModel* model = nullptr;
};
ColorPaletteWidget::ColorPaletteWidget(QWidget* parent)
: QWidget(parent), p(new Private)
{
p->setupUi(this);
connect(p->swatch, &Swatch::colorSizeChanged, this, &ColorPaletteWidget::colorSizeChanged);
connect(p->swatch, &Swatch::colorSizePolicyChanged, this, &ColorPaletteWidget::colorSizePolicyChanged);
connect(p->swatch, &Swatch::forcedRowsChanged, this, &ColorPaletteWidget::forcedRowsChanged);
connect(p->swatch, &Swatch::forcedColumnsChanged, this, &ColorPaletteWidget::forcedColumnsChanged);
connect(p->swatch, &Swatch::colorSelected, this, &ColorPaletteWidget::currentColorChanged);
}
ColorPaletteWidget::~ColorPaletteWidget() = default;
ColorPaletteModel* ColorPaletteWidget::model() const
{
return p->model;
}
const ColorPalette& ColorPaletteWidget::currentPalette() const
{
return p->swatch->palette();
}
QSize ColorPaletteWidget::colorSize() const
{
return p->swatch->colorSize();
}
Swatch::ColorSizePolicy ColorPaletteWidget::colorSizePolicy() const
{
return p->swatch->colorSizePolicy();
}
int ColorPaletteWidget::forcedRows() const
{
return p->swatch->forcedRows();
}
int ColorPaletteWidget::forcedColumns() const
{
return p->swatch->forcedColumns();
}
bool ColorPaletteWidget::readOnly() const
{
return p->swatch->readOnly();
}
QColor ColorPaletteWidget::currentColor() const
{
return p->swatch->selectedColor();
}
void ColorPaletteWidget::setModel(ColorPaletteModel* model)
{
if ( model == p->model )
return;
p->model = model;
p->swatch->setPalette(ColorPalette());
p->palette_list->setModel(model);
}
void ColorPaletteWidget::setColorSize(const QSize& colorSize)
{
p->swatch->setColorSize(colorSize);
}
void ColorPaletteWidget::setColorSizePolicy(Swatch::ColorSizePolicy colorSizePolicy)
{
p->swatch->setColorSizePolicy(colorSizePolicy);
}
void ColorPaletteWidget::setForcedRows(int forcedRows)
{
p->swatch->setForcedRows(forcedRows);
}
void ColorPaletteWidget::setForcedColumns(int forcedColumns)
{
p->swatch->setForcedColumns(forcedColumns);
}
void ColorPaletteWidget::setReadOnly(bool readOnly)
{
if ( readOnly == p->swatch->readOnly() )
return;
p->swatch->setReadOnly(readOnly);
p->group_edit_list->setVisible(!readOnly);
p->group_edit_palette->setVisible(!readOnly);
emit readOnlyChanged(readOnly);
}
bool ColorPaletteWidget::setCurrentColor(const QColor& color)
{
const auto& palette = p->swatch->palette();
for ( int i = 0; i < palette.count(); i++ )
{
if ( palette.colorAt(i) == color )
{
p->swatch->setSelected(i);
return true;
}
}
p->swatch->clearSelection();
return false;
}
bool ColorPaletteWidget::setCurrentColor(const QString& name)
{
const auto& palette = p->swatch->palette();
for ( int i = 0; i < palette.count(); i++ )
{
if ( palette.nameAt(i) == name )
{
p->swatch->setSelected(i);
return true;
}
}
p->swatch->clearSelection();
return false;
}
void ColorPaletteWidget::on_palette_list_currentIndexChanged(int index)
{
if ( !p->model )
p->swatch->setPalette(ColorPalette());
else
p->swatch->setPalette(p->model->palette(index));
}
void ColorPaletteWidget::on_swatch_doubleClicked(int index)
{
if ( !p->swatch->readOnly() )
{
ColorDialog dialog(this);
dialog.setAlphaEnabled(false);
dialog.setColor(p->swatch->palette().colorAt(index));
if ( dialog.exec() )
p->swatch->palette().setColorAt(index, dialog.color());
}
}
} // namespace color_widgets
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>color_widgets::ColorPaletteWidget</class>
<widget class="QWidget" name="color_widgets::ColorPaletteWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>227</width>
<height>186</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QComboBox" name="palette_list">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="group_edit_list" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="button_palette_open">
<property name="toolTip">
<string>Open a new palette from file</string>
</property>
<property name="icon">
<iconset theme="document-open"/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="button_palette_new">
<property name="toolTip">
<string>Create a new palette</string>
</property>
<property name="icon">
<iconset theme="document-new"/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="document_palette_duplicate">
<property name="toolTip">
<string>Duplicate the current palette</string>
</property>
<property name="icon">
<iconset theme="edit-copy"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="color_widgets::Swatch" name="swatch"/>
</item>
<item>
<widget class="QWidget" name="group_edit_palette" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="button_palette_delete">
<property name="toolTip">
<string>Delete the current palette</string>
</property>
<property name="icon">
<iconset theme="document-close"/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="button_palette_revert">
<property name="toolTip">
<string>Revert changes to the current palette</string>
</property>
<property name="icon">
<iconset theme="document-revert"/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="button_palette_save">
<property name="toolTip">
<string>Save changes to the current palette</string>
</property>
<property name="icon">
<iconset theme="document-save"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">