diff --git a/common/lc_doublespinbox.cpp b/common/lc_doublespinbox.cpp new file mode 100644 index 00000000..6f18522f --- /dev/null +++ b/common/lc_doublespinbox.cpp @@ -0,0 +1,211 @@ +#include "lc_global.h" +#include "lc_doublespinbox.h" +#include "lc_qutils.h" + +lcDoubleSpinBox::lcDoubleSpinBox(QWidget* Parent) + : QDoubleSpinBox(Parent) +{ + lineEdit()->installEventFilter(this); + + connect(lineEdit(), &QLineEdit::returnPressed, this, &lcDoubleSpinBox::ReturnPressed); +} + +void lcDoubleSpinBox::SetValue(double Value) +{ + if (!hasFocus()) + mInitialValue = Value; + + setValue(Value); +} + +QString lcDoubleSpinBox::textFromValue(double Value) const +{ + return lcFormatValueLocalized(Value); +} + +void lcDoubleSpinBox::CancelEditing() +{ + emit EditingCanceled(); + + setValue(mInitialValue); +} + +void lcDoubleSpinBox::FinishEditing() +{ + if (value() == mInitialValue) + return; + + emit EditingFinished(); + + mInitialValue = value(); +} + +void lcDoubleSpinBox::ReturnPressed() +{ + FinishEditing(); + clearFocus(); +} + +void lcDoubleSpinBox::HandleMousePressEvent(QMouseEvent* MouseEvent) +{ + if (MouseEvent->buttons() == Qt::LeftButton) + { + mDragMode = DragMode::Start; + mLastPosition = GetGlobalMousePosition(MouseEvent); + } + else + { + if (mDragMode == DragMode::Value) + CancelEditing(); + + mDragMode = DragMode::None; + } +} + +bool lcDoubleSpinBox::HandleMouseMoveEvent(QMouseEvent* MouseEvent, QObject* Object) +{ + if (mDragMode != DragMode::None) + { + if (mDragMode == DragMode::Start) + { + if (lineEdit()->selectionLength() > 0 && Object != this) + { + mDragMode = DragMode::None; + } + else + { + QPoint Position = GetGlobalMousePosition(MouseEvent); + + if (abs(mLastPosition.y() - Position.y()) > 3) + { + mDragMode = DragMode::Value; + mLastPosition = QCursor::pos(); + } + } + } + + if (mDragMode == DragMode::Value) + { + QPoint Position = QCursor::pos(); + + stepBy(mLastPosition.y() - Position.y()); + + QScreen* Screen = QGuiApplication::screenAt(mapToGlobal(QPoint(width() / 2, height() / 2))); + + if (Screen) + { + int ScreenHeight = Screen->geometry().height(); + + if (Position.y() <= 0) + { + Position.setY(ScreenHeight - 2); + + QCursor::setPos(Position); + } + else if (Position.y() >= ScreenHeight - 1) + { + Position.setY(1); + + QCursor::setPos(Position); + } + } + + mLastPosition = Position; + + MouseEvent->accept(); + + return true; + } + } + + return false; +} + +void lcDoubleSpinBox::HandleMouseReleaseEvent(QMouseEvent* MouseEvent) +{ + Q_UNUSED(MouseEvent); + + if (mDragMode == DragMode::Value) + { + FinishEditing(); + + clearFocus(); + } + + mDragMode = DragMode::None; +} + +bool lcDoubleSpinBox::eventFilter(QObject* Object, QEvent* Event) +{ + switch (Event->type()) + { + case QEvent::MouseButtonPress: + if (Object == lineEdit()) + HandleMousePressEvent(reinterpret_cast(Event)); + break; + + case QEvent::MouseMove: + if (Object == lineEdit()) + HandleMouseMoveEvent(reinterpret_cast(Event), Object); + break; + + case QEvent::MouseButtonRelease: + if (Object == lineEdit()) + HandleMouseReleaseEvent(reinterpret_cast(Event)); + break; + + default: + break; + } + + return QDoubleSpinBox::eventFilter(Object, Event); +} + +void lcDoubleSpinBox::mousePressEvent(QMouseEvent* MouseEvent) +{ + HandleMousePressEvent(MouseEvent); + + QDoubleSpinBox::mousePressEvent(MouseEvent); +} + +void lcDoubleSpinBox::mouseMoveEvent(QMouseEvent* MouseEvent) +{ + if (!HandleMouseMoveEvent(MouseEvent, this)) + QDoubleSpinBox::mouseMoveEvent(MouseEvent); +} + +void lcDoubleSpinBox::mouseReleaseEvent(QMouseEvent* MouseEvent) +{ + HandleMouseReleaseEvent(MouseEvent); + + QDoubleSpinBox::mouseReleaseEvent(MouseEvent); +} + +bool lcDoubleSpinBox::event(QEvent* Event) +{ + if (Event->type() == QEvent::ShortcutOverride) + { + QKeyEvent* KeyEvent = static_cast(Event); + + if (KeyEvent->key() == Qt::Key_Escape) + { + if (mDragMode == DragMode::Value) + CancelEditing(); + + mDragMode = DragMode::None; + + clearFocus(); + KeyEvent->accept(); + } + } + + return QDoubleSpinBox::event(Event); +} + +void lcDoubleSpinBox::focusOutEvent(QFocusEvent* FocusEvent) +{ + if (value() != mInitialValue) + CancelEditing(); + + QDoubleSpinBox::focusOutEvent(FocusEvent); +} diff --git a/common/lc_doublespinbox.h b/common/lc_doublespinbox.h new file mode 100644 index 00000000..d1dc16b0 --- /dev/null +++ b/common/lc_doublespinbox.h @@ -0,0 +1,56 @@ +#pragma once + +class lcDoubleSpinBox : public QDoubleSpinBox +{ + Q_OBJECT + +public: + lcDoubleSpinBox(QWidget* Parent); + virtual ~lcDoubleSpinBox() = default; + + void SetValue(double Value); + + QString textFromValue(double Value) const override; + bool eventFilter(QObject* Object, QEvent* Event) override; + + void mousePressEvent(QMouseEvent* MouseEvent) override; + void mouseMoveEvent(QMouseEvent* MouseEvent) override; + void mouseReleaseEvent(QMouseEvent* MouseEvent) override; + void focusOutEvent(QFocusEvent* FocusEvent) override; + +signals: + void EditingCanceled(); + void EditingFinished(); + +protected slots: + void CancelEditing(); + void FinishEditing(); + void ReturnPressed(); + +protected: + bool event(QEvent* Event) override; + + static QPoint GetGlobalMousePosition(QMouseEvent* MouseEvent) + { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + return MouseEvent->globalPos(); +#else + return MouseEvent->globalPosition().toPoint(); +#endif + } + + void HandleMousePressEvent(QMouseEvent* MouseEvent); + bool HandleMouseMoveEvent(QMouseEvent* MouseEvent, QObject* Object); + void HandleMouseReleaseEvent(QMouseEvent* MouseEvent); + + enum class DragMode + { + None, + Start, + Value + }; + + double mInitialValue = 0.0; + QPoint mLastPosition; + DragMode mDragMode = DragMode::None; +}; diff --git a/common/lc_model.cpp b/common/lc_model.cpp index 5e5209e1..d19648c7 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -3376,7 +3376,7 @@ void lcModel::SetCameraOrthographic(lcCamera* Camera, bool Ortho) gMainWindow->UpdateSelectedObjects(false); } -void lcModel::SetCameraFOV(lcCamera* Camera, float FOV) +void lcModel::SetCameraFOV(lcCamera* Camera, float FOV, bool Checkpoint) { if (Camera->m_fovy == FOV) return; @@ -3384,11 +3384,13 @@ void lcModel::SetCameraFOV(lcCamera* Camera, float FOV) Camera->m_fovy = FOV; Camera->UpdatePosition(mCurrentStep); - SaveCheckpoint(tr("Changing FOV")); + if (Checkpoint) + SaveCheckpoint(tr("Changing FOV")); + UpdateAllViews(); } -void lcModel::SetCameraZNear(lcCamera* Camera, float ZNear) +void lcModel::SetCameraZNear(lcCamera* Camera, float ZNear, bool Checkpoint) { if (Camera->m_zNear == ZNear) return; @@ -3396,11 +3398,13 @@ void lcModel::SetCameraZNear(lcCamera* Camera, float ZNear) Camera->m_zNear = ZNear; Camera->UpdatePosition(mCurrentStep); - SaveCheckpoint(tr("Editing Camera")); + if (Checkpoint) + SaveCheckpoint(tr("Editing Camera")); + UpdateAllViews(); } -void lcModel::SetCameraZFar(lcCamera* Camera, float ZFar) +void lcModel::SetCameraZFar(lcCamera* Camera, float ZFar, bool Checkpoint) { if (Camera->m_zFar == ZFar) return; @@ -3408,7 +3412,9 @@ void lcModel::SetCameraZFar(lcCamera* Camera, float ZFar) Camera->m_zFar = ZFar; Camera->UpdatePosition(mCurrentStep); - SaveCheckpoint(tr("Editing Camera")); + if (Checkpoint) + SaveCheckpoint(tr("Editing Camera")); + UpdateAllViews(); } @@ -3447,6 +3453,84 @@ void lcModel::SetObjectsProperty(const std::vector& Objects, lcObject } } +void lcModel::EndPropertyEdit(lcObjectPropertyId PropertyId, bool Accept) +{ + if (!Accept) + { + if (!mUndoHistory.empty()) + LoadCheckPoint(mUndoHistory.front()); + return; + } + + switch (PropertyId) + { + case lcObjectPropertyId::PieceId: + case lcObjectPropertyId::PieceColor: + case lcObjectPropertyId::PieceStepShow: + case lcObjectPropertyId::PieceStepHide: + case lcObjectPropertyId::CameraName: + case lcObjectPropertyId::CameraType: + break; + + case lcObjectPropertyId::CameraFOV: + SaveCheckpoint(tr("Changing FOV")); + break; + + case lcObjectPropertyId::CameraNear: + case lcObjectPropertyId::CameraFar: + SaveCheckpoint(tr("Editing Camera")); + break; + + case lcObjectPropertyId::CameraPositionX: + case lcObjectPropertyId::CameraPositionY: + case lcObjectPropertyId::CameraPositionZ: + case lcObjectPropertyId::CameraTargetX: + case lcObjectPropertyId::CameraTargetY: + case lcObjectPropertyId::CameraTargetZ: + case lcObjectPropertyId::CameraUpX: + case lcObjectPropertyId::CameraUpY: + case lcObjectPropertyId::CameraUpZ: + SaveCheckpoint(tr("Move")); + break; + + case lcObjectPropertyId::LightName: + case lcObjectPropertyId::LightType: + case lcObjectPropertyId::LightColor: + case lcObjectPropertyId::LightBlenderPower: + case lcObjectPropertyId::LightPOVRayPower: + case lcObjectPropertyId::LightCastShadow: + case lcObjectPropertyId::LightPOVRayFadeDistance: + case lcObjectPropertyId::LightPOVRayFadePower: + case lcObjectPropertyId::LightPointBlenderRadius: + case lcObjectPropertyId::LightSpotBlenderRadius: + case lcObjectPropertyId::LightDirectionalBlenderAngle: + case lcObjectPropertyId::LightAreaSizeX: + case lcObjectPropertyId::LightAreaSizeY: + case lcObjectPropertyId::LightSpotConeAngle: + case lcObjectPropertyId::LightSpotPenumbraAngle: + case lcObjectPropertyId::LightSpotPOVRayTightness: + case lcObjectPropertyId::LightAreaShape: + case lcObjectPropertyId::LightAreaPOVRayGridX: + case lcObjectPropertyId::LightAreaPOVRayGridY: + break; + + case lcObjectPropertyId::ObjectPositionX: + case lcObjectPropertyId::ObjectPositionY: + case lcObjectPropertyId::ObjectPositionZ: + SaveCheckpoint(tr("Move")); + break; + + case lcObjectPropertyId::ObjectRotationX: + case lcObjectPropertyId::ObjectRotationY: + case lcObjectPropertyId::ObjectRotationZ: + SaveCheckpoint(tr("Rotate")); + break; + + case lcObjectPropertyId::Count: + break; + } +} + bool lcModel::AnyPiecesSelected() const { for (const std::unique_ptr& Piece : mPieces) diff --git a/common/lc_model.h b/common/lc_model.h index 02a395f6..c48aae34 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -377,11 +377,12 @@ public: void SetSelectedPiecesStepHide(lcStep Step); void SetObjectsProperty(const std::vector& Objects, lcObjectPropertyId PropertyId, QVariant Value); + void EndPropertyEdit(lcObjectPropertyId PropertyId, bool Accept); void SetCameraOrthographic(lcCamera* Camera, bool Ortho); - void SetCameraFOV(lcCamera* Camera, float FOV); - void SetCameraZNear(lcCamera* Camera, float ZNear); - void SetCameraZFar(lcCamera* Camera, float ZFar); + void SetCameraFOV(lcCamera* Camera, float FOV, bool Checkpoint); + void SetCameraZNear(lcCamera* Camera, float ZNear, bool Checkpoint); + void SetCameraZFar(lcCamera* Camera, float ZFar, bool Checkpoint); void ShowPropertiesDialog(); void ShowSelectByNameDialog(); diff --git a/common/lc_propertieswidget.cpp b/common/lc_propertieswidget.cpp index 023f226c..d6e8d918 100644 --- a/common/lc_propertieswidget.cpp +++ b/common/lc_propertieswidget.cpp @@ -1,6 +1,7 @@ #include "lc_global.h" #include "lc_propertieswidget.h" #include "lc_keyframewidget.h" +#include "lc_doublespinbox.h" #include "object.h" #include "piece.h" #include "camera.h" @@ -246,9 +247,9 @@ void lcPropertiesWidget::AddBoolProperty(lcObjectPropertyId PropertyId, const QS mLayoutRow++; } -void lcPropertiesWidget::FloatChanged() +void lcPropertiesWidget::FloatEditingFinished() { - QLineEdit* Widget = qobject_cast(sender()); + lcDoubleSpinBox* Widget = qobject_cast(sender()); lcObjectPropertyId PropertyId = GetEditorWidgetPropertyId(Widget); if (PropertyId == lcObjectPropertyId::Count) @@ -256,15 +257,50 @@ void lcPropertiesWidget::FloatChanged() lcModel* Model = gMainWindow->GetActiveModel(); + if (!Model) + return; + + Model->EndPropertyEdit(PropertyId, true); +} + +void lcPropertiesWidget::FloatEditingCanceled() +{ + lcDoubleSpinBox* Widget = qobject_cast(sender()); + lcObjectPropertyId PropertyId = GetEditorWidgetPropertyId(Widget); + + if (PropertyId == lcObjectPropertyId::Count) + return; + + lcModel* Model = gMainWindow->GetActiveModel(); + + if (!Model) + return; + + Model->EndPropertyEdit(PropertyId, false); +} + +void lcPropertiesWidget::FloatChanged(const QString& TextValue) +{ + lcDoubleSpinBox* Widget = qobject_cast(sender()); + lcObjectPropertyId PropertyId = GetEditorWidgetPropertyId(Widget); + float Value = lcParseValueLocalized(TextValue); + + ChangeFloatValue(PropertyId, Value, true); +} + +void lcPropertiesWidget::ChangeFloatValue(lcObjectPropertyId PropertyId, float Value, bool Dragging) +{ + if (PropertyId == lcObjectPropertyId::Count) + return; + + lcModel* Model = gMainWindow->GetActiveModel(); + if (!Model) return; lcPiece* Piece = dynamic_cast(mFocusObject); lcCamera* Camera = dynamic_cast(mFocusObject); lcLight* Light = dynamic_cast(mFocusObject); - float Value = lcParseValueLocalized(Widget->text()); - - // todo: mouse drag if (PropertyId == lcObjectPropertyId::ObjectPositionX || PropertyId == lcObjectPropertyId::ObjectPositionY || PropertyId == lcObjectPropertyId::ObjectPositionZ) { @@ -282,7 +318,7 @@ void lcPropertiesWidget::FloatChanged() lcVector3 Distance = Position - Center; - Model->MoveSelectedObjects(Distance, false, false, true, true, true); + Model->MoveSelectedObjects(Distance, false, false, true, !Dragging, true); } else if (PropertyId == lcObjectPropertyId::ObjectRotationX || PropertyId == lcObjectPropertyId::ObjectRotationY || PropertyId == lcObjectPropertyId::ObjectRotationZ) { @@ -302,7 +338,7 @@ void lcPropertiesWidget::FloatChanged() else if (PropertyId == lcObjectPropertyId::ObjectRotationZ) Rotation[2] = Value; - Model->RotateSelectedObjects(Rotation - InitialRotation, true, false, true, true); + Model->RotateSelectedObjects(Rotation - InitialRotation, true, false, true, !Dragging); } else if (Piece || Light) { @@ -338,7 +374,7 @@ void lcPropertiesWidget::FloatChanged() lcVector3 Distance = End - Start; - Model->MoveSelectedObjects(Distance, false, false, true, true, true); + Model->MoveSelectedObjects(Distance, false, false, true, !Dragging, true); if (Camera) { @@ -375,7 +411,7 @@ void lcPropertiesWidget::FloatChanged() lcVector3 Distance = End - Start; - Model->MoveSelectedObjects(Distance, false, false, true, true, true); + Model->MoveSelectedObjects(Distance, false, false, true, !Dragging, true); if (Camera) { @@ -412,7 +448,7 @@ void lcPropertiesWidget::FloatChanged() lcVector3 Distance = End - Start; - Model->MoveSelectedObjects(Distance, false, false, true, true, true); + Model->MoveSelectedObjects(Distance, false, false, true, !Dragging, true); if (Camera) { @@ -424,28 +460,28 @@ void lcPropertiesWidget::FloatChanged() { if (PropertyId == lcObjectPropertyId::CameraFOV) { - Model->SetCameraFOV(Camera, Value); + Model->SetCameraFOV(Camera, Value, !Dragging); } else if (PropertyId == lcObjectPropertyId::CameraNear) { - Model->SetCameraZNear(Camera, Value); + Model->SetCameraZNear(Camera, Value, !Dragging); } else if (PropertyId == lcObjectPropertyId::CameraFar) { - Model->SetCameraZFar(Camera, Value); + Model->SetCameraZFar(Camera, Value, !Dragging); } } } void lcPropertiesWidget::UpdateFloat(lcObjectPropertyId PropertyId, float Value) { - QLineEdit* Widget = qobject_cast(mPropertyWidgets[static_cast(PropertyId)].Editor); + lcDoubleSpinBox* Widget = qobject_cast(mPropertyWidgets[static_cast(PropertyId)].Editor); if (Widget) { QSignalBlocker Blocker(Widget); - Widget->setText(lcFormatValueLocalized(Value)); + Widget->SetValue(Value); } UpdateKeyFrameWidget(PropertyId); @@ -455,12 +491,14 @@ void lcPropertiesWidget::AddFloatProperty(lcObjectPropertyId PropertyId, const Q { AddLabel(PropertyId, Text, ToolTip); - QLineEdit* Widget = new QLineEdit(this); + lcDoubleSpinBox* Widget = new lcDoubleSpinBox(this); Widget->setToolTip(ToolTip); - Widget->setValidator(new QDoubleValidator(Min, Max, 1, Widget)); + Widget->setRange(Min, Max); - connect(Widget, &QLineEdit::editingFinished, this, &lcPropertiesWidget::FloatChanged); + connect(Widget, &lcDoubleSpinBox::EditingCanceled, this, &lcPropertiesWidget::FloatEditingCanceled); + connect(Widget, &lcDoubleSpinBox::EditingFinished, this, &lcPropertiesWidget::FloatEditingFinished); + connect(Widget, &lcDoubleSpinBox::textChanged, this, &lcPropertiesWidget::FloatChanged); mLayout->addWidget(Widget, mLayoutRow, 2); diff --git a/common/lc_propertieswidget.h b/common/lc_propertieswidget.h index 545ce990..87fba902 100644 --- a/common/lc_propertieswidget.h +++ b/common/lc_propertieswidget.h @@ -18,7 +18,9 @@ protected slots: void CategoryStateChanged(bool Expanded); void KeyFrameChanged(); void BoolChanged(); - void FloatChanged(); + void FloatEditingFinished(); + void FloatEditingCanceled(); + void FloatChanged(const QString& TextValue); void IntegerChanged(); void StepNumberChanged(); void StringChanged(); @@ -113,6 +115,8 @@ protected: void UpdatePieceColor(lcObjectPropertyId PropertyId); void UpdatePieceId(lcObjectPropertyId PropertyId); + void ChangeFloatValue(lcObjectPropertyId PropertyId, float Value, bool Dragging); + void SetEmpty(); void SetPiece(const std::vector& Selection, lcObject* Focus); void SetCamera(const std::vector& Selection, lcObject* Focus); diff --git a/leocad.pro b/leocad.pro index 8ebbc63f..90f26b6e 100644 --- a/leocad.pro +++ b/leocad.pro @@ -188,6 +188,7 @@ SOURCES += \ common/lc_colors.cpp \ common/lc_commands.cpp \ common/lc_context.cpp \ + common/lc_doublespinbox.cpp \ common/lc_edgecolordialog.cpp \ common/lc_file.cpp \ common/lc_findreplacewidget.cpp \ @@ -261,6 +262,7 @@ HEADERS += \ common/lc_colors.h \ common/lc_commands.h \ common/lc_context.h \ + common/lc_doublespinbox.h \ common/lc_edgecolordialog.h \ common/lc_file.h \ common/lc_findreplacewidget.h \