From d990db76dab62a3f19aa44e063cd491306688478 Mon Sep 17 00:00:00 2001 From: EIntemporel Date: Mon, 14 Nov 2022 23:20:31 +0100 Subject: [PATCH] Add /Widgets - LightViewWidget: used for LightEditor - DynamicMouseWidget: used for overrided the cursor shape --- src/noggit/ui/widgets/DynamicMouseWidget.cpp | 28 + src/noggit/ui/widgets/DynamicMouseWidget.h | 22 + src/noggit/ui/widgets/LightViewWidget.cpp | 803 +++++++++++++++++++ src/noggit/ui/widgets/LightViewWidget.h | 224 ++++++ 4 files changed, 1077 insertions(+) create mode 100644 src/noggit/ui/widgets/DynamicMouseWidget.cpp create mode 100644 src/noggit/ui/widgets/DynamicMouseWidget.h create mode 100644 src/noggit/ui/widgets/LightViewWidget.cpp create mode 100644 src/noggit/ui/widgets/LightViewWidget.h diff --git a/src/noggit/ui/widgets/DynamicMouseWidget.cpp b/src/noggit/ui/widgets/DynamicMouseWidget.cpp new file mode 100644 index 00000000..58592948 --- /dev/null +++ b/src/noggit/ui/widgets/DynamicMouseWidget.cpp @@ -0,0 +1,28 @@ +#include "DynamicMouseWidget.h" + +DynamicMouseWidget::DynamicMouseWidget(QWidget* parent) + : QWidget(parent) +{ } + +DynamicMouseWidget::~DynamicMouseWidget() +{ } + +void DynamicMouseWidget::enterEvent(QEvent* event) +{ + QApplication::setOverrideCursor(Qt::PointingHandCursor); +} + +void DynamicMouseWidget::leaveEvent(QEvent* event) +{ + QApplication::restoreOverrideCursor(); +} + +void DynamicMouseWidget::mousePressEvent(QMouseEvent* event) +{ + QApplication::setOverrideCursor(Qt::CursorShape::ClosedHandCursor); +} + +void DynamicMouseWidget::mouseReleaseEvent(QMouseEvent* event) +{ + QApplication::restoreOverrideCursor(); +} \ No newline at end of file diff --git a/src/noggit/ui/widgets/DynamicMouseWidget.h b/src/noggit/ui/widgets/DynamicMouseWidget.h new file mode 100644 index 00000000..4041f9e1 --- /dev/null +++ b/src/noggit/ui/widgets/DynamicMouseWidget.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include +#include + +class DynamicMouseWidget : public QWidget +{ + Q_OBJECT + +public: + DynamicMouseWidget(QWidget* parent = nullptr); + virtual ~DynamicMouseWidget(); + +protected: + void enterEvent(QEvent* event) override; + void leaveEvent(QEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; +}; + diff --git a/src/noggit/ui/widgets/LightViewWidget.cpp b/src/noggit/ui/widgets/LightViewWidget.cpp new file mode 100644 index 00000000..45f2c9db --- /dev/null +++ b/src/noggit/ui/widgets/LightViewWidget.cpp @@ -0,0 +1,803 @@ +#include "LightViewWidget.h" + +LightViewWidget::LightViewWidget(QWidget* parent) + : QWidget(parent) +{ + +} + +QColor LightViewWidget::InterpolateColor(QColor& a, QColor& b, float t) +{ + QColor Color; + + Color.setRgb( + ClampColor(a.red() * (1 - t) + b.red() * t), + ClampColor(a.green() * (1 - t) + b.green() * t), + ClampColor(a.blue() * (1 - t) + b.blue() * t) + ); + + return Color; +} + +QImage LightViewWidget::FillImagePart(QImage Image, int X, QColor Color) +{ + if (X == Image.width() || X < 0) + return Image; + + for (int Y = 0; Y < Image.height(); ++Y) + Image.setPixelColor(QPoint(X, Y), Color); + + return Image; +} + +int LightViewWidget::ClampColor(int Value) +{ + if (Value < 0) Value == 0; + if (Value > 255) Value == 255; + + return Value; +} + +QColor LightViewWidget::GetColorFromStyleSheet() +{ + const QString SearchPattern = ".*qmainwindow{background-color:(#[A-Fa-f0-9]{3-6});}.*"; + QString StyleSheet = qApp->styleSheet().simplified().toLower().remove(R"( )"); + + QRegularExpression Regex = QRegularExpression(SearchPattern); + auto Match = Regex.match(StyleSheet); + QStringList HexColor = Match.capturedTexts(); + + if (HexColor.size() > 0) + return QColor(HexColor.at(0)); + + return Qt::transparent; +} + +void LightViewWidget::SortSkyColorVector(std::vector& Vector) +{ + std::sort(Vector.begin(), Vector.end(), [](const auto& a, const auto& b) + { + return (a.time < b.time); + }); +} + +// use for access to the editor and for view +LightViewPreview::LightViewPreview(QString LightName, QSize Size, QWidget* parent) + : DynamicMouseWidget(parent), MainLayout(new QVBoxLayout(this)), Name(new QLabel(this)), + Preview(new LightViewPixmap(Size, this)) +{ + Name->setText(LightName); + + MainLayout->addWidget(Name); + MainLayout->addWidget(Preview); + MainLayout->setSpacing(0); + MainLayout->setMargin(0); +} + +LightViewPreview::LightViewPreview(QSize Size, QWidget* parent) + : DynamicMouseWidget(parent), MainLayout(new QVBoxLayout(this)), Name(new QLabel(this)), + Preview(new LightViewPixmap(Size, this)) +{ + MainLayout->addWidget(Preview); + MainLayout->setSpacing(0); + MainLayout->setMargin(0); +} + +void LightViewPreview::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + emit LeftClicked(); +} + +void LightViewPreview::ShowLines(bool Show) +{ + Preview->ShowLines(Show); +} + +void LightViewPreview::SetPreview(std::vector& data) +{ + Preview->SetPreview(data); +} + +void LightViewPreview::UpdatePixmap(const QPixmap Updated) +{ + Preview->setPixmap(Updated.scaled(Preview->pixmap()->size(), Qt::IgnoreAspectRatio)); +} + +LightViewPixmap::LightViewPixmap(QSize Size, QWidget* parent) + : QLabel(parent), PixmapSize(Size) +{ + Image = QImage(PixmapSize, QImage::Format_ARGB32); + Base = QPixmap(PixmapSize); +} + +void LightViewPixmap::mousePressEvent(QMouseEvent* event) +{ + if (!bEvent) + { + QWidget::mousePressEvent(event); + return; + } + + if (event->modifiers() & Qt::CTRL && event->button() == Qt::LeftButton) + { + int Time = event->pos().x() * MAX_TIME_VALUE / width(); + emit CreateValue(Time); + } +} + +void LightViewPixmap::ShowLines(bool Show) +{ + bShowLines = Show; + SetPreview(Data); +} + +void LightViewPixmap::SetPreview(std::vector& data, int Index) +{ + Data = data; + if (Data.size() == 0) + return; + + for (int i = 0; i < Data.size(); ++i) + Data[i].color *= 255.f; + + LightViewWidget::SortSkyColorVector(Data); + + if (Data.size() == 1) + { + Image.fill(qRgba(Data[0].color.r, Data[0].color.g, Data[0].color.b, 255)); + + QPainter Painter(&Base); + Painter.drawImage(QPoint(0, 0), Image); + emit UpdatePixmap(Base); + + if (DrawLines()) + return; + + setPixmap(Base); + return; + } + + Image.fill(qRgba(0, 0, 0, 0)); + + QPainter Painter(&Base); + for (int i = 0; i < Data.size(); ++i) + { + QColor PreColor = nullptr; + QColor CurrentColor(Data[i].color.r, Data[i].color.g, Data[i].color.b); + QColor NextColor = nullptr; + + if (i == 0) + { + glm::vec3 Color = Data[Data.size() - 1].color; + PreColor = QColor(Color.r, Color.g, Color.b); + + int LastTimeOffset = Data[Data.size() - 1].time - MAX_TIME_VALUE; + int DistanceWithFirstTime = Data[i].time - LastTimeOffset; + + for (int time = 0; time <= Data[i].time; ++time) + { + float t = 1.f; + if (DistanceWithFirstTime != 0) + t = (float(time) + float(abs(LastTimeOffset))) / float(DistanceWithFirstTime); + + QColor Calculated = LightViewWidget::InterpolateColor(PreColor, CurrentColor, t); + + int X = time * Image.width() / MAX_TIME_VALUE; + Image = LightViewWidget::FillImagePart(Image, X, Calculated); + } + } + else if (i == Data.size() - 1) + { + glm::vec3 Color = Data[i - 1].color; + PreColor = QColor(Color.r, Color.g, Color.b); + + Color = Data[0].color; + NextColor = QColor(Color.r, Color.g, Color.b); + + int FirstTimeOffset = Data[0].time + MAX_TIME_VALUE; + int DistanceWithFirstTime = FirstTimeOffset - Data[i].time; + + for (int time = Data[i].time; time <= MAX_TIME_VALUE; ++time) + { + float t = 1.f; + if (DistanceWithFirstTime != 0) + t = (float(time) - float(Data[i].time)) / float(DistanceWithFirstTime); + + QColor Calculated = LightViewWidget::InterpolateColor(CurrentColor, NextColor, t); + + int X = time * Image.width() / MAX_TIME_VALUE; + Image = LightViewWidget::FillImagePart(Image, X, Calculated); + } + break; + } + + glm::vec3 Color = Data[i + 1].color; + NextColor = QColor(Color.r, Color.g, Color.b); + + for (int time = Data[i].time; time <= Data[i + 1].time; ++time) + { + float t = 1.f; + if (Data[i + 1].time != Data[i].time) + t = (float(time) - float(Data[i].time)) / (float(Data[i + 1].time) - float(Data[i].time)); + + QColor Calculated = LightViewWidget::InterpolateColor(CurrentColor, NextColor, t); + + int X = time * Image.width() / MAX_TIME_VALUE; + Image = LightViewWidget::FillImagePart(Image, X, Calculated); + } + } + + Painter.drawImage(QPoint(0, 0), Image); + emit UpdatePixmap(Base); + + if (DrawLines()) + return; + + setPixmap(Base); +} + +bool LightViewPixmap::DrawLines() +{ + if (!bShowLines) return bShowLines; + + QImage Final = Image; + QPixmap ResultPixmap = QPixmap(Final.size()); + + QPainter Painter(&ResultPixmap); + for (int i = 0; i < Data.size(); ++i) + { + for (int y = 0; y < LIGHT_VIEW_PREVIEW_LINE_SIZE; ++y) + { + int X = Data[i].time * Final.width() / MAX_TIME_VALUE - LIGHT_VIEW_PREVIEW_LINE_SIZE / 2 + y; + Final = LightViewWidget::FillImagePart(Final, X, QColor(LIGHT_VIEW_PREVIEW_LINE_COLOR)); + } + } + + Painter.drawImage(QPoint(0, 0), Final); + setPixmap(ResultPixmap); + return bShowLines; +} + +LightViewEditor::LightViewEditor(MapView* Map, SkyParam* Sky, SkyColorNames eSkyColor, QWidget* parent) + : QWidget(parent), _World(Map->getWorld()), _Map(Map), _Sky(Sky), _eSkyColorIndex(eSkyColor) +{ + setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); + + MainLayout = new QVBoxLayout(this); + MainLayout->setContentsMargins(0, 0, 0, 0); + MainLayout->setSpacing(0); + + _ColorName = SkyColorNameByIndex.at(_eSkyColorIndex); + setWindowTitle(QString(tr("Light Color Editor - %1 Color")).arg(_ColorName.c_str())); + + Indicator = new LightViewDayIndicator(QSize(LIGHT_VIEW_PREVIEW_WIDTH * 2 + 30, LIGHT_VIEW_PREVIEW_HEIGHT), this); + + QHBoxLayout* PreviewLayout = new QHBoxLayout(this); + + Preview = new LightViewPixmap(QSize(LIGHT_VIEW_PREVIEW_WIDTH * 2, LIGHT_VIEW_PREVIEW_HEIGHT * 2), this); + Preview->ShowLines(true); + Preview->EnableEvent(); + Preview->SetPreview(_Sky->colorRows[_eSkyColorIndex]); + + Arrow = new LightViewArrow(QSize(LIGHT_VIEW_PREVIEW_WIDTH * 2 + 30, LIGHT_VIEW_PREVIEW_HEIGHT * 2), this); + Arrow->UpdateData(_Sky->colorRows[_eSkyColorIndex]); + + connect(Preview, &LightViewPixmap::UpdatePixmap, this, [=](const QPixmap Updated) + { + emit UpdatePixmap(Updated); + }); + + connect(Preview, SIGNAL(CreateValue(int)), this, SLOT(CreateValue(int))); + + connect(Arrow, &LightViewArrow::ChangeTime, this, [=](int Index, int Time) + { + if (Index > 0 && Index <= _Sky->colorRows[_eSkyColorIndex].size() - 1) + { + if (Time < _Sky->colorRows[_eSkyColorIndex][Index - 1].time) + { + Arrow->CurrentHover--; + Arrow->CurrentSelected--; + } + } + + if (Index >= 0 && Index < _Sky->colorRows[_eSkyColorIndex].size() - 1) + { + if (Time > _Sky->colorRows[_eSkyColorIndex][Index + 1].time) + { + Arrow->CurrentHover++; + Arrow->CurrentSelected++; + } + } + + _Sky->colorRows[_eSkyColorIndex][Index].time = Time; + + LightViewWidget::SortSkyColorVector(_Sky->colorRows[_eSkyColorIndex]); + UpdateSkyColorRowsValue(); + }); + + connect(Arrow, &LightViewArrow::ReleaseArrow, this, [=]() + { LightViewWidget::SortSkyColorVector(_Sky->colorRows[_eSkyColorIndex]); }); + + connect(Arrow, SIGNAL(SelectValue(int)), this, SLOT(UpdateWidgetEdition(int))); + connect(Arrow, SIGNAL(DeleteValue(int)), this, SLOT(DeleteValue(int))); + + PreviewLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + PreviewLayout->addWidget(Preview); + PreviewLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + + QFrame* Line = new QFrame(); + Line->setFrameShape(QFrame::HLine); + Line->setFrameShadow(QFrame::Sunken); + + // Other widget for display + QHBoxLayout* SecLayout = new QHBoxLayout(this); + { + ColorPicker = new color_widgets::ColorSelector(this); + ColorPicker->setDisplayMode(color_widgets::ColorSelector::NoAlpha); + ColorPicker->setColor(Qt::gray); + + connect(ColorPicker, &color_widgets::ColorSelector::colorChanged, this, [=](QColor Changed) + { + if (!ValidCurrentIndex()) + return; + + _Sky->colorRows[_eSkyColorIndex][CurrentIndex].color = glm::vec3(Changed.redF(), Changed.greenF(), Changed.blueF()); + UpdateSkyColorRowsValue(); + }); + + TimeSelectorHour = new QSpinBox(this); + TimeSelectorHour->setMinimum(0); + TimeSelectorHour->setMaximum(23); + + TimeSelectorMin = new QSpinBox(this); + TimeSelectorMin->setMinimum(0); + TimeSelectorMin->setMaximum(59); + + connect(TimeSelectorHour, &QSpinBox::textChanged, [=](QString) + { + if (!ValidCurrentIndex()) + return; + + int Time = ((TimeSelectorHour->value() * 60) + TimeSelectorMin->value()) * MAX_TIME_VALUE / (23 * 60 + 59); + _Sky->colorRows[_eSkyColorIndex][CurrentIndex].time = Time; + UpdateSkyColorRowsValue(); + }); + + connect(TimeSelectorMin, &QSpinBox::textChanged, [=](QString) + { + if (!ValidCurrentIndex()) + return; + + int Time = ((TimeSelectorHour->value() * 60) + TimeSelectorMin->value()) * MAX_TIME_VALUE / (23 * 60 + 59); + _Sky->colorRows[_eSkyColorIndex][CurrentIndex].time = Time; + UpdateSkyColorRowsValue(); + }); + + TimeSelectorHour->setEnabled(false); + TimeSelectorMin->setEnabled(false); + ColorPicker->setEnabled(false); + + SecLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + SecLayout->addWidget(new QLabel(tr("Color"), this)); + SecLayout->addWidget(ColorPicker); + SecLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + SecLayout->addWidget(new QLabel(tr("Hours"), this)); + SecLayout->addWidget(TimeSelectorHour); + SecLayout->addWidget(new QLabel(tr("Minutes"), this)); + SecLayout->addWidget(TimeSelectorMin); + SecLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + } + + QHBoxLayout* ThirdLayout = new QHBoxLayout(this); + { + Remove = new QPushButton(tr("Delete Selected"), this); + connect(Remove, &QPushButton::clicked, this, [=]() + { + DeleteValue(CurrentIndex); + }); + + ThirdLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + ThirdLayout->addWidget(Remove); + ThirdLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + + Remove->setEnabled(false); + } + + QHBoxLayout* PreLayout = new QHBoxLayout(this); + { + Add = new QPushButton(tr("Insert Value"), this); + + AddHour = new QSpinBox(this); + AddHour->setMinimum(0); + AddHour->setMaximum(23); + + AddMin = new QSpinBox(this); + AddMin->setMinimum(0); + AddMin->setMaximum(59); + + connect(Add, &QPushButton::clicked, this, [=]() + { + int Time = ((AddHour->value() * 60) + AddMin->value()) * MAX_TIME_VALUE / (24 * 60); + CreateValue(Time); + }); + + PreLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + PreLayout->addWidget(Add); + PreLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + PreLayout->addWidget(new QLabel(tr("Time"), this)); + PreLayout->addWidget(AddHour); + PreLayout->addWidget(new QLabel(tr("Minutes"), this)); + PreLayout->addWidget(AddMin); + PreLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + } + + MainLayout->addSpacerItem(new QSpacerItem(0, 8, QSizePolicy::Expanding, QSizePolicy::Fixed)); + MainLayout->addLayout(PreLayout); + MainLayout->addSpacerItem(new QSpacerItem(0, 8, QSizePolicy::Expanding, QSizePolicy::Fixed)); + MainLayout->addWidget(Indicator); + MainLayout->addLayout(PreviewLayout); + MainLayout->addWidget(Arrow); + MainLayout->addSpacerItem(new QSpacerItem(0, 16, QSizePolicy::Expanding, QSizePolicy::Fixed)); + MainLayout->addWidget(Line); + MainLayout->addSpacerItem(new QSpacerItem(0, 16, QSizePolicy::Expanding, QSizePolicy::Fixed)); + MainLayout->addLayout(SecLayout); + MainLayout->addSpacerItem(new QSpacerItem(0, 8, QSizePolicy::Expanding, QSizePolicy::Fixed)); + MainLayout->addLayout(ThirdLayout); + MainLayout->addSpacerItem(new QSpacerItem(0, 16, QSizePolicy::Expanding, QSizePolicy::Fixed)); + + setFixedSize(sizeHint()); +} + +void LightViewEditor::closeEvent(QCloseEvent* event) +{ + emit Delete(this); + event->accept(); +} + +void LightViewEditor::UpdateSkyColorRowsValue() +{ + UpdateWidgetEdition(CurrentIndex); + _World->renderer()->skies()->update_sky_colors(_Map->getCamera()->position, static_cast(_World->time) % MAX_TIME_VALUE); + + Arrow->UpdateData(_Sky->colorRows[_eSkyColorIndex]); + Preview->SetPreview(_Sky->colorRows[_eSkyColorIndex]); +} + +bool LightViewEditor::ValidCurrentIndex() +{ + return (CurrentIndex >= 0 && CurrentIndex < _Sky->colorRows[_eSkyColorIndex].size()); +} + +void LightViewEditor::UpdateWorldTime() +{ + Indicator->UpdateWorldTime(static_cast(_World->time) % MAX_TIME_VALUE); +} + +void LightViewEditor::UpdateWidgetEdition(int Index) +{ + CurrentIndex = Index; + if (CurrentIndex < 0 || CurrentIndex >= _Sky->colorRows[_eSkyColorIndex].size()) + { + TimeSelectorHour->setEnabled(false); + TimeSelectorMin->setEnabled(false); + ColorPicker->setEnabled(false); + Remove->setEnabled(false); + + QSignalBlocker TimeHourBlocker(TimeSelectorHour); + QSignalBlocker TimeMinBlocker(TimeSelectorMin); + QSignalBlocker ColorBlocker(ColorPicker); + + TimeSelectorHour->setValue(0); + TimeSelectorMin->setValue(0); + ColorPicker->setColor(Qt::gray); + + TimeHourBlocker.unblock(); + TimeMinBlocker.unblock(); + ColorBlocker.unblock(); + return; + } + + TimeSelectorHour->setEnabled(true); + TimeSelectorMin->setEnabled(true); + ColorPicker->setEnabled(true); + Remove->setEnabled(true); + + QSignalBlocker TimeHourBlocker(TimeSelectorHour); + QSignalBlocker TimeMinBlocker(TimeSelectorMin); + QSignalBlocker ColorBlocker(ColorPicker); + + int Time = _Sky->colorRows[_eSkyColorIndex][CurrentIndex].time; + int ConvertedTime = Time * (24 * 60) / MAX_TIME_VALUE; + + int Hour = floor(ConvertedTime / 60); + int Min = ConvertedTime % 60; + + TimeSelectorHour->setValue(Hour); + TimeSelectorMin->setValue(Min); + + auto Color = _Sky->colorRows[_eSkyColorIndex][CurrentIndex].color * 255.f; + ColorPicker->setColor(QColor(Color.r, Color.g, Color.b)); + + TimeHourBlocker.unblock(); + TimeMinBlocker.unblock(); + ColorBlocker.unblock(); +} + +void LightViewEditor::CreateValue(int Time) +{ + if (_Sky->colorRows[_eSkyColorIndex].size() == 16) + return; + + SkyColor NewValue(Time, 0); + _Sky->colorRows[_eSkyColorIndex].push_back(NewValue); + LightViewWidget::SortSkyColorVector(_Sky->colorRows[_eSkyColorIndex]); + + for (int i = 0; i < _Sky->colorRows[_eSkyColorIndex].size(); ++i) + { + if (_Sky->colorRows[_eSkyColorIndex][i].time == Time) + { + auto Vec = _Sky->colorRows[_eSkyColorIndex]; + + if (Vec.size() == 1) + { + Vec[i].color = glm::vec3(.2f, .2f, .2f); + UpdateSkyColorRowsValue(); + return; + } + + if (Vec.size() == 2) + { + glm::vec3 Color = Vec[0].color * 255.f; + + if (i == 0) + Color = Vec[i + 1].color * 255.f; + + Vec[i].color = Color; + UpdateSkyColorRowsValue(); + return; + } + + QColor Color; + if (i == 0) + { + glm::vec3 Vec3A = Vec[Vec.size() - 1].color * 255.f; + glm::vec3 Vec3B = Vec[i + 1].color * 255.f; + + QColor ColorA(Vec3A.r, Vec3A.g, Vec3A.b); + QColor ColorB(Vec3B.r, Vec3B.g, Vec3B.b); + + int OffsetTime = abs(Vec[Vec.size() - 1].time - MAX_TIME_VALUE); + int TimeRef = Vec[i + 1].time + OffsetTime; + + float t = float(Time + OffsetTime) / float(TimeRef); + Color = LightViewWidget::InterpolateColor(ColorA, ColorB, t); + } + else if (i == _Sky->colorRows[_eSkyColorIndex].size() - 1) + { + glm::vec3 Vec3A = Vec[i - 1].color * 255.f; + glm::vec3 Vec3B = Vec[0].color * 255.f; + + QColor ColorA(Vec3A.r, Vec3A.g, Vec3A.b); + QColor ColorB(Vec3B.r, Vec3B.g, Vec3B.b); + + int OffsetTime = abs(Vec[i - 1].time - MAX_TIME_VALUE); + int TimeRef = Vec[0].time + OffsetTime; + + float t = float(Time - Vec[i - 1].time) / float(TimeRef); + Color = LightViewWidget::InterpolateColor(ColorA, ColorB, t); + } + else + { + glm::vec3 Vec3A = Vec[i - 1].color * 255.f; + glm::vec3 Vec3B = Vec[i + 1].color * 255.f; + + QColor ColorA(Vec3A.r, Vec3A.g, Vec3A.b); + QColor ColorB(Vec3B.r, Vec3B.g, Vec3B.b); + + int OffsetTime = Vec[i - 1].time; + int TimeRef = Vec[i + 1].time - OffsetTime; + + float t = float(Time - OffsetTime) / float(TimeRef); + Color = LightViewWidget::InterpolateColor(ColorA, ColorB, t); + } + + _Sky->colorRows[_eSkyColorIndex][i].color = glm::vec3(Color.redF(), Color.greenF(), Color.blueF()); + Arrow->CurrentSelected = i; + CurrentIndex = i; + UpdateSkyColorRowsValue(); + return; + } + } +} + +void LightViewEditor::DeleteValue(int Index) +{ + if (_Sky->colorRows[_eSkyColorIndex].size() == 1) + return; + + QSignalBlocker Blocker(Arrow); + if (Index >= 0 && Index < _Sky->colorRows[_eSkyColorIndex].size()) + { + _Sky->colorRows[_eSkyColorIndex].erase(_Sky->colorRows[_eSkyColorIndex].begin() + Index); + + Arrow->CurrentHover = -1; + Arrow->CurrentSelected = -1; + CurrentIndex = -1; + UpdateSkyColorRowsValue(); + Blocker.unblock(); + return; + } + Blocker.unblock(); +} + +LightViewDayIndicator::LightViewDayIndicator(QSize Size, QWidget* parent) + : QLabel(parent), PixmapSize(Size) +{ + ColorFromStyleSheet = LightViewWidget::GetColorFromStyleSheet(); +} + +void LightViewDayIndicator::UpdateWorldTime(int Time) +{ + Base = QPixmap(PixmapSize); + Base.fill(ColorFromStyleSheet); + QPainter Painter(&Base); + + const int PixelPointing = Time * (PixmapSize.width() - 30) / MAX_TIME_VALUE + 15; + + QPainterPath Triangle; + Triangle.moveTo(QPoint(PixelPointing, PixmapSize.height() - 5)); + Triangle.lineTo(QPoint(PixelPointing + 10, PixmapSize.height() - 15)); + Triangle.lineTo(QPoint(PixelPointing - 10, PixmapSize.height() - 15)); + Triangle.lineTo(QPoint(PixelPointing, PixmapSize.height() - 5)); + + Painter.setPen(Qt::NoPen); + Painter.fillPath(Triangle, QBrush(Qt::white)); + + setPixmap(Base); +} + +LightViewArrow::LightViewArrow(QSize Size, QWidget* parent) + : QLabel(parent), PixmapSize(Size) +{ + setMouseTracking(true); + ColorFromStyleSheet = LightViewWidget::GetColorFromStyleSheet(); +} + +void LightViewArrow::UpdateData(std::vector& data, bool UpdateDataColor) +{ + Data = data; + if (Data.size() == 0) + return; + + if (UpdateDataColor) + for (int i = 0; i < Data.size(); ++i) + Data[i].color *= 255.f; + + LightViewWidget::SortSkyColorVector(Data); + + Base = QPixmap(PixmapSize); + Base.fill(ColorFromStyleSheet); + QPainter Painter(&Base); + + for (int i = 0; i < Data.size(); ++i) + { + // this is where the value is exactly on the Pixmap + const int PixelPointing = Data[i].time * (PixmapSize.width() - 30) / MAX_TIME_VALUE + 15; + QColor ColorCadre = (i == CurrentSelected) ? Qt::black : Qt::white; + + // draw triangle pointing exactly where it's needed + { + QPainterPath Triangle; + Triangle.moveTo(QPoint(PixelPointing, 5)); + Triangle.lineTo(QPoint(PixelPointing + 10, 15)); + Triangle.lineTo(QPoint(PixelPointing - 10, 15)); + Triangle.lineTo(QPoint(PixelPointing, 5)); + + Painter.setPen(Qt::NoPen); + Painter.fillPath(Triangle, QBrush(ColorCadre)); + } + + // draw rectangle preview with correct color + { + QPainterPath Cadre; + Cadre.moveTo(QPoint(PixelPointing - 10, 15)); + Cadre.lineTo(QPoint(PixelPointing + 10, 15)); + Cadre.lineTo(QPoint(PixelPointing + 10, 35)); + Cadre.lineTo(QPoint(PixelPointing - 10, 35)); + Cadre.lineTo(QPoint(PixelPointing - 10, 15)); + + Painter.setPen(Qt::NoPen); + Painter.fillPath(Cadre, QBrush(ColorCadre)); + + QPainterPath Color; + Color.moveTo(QPoint(PixelPointing - 8, 17)); + Color.lineTo(QPoint(PixelPointing + 8, 17)); + Color.lineTo(QPoint(PixelPointing + 8, 33)); + Color.lineTo(QPoint(PixelPointing - 8, 33)); + Color.lineTo(QPoint(PixelPointing - 8, 17)); + + Painter.setPen(Qt::NoPen); + Painter.fillPath(Color, QColor(Data[i].color.r, Data[i].color.g, Data[i].color.b)); + } + } + + setPixmap(Base); +} + +void LightViewArrow::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { + if (CurrentHover < 0) + return; + + CurrentSelected = CurrentHover; + UpdateData(Data, false); + emit SelectValue(CurrentSelected); + + bDragging = true; + QApplication::setOverrideCursor(Qt::ClosedHandCursor); + } + + if (event->modifiers() & Qt::CTRL && event->button() == Qt::RightButton) + { + if (CurrentHover < 0) + return; + + emit DeleteValue(CurrentHover); + QApplication::restoreOverrideCursor(); + } +} + +void LightViewArrow::mouseReleaseEvent(QMouseEvent* event) +{ + if (!bDragging) + return; + + CurrentHover = -1; + bDragging = false; + QApplication::restoreOverrideCursor(); + emit ReleaseArrow(); +} + +void LightViewArrow::mouseMoveEvent(QMouseEvent* event) +{ + if (!bDragging) + { + QApplication::restoreOverrideCursor(); + CurrentHover = -1; + + if (event->pos().y() >= 10 && event->pos().y() <= 30) + { + for (int i = 0; i < Data.size(); ++i) + { + const int PixelPointing = Data[i].time * (PixmapSize.width() - 30) / MAX_TIME_VALUE + 20; + + if (event->pos().x() >= PixelPointing - 10 && event->pos().x() <= PixelPointing + 10) + { + QApplication::setOverrideCursor(Qt::PointingHandCursor); + CurrentHover = i; + return; + } + } + } + } + else + { + if (event->pos().x() < 15) + { + emit ChangeTime(CurrentHover, 0); + return; + } + + if (event->pos().x() > PixmapSize.width() - 15) + { + emit ChangeTime(CurrentHover, MAX_TIME_VALUE); + return; + } + + int Time = (event->pos().x() - 15) * MAX_TIME_VALUE / (PixmapSize.width() - 30); + emit ChangeTime(CurrentHover, Time); + } +} \ No newline at end of file diff --git a/src/noggit/ui/widgets/LightViewWidget.h b/src/noggit/ui/widgets/LightViewWidget.h new file mode 100644 index 00000000..fcafa100 --- /dev/null +++ b/src/noggit/ui/widgets/LightViewWidget.h @@ -0,0 +1,224 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DynamicMouseWidget.h" + +#define MAX_TIME_VALUE 2880 + +#define LIGHT_VIEW_PREVIEW_WIDTH 340 +#define LIGHT_VIEW_PREVIEW_HEIGHT 20 + +#define LIGHT_VIEW_PREVIEW_LINE_SIZE 1 +#define LIGHT_VIEW_PREVIEW_LINE_COLOR 0xFFFFFF +#define LIGHT_VIEW_PREVIEW_LINE_DAY_CYCLE_COLOR 0x000000 + +static std::map SkyColorNameByIndex = { + {0 , "Direct"}, + {1 , "Ambiant"}, + {2 , "Sky Top"}, + {3 , "Sky Midle"}, + {4 , "Sky Band 1"}, + {5 , "Sky Band 2"}, + {6 , "Sky Smog"}, + {7 , "Sky Fog"}, + {8 , "Sun"}, + {9 , "Cloud Sun"}, + {10 , "Cloud Emissive"}, + {11 , "Cloud Layer 1 Ambian"}, + {12 , "Cloud Layer 2 Ambiant"}, + {13 , "Unknown/Unused"}, + {14 , "Ocean Close"}, + {15 , "Ocean Far"}, + {16 , "River Close"}, + {17 , "River Far"} +}; + +/* +* PIXMAP +*/ +class LightViewPixmap : public QLabel +{ + Q_OBJECT + +public: + LightViewPixmap(QSize Size = QSize(LIGHT_VIEW_PREVIEW_WIDTH, LIGHT_VIEW_PREVIEW_HEIGHT), QWidget* parent = nullptr); + void ShowLines(bool show); + void EnableEvent() { bEvent = true; }; + void DisableEvent() { bEvent = false; }; + void SetPreview(std::vector& data, int Index = -1); + +protected: + void mousePressEvent(QMouseEvent* event) override; + +signals: + void UpdatePixmap(const QPixmap Updated); + void CreateValue(int Time); + +private: + bool bShowLines = false; + bool bEvent = false; + std::vector Data; + + bool DrawLines(); + + QImage Image; + QPixmap Base; + QSize PixmapSize; +}; + +/* +* ARROW +*/ +class LightViewArrow : public QLabel +{ + Q_OBJECT + +public: + LightViewArrow(QSize Size = QSize(LIGHT_VIEW_PREVIEW_WIDTH, LIGHT_VIEW_PREVIEW_HEIGHT), QWidget* parent = nullptr); + void UpdateData(std::vector& data, bool UpdateDataColor = true); + int CurrentHover = -1; + int CurrentSelected = -1; + +protected: + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + +signals: + void DeleteValue(int Index); + void SelectValue(int Index); + void ChangeTime(int Index, int Time); + void ReleaseArrow(); + +private: + std::vector Data; + + QPixmap Base; + QSize PixmapSize; + QColor ColorFromStyleSheet; + + bool bDragging = false; + bool bUpdateMouse = false; +}; + +/* +* DAY INDICATOR +*/ +class LightViewDayIndicator : public QLabel +{ +public: + LightViewDayIndicator(QSize Size = QSize(LIGHT_VIEW_PREVIEW_WIDTH, LIGHT_VIEW_PREVIEW_HEIGHT), QWidget* parent = nullptr); + void UpdateWorldTime(int Iime); + +private: + QPixmap Base; + QSize PixmapSize; + QColor ColorFromStyleSheet; +}; + +/* +* EDITOR +*/ +class LightViewEditor : public QWidget +{ + Q_OBJECT + +public: + LightViewEditor(MapView* Map, SkyParam* Sky, SkyColorNames eSkyColor, QWidget* parent = nullptr); + void UpdateWorldTime(); + +protected: + void closeEvent(QCloseEvent* event) override; + +private slots: + void UpdateWidgetEdition(int Index); + void DeleteValue(int Index); + void CreateValue(int Time = 0); + +private: + void UpdateSkyColorRowsValue(); + bool ValidCurrentIndex(); + +signals: + void Delete(LightViewEditor* self); + void UpdatePixmap(const QPixmap Updated); + +private: + World* _World; + MapView* _Map; + SkyParam* _Sky; + SkyColorNames _eSkyColorIndex; + std::string _ColorName; + + QVBoxLayout* MainLayout; + + int CurrentIndex = -1; + + LightViewDayIndicator* Indicator; + LightViewPixmap* Preview; + LightViewArrow* Arrow; + + QPushButton* Add; + QSpinBox* AddHour; + QSpinBox* AddMin; + + QPushButton* Remove; + + color_widgets::ColorSelector* ColorPicker; + QSpinBox* TimeSelectorHour; + QSpinBox* TimeSelectorMin; +}; + +/* +* PREVIEW +*/ +class LightViewPreview : public DynamicMouseWidget +{ + Q_OBJECT + +public: + LightViewPreview(QSize Size = QSize(LIGHT_VIEW_PREVIEW_WIDTH, LIGHT_VIEW_PREVIEW_HEIGHT), QWidget* parent = nullptr); + LightViewPreview(QString LightName, QSize Size = QSize(LIGHT_VIEW_PREVIEW_WIDTH, LIGHT_VIEW_PREVIEW_HEIGHT), QWidget* parent = nullptr); + void ShowLines(bool show); + void SetPreview(std::vector& data); + void UpdatePixmap(const QPixmap Updated); + +protected: + void mousePressEvent(QMouseEvent* event) override; + +signals: + void LeftClicked(); + +private: + QVBoxLayout* MainLayout; + + QLabel* Name; + LightViewPixmap* Preview; +}; + +/* +* WIDGET BASE +*/ +class LightViewWidget : public QWidget +{ + Q_OBJECT + +public: + LightViewWidget(QWidget* parent = nullptr); + + static QColor InterpolateColor(QColor& a, QColor& b, float t); + static QImage FillImagePart(QImage Image, int X, QColor Color); + static inline int ClampColor(int Value); + static QColor GetColorFromStyleSheet(); + static void SortSkyColorVector(std::vector& Vector); +}; +