diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d495415907..d86798af95 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -70,11 +70,12 @@ opencs_units (view/world opencs_units_noqt (view/world subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate scripthighlighter idvalidator dialoguecreator physicssystem idcompletiondelegate + colordelegate ) opencs_units (view/widget scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton - scenetooltoggle2 completerpopup + scenetooltoggle2 completerpopup coloreditor colorpickerpopup ) opencs_units (view/render diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 8758d924b0..a8ae5dfa12 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -694,7 +694,7 @@ namespace CSMWorld QColor colour = data.value(); - record2.mMapColor = colour.rgb() & 0xffffff; + record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red(); record.setModified (record2); } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 7c79302d74..7aec001cf6 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -21,6 +21,7 @@ #include "../world/recordstatusdelegate.hpp" #include "../world/idtypedelegate.hpp" #include "../world/idcompletiondelegate.hpp" +#include "../world/colordelegate.hpp" #include "../../model/settings/usersettings.hpp" @@ -61,6 +62,9 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType, new CSVWorld::IdTypeDelegateFactory()); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_Colour, + new CSVWorld::ColorDelegateFactory()); + std::vector idCompletionColumns = CSMWorld::IdCompletionManager::getDisplayTypes(); for (std::vector::const_iterator current = idCompletionColumns.begin(); current != idCompletionColumns.end(); diff --git a/apps/opencs/view/widget/coloreditor.cpp b/apps/opencs/view/widget/coloreditor.cpp new file mode 100644 index 0000000000..7ef1ec7b17 --- /dev/null +++ b/apps/opencs/view/widget/coloreditor.cpp @@ -0,0 +1,113 @@ +#include "coloreditor.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "colorpickerpopup.hpp" + +CSVWidget::ColorEditor::ColorEditor(const QColor &color, QWidget *parent, bool popupOnStart) + : QPushButton(parent), + mColor(color), + mColorPicker(new ColorPickerPopup(this)), + mPopupOnStart(popupOnStart) +{ + setCheckable(true); + connect(this, SIGNAL(clicked()), this, SLOT(showPicker())); + connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid())); + connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &))); +} + +void CSVWidget::ColorEditor::paintEvent(QPaintEvent *event) +{ + QPushButton::paintEvent(event); + + QRect buttonRect = rect(); + QRect coloredRect(buttonRect.x() + qRound(buttonRect.width() / 4.0), + buttonRect.y() + qRound(buttonRect.height() / 4.0), + buttonRect.width() / 2, + buttonRect.height() / 2); + QPainter painter(this); + painter.fillRect(coloredRect, mColor); + painter.setPen(Qt::black); + painter.drawRect(coloredRect); +} + +void CSVWidget::ColorEditor::showEvent(QShowEvent *event) +{ + QPushButton::showEvent(event); + if (isVisible() && mPopupOnStart) + { + setChecked(true); + showPicker(); + mPopupOnStart = false; + } +} + +QColor CSVWidget::ColorEditor::color() const +{ + return mColor; +} + +void CSVWidget::ColorEditor::setColor(const QColor &color) +{ + mColor = color; + update(); +} + +void CSVWidget::ColorEditor::showPicker() +{ + if (isChecked()) + { + mColorPicker->showPicker(calculatePopupPosition(), mColor); + } + else + { + mColorPicker->hide(); + } +} + +void CSVWidget::ColorEditor::pickerHid() +{ + setChecked(false); + emit pickingFinished(); +} + +void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color) +{ + mColor = color; + update(); +} + +QPoint CSVWidget::ColorEditor::calculatePopupPosition() +{ + QRect editorGeometry = geometry(); + QRect popupGeometry = mColorPicker->geometry(); + QRect screenGeometry = QApplication::desktop()->screenGeometry(); + + // Center the popup horizontally relative to the editor + int localPopupX = (editorGeometry.width() - popupGeometry.width()) / 2; + // Popup position need to be specified in global coords + QPoint popupPosition = mapToGlobal(QPoint(localPopupX, editorGeometry.height())); + + // Make sure that the popup isn't out of the screen + if (popupPosition.x() < screenGeometry.left()) + { + popupPosition.setX(screenGeometry.left() + 1); + } + else if (popupPosition.x() + popupGeometry.width() > screenGeometry.right()) + { + popupPosition.setX(screenGeometry.right() - popupGeometry.width() - 1); + } + if (popupPosition.y() + popupGeometry.height() > screenGeometry.bottom()) + { + // Place the popup above the editor + popupPosition.setY(popupPosition.y() - popupGeometry.height() - editorGeometry.height() - 1); + } + + return popupPosition; +} diff --git a/apps/opencs/view/widget/coloreditor.hpp b/apps/opencs/view/widget/coloreditor.hpp new file mode 100644 index 0000000000..61232cb13e --- /dev/null +++ b/apps/opencs/view/widget/coloreditor.hpp @@ -0,0 +1,44 @@ +#ifndef CSV_WIDGET_COLOREDITOR_HPP +#define CSV_WIDGET_COLOREDITOR_HPP + +#include + +class QColor; +class QPoint; +class QSize; + +namespace CSVWidget +{ + class ColorPickerPopup; + + class ColorEditor : public QPushButton + { + Q_OBJECT + + QColor mColor; + ColorPickerPopup *mColorPicker; + bool mPopupOnStart; + + QPoint calculatePopupPosition(); + + public: + ColorEditor(const QColor &color, QWidget *parent = 0, bool popupOnStart = false); + + QColor color() const; + void setColor(const QColor &color); + + protected: + virtual void paintEvent(QPaintEvent *event); + virtual void showEvent(QShowEvent *event); + + private slots: + void showPicker(); + void pickerHid(); + void pickerColorChanged(const QColor &color); + + signals: + void pickingFinished(); + }; +} + +#endif diff --git a/apps/opencs/view/widget/colorpickerpopup.cpp b/apps/opencs/view/widget/colorpickerpopup.cpp new file mode 100644 index 0000000000..8e71ce39e4 --- /dev/null +++ b/apps/opencs/view/widget/colorpickerpopup.cpp @@ -0,0 +1,86 @@ +#include "colorpickerpopup.hpp" + +#include +#include +#include +#include +#include +#include +#include + +CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent) + : QFrame(parent) +{ + setWindowFlags(Qt::Popup); + setFrameStyle(QFrame::Box | QFrame::Plain); + hide(); + + mColorPicker = new QColorDialog(this); + mColorPicker->setWindowFlags(Qt::Widget); + mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog); + mColorPicker->installEventFilter(this); + mColorPicker->open(); + connect(mColorPicker, + SIGNAL(currentColorChanged(const QColor &)), + this, + SIGNAL(colorChanged(const QColor &))); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(mColorPicker); + layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + setFixedSize(mColorPicker->size()); +} + +void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColor &initialColor) +{ + QRect geometry = this->geometry(); + geometry.moveTo(position); + setGeometry(geometry); + + mColorPicker->setCurrentColor(initialColor); + show(); +} + +void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event) +{ + QPushButton *button = qobject_cast(parentWidget()); + if (button != NULL) + { + QStyleOptionButton option; + option.init(button); + QRect buttonRect = option.rect; + buttonRect.moveTo(button->mapToGlobal(buttonRect.topLeft())); + + // If the mouse is pressed above the pop-up parent, + // the pop-up will be hidden and the pressed signal won't be repeated for the parent + if (buttonRect.contains(event->globalPos()) || buttonRect.contains(event->pos())) + { + setAttribute(Qt::WA_NoMouseReplay); + } + } + QFrame::mousePressEvent(event); +} + +void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event) +{ + QFrame::hideEvent(event); + emit hid(); +} + +bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event) +{ + if (object == mColorPicker && event->type() == QEvent::KeyPress) + { + QKeyEvent *keyEvent = static_cast(event); + // Prevent QColorDialog from closing when Escape is pressed. + // Instead, hide the popup. + if (keyEvent->key() == Qt::Key_Escape) + { + hide(); + return true; + } + } + return QFrame::eventFilter(object, event); +} diff --git a/apps/opencs/view/widget/colorpickerpopup.hpp b/apps/opencs/view/widget/colorpickerpopup.hpp new file mode 100644 index 0000000000..602bbdb6da --- /dev/null +++ b/apps/opencs/view/widget/colorpickerpopup.hpp @@ -0,0 +1,32 @@ +#ifndef CSVWIDGET_COLORPICKERPOPUP_HPP +#define CSVWIDGET_COLORPICKERPOPUP_HPP + +#include + +class QColorDialog; + +namespace CSVWidget +{ + class ColorPickerPopup : public QFrame + { + Q_OBJECT + + QColorDialog *mColorPicker; + + public: + explicit ColorPickerPopup(QWidget *parent); + + void showPicker(const QPoint &position, const QColor &initialColor); + + protected: + virtual void mousePressEvent(QMouseEvent *event); + virtual void hideEvent(QHideEvent *event); + virtual bool eventFilter(QObject *object, QEvent *event); + + signals: + void hid(); + void colorChanged(const QColor &color); + }; +} + +#endif diff --git a/apps/opencs/view/world/colordelegate.cpp b/apps/opencs/view/world/colordelegate.cpp new file mode 100644 index 0000000000..1a89fc675e --- /dev/null +++ b/apps/opencs/view/world/colordelegate.cpp @@ -0,0 +1,36 @@ +#include "colordelegate.hpp" + +#include +#include + +#include "../widget/coloreditor.hpp" + +CSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, + QObject *parent) + : CommandDelegate(dispatcher, document, parent) +{} + +void CSVWorld::ColorDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0), + option.rect.y() + qRound(option.rect.height() / 4.0), + option.rect.width() / 2, + option.rect.height() / 2); + painter->save(); + painter->fillRect(coloredRect, index.data().value()); + painter->setPen(Qt::black); + painter->drawRect(coloredRect); + painter->restore(); +} + +CSVWorld::CommandDelegate *CSVWorld::ColorDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document &document, + QObject *parent) const +{ + return new ColorDelegate(dispatcher, document, parent); +} + + diff --git a/apps/opencs/view/world/colordelegate.hpp b/apps/opencs/view/world/colordelegate.hpp new file mode 100644 index 0000000000..87051e86d8 --- /dev/null +++ b/apps/opencs/view/world/colordelegate.hpp @@ -0,0 +1,37 @@ +#ifndef CSV_WORLD_COLORDELEGATE_HPP +#define CSV_WORLD_COLORDELEGATE_HPP + +#include "util.hpp" + +class QRect; + +namespace CSVWidget +{ + class ColorEditButton; +} + +namespace CSVWorld +{ + class ColorDelegate : public CommandDelegate + { + public: + ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, + QObject *parent); + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + }; + + class ColorDelegateFactory : public CommandDelegateFactory + { + public: + virtual CommandDelegate *makeDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document &document, + QObject *parent) const; + ///< The ownership of the returned CommandDelegate is transferred to the caller. + }; +} + +#endif diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index fa3a3e27aa..ab466dfcd8 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -33,6 +33,8 @@ #include "../../model/world/commands.hpp" #include "../../model/doc/document.hpp" +#include "../widget/coloreditor.hpp" + #include "recordstatusdelegate.hpp" #include "util.hpp" #include "tablebottombox.hpp" @@ -331,6 +333,10 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); } + else if (qobject_cast(editor)) + { + connect(editor, SIGNAL(pickingFinished()), proxy, SLOT(editorDataCommited())); + } else // throw an exception because this is a coding error throw std::logic_error ("Dialogue editor type missing"); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index e1d165a24f..5974987c0c 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -17,6 +17,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/commanddispatcher.hpp" +#include "../widget/coloreditor.hpp" #include "dialoguespinbox.hpp" #include "scriptedit.hpp" @@ -123,10 +124,19 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM if (!mCommandDispatcher) return; - NastyTableModelHack hack (*model); - QStyledItemDelegate::setModelData (editor, &hack, index); - - QVariant new_ = hack.getData(); + QVariant new_; + // Color columns use a custom editor, so we need explicitly extract a data from it + CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); + if (colorEditor != NULL) + { + new_ = colorEditor->color(); + } + else + { + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + new_ = hack.getData(); + } if ((model->data (index)!=new_) && (model->flags(index) & Qt::ItemIsEditable)) mCommandDispatcher->executeModify (model, index, new_); @@ -162,6 +172,12 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { return QStyledItemDelegate::createEditor(parent, option, index); } + // For tables the pop-up of the color editor should appear immediately after the editor creation + // (the third parameter of ColorEditor's constructor) + else if (display == CSMWorld::ColumnBase::Display_Colour) + { + return new CSVWidget::ColorEditor(index.data().value(), parent, true); + } return createEditor (parent, option, index, display); } @@ -184,7 +200,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { case CSMWorld::ColumnBase::Display_Colour: - return new QLineEdit(parent); + return new CSVWidget::ColorEditor(index.data().value(), parent); case CSMWorld::ColumnBase::Display_Integer: { @@ -284,6 +300,14 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde } } + // Color columns use a custom editor, so we need explicitly set a data for it + CSVWidget::ColorEditor *colorEditor = qobject_cast(editor); + if (colorEditor != NULL) + { + colorEditor->setColor(index.data().value()); + return; + } + QByteArray n = editor->metaObject()->userProperty().name(); if (n == "dateTime") {